WxPython To Create UI
Reason
I am tired of ghost windows, and sometimes I cannot remember how to give input to my former programs. Hence, UI is necessary.
Consider that QT is more complex and more applied in C++, I choose wxPython to create windows of python files.
The most important part in wxPython is the so-called “sizer”, used to control the relative size of parts by proportion, which is difficult to realize in code, thus I use wxFormBuilder to draw them.
Example
I try to design an app to recorded and calculate my scores by python.
The UI is drawn in wxFormBuilder, and the first .py file is generated by it although I change some details in it. Then, in the main part, I have to realize the window and its functions.
Source code
# -*- coding: utf-8 -*-
###########################################################################
## Python code generated with wxFormBuilder (version Oct 26 2018)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################
import wx
import wx.xrc
###########################################################################
## Class MyFrame1
###########################################################################
class MyFrame1 ( wx.Frame ):
def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"GPATool", pos = wx.DefaultPosition, size = wx.Size( 945,643 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
self.SetBackgroundColour( wx.Colour( 235, 236, 250 ) )
container = wx.BoxSizer( wx.HORIZONTAL )
left = wx.BoxSizer( wx.VERTICAL )
self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, u"Condition Of Lessons", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText2.Wrap( -1 )
self.m_staticText2.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
left.Add( self.m_staticText2, 10, wx.ALIGN_CENTER|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
self.m_staticline1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
left.Add( self.m_staticline1, 0, wx.EXPAND |wx.ALL, 5 )
bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
bSizer5 = wx.BoxSizer( wx.VERTICAL )
self.m_staticText3 = wx.StaticText( self, wx.ID_ANY, u"Lesson Name : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText3.Wrap( -1 )
self.m_staticText3.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer5.Add( self.m_staticText3, 1, wx.ALIGN_CENTER|wx.ALL, 5 )
self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer5.Add( self.m_staticline4, 0, wx.EXPAND |wx.ALL, 5 )
self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, u"Lesson Credit : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText4.Wrap( -1 )
self.m_staticText4.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer5.Add( self.m_staticText4, 1, wx.ALIGN_CENTER|wx.ALL, 5 )
self.m_staticline5 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer5.Add( self.m_staticline5, 0, wx.EXPAND |wx.ALL, 5 )
self.m_staticText5 = wx.StaticText( self, wx.ID_ANY, u"Core Lesson : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText5.Wrap( -1 )
self.m_staticText5.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer5.Add( self.m_staticText5, 1, wx.ALIGN_CENTER|wx.ALL, 5 )
self.m_staticline6 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer5.Add( self.m_staticline6, 0, wx.EXPAND |wx.ALL, 5 )
self.m_staticText6 = wx.StaticText( self, wx.ID_ANY, u"Final Score : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText6.Wrap( -1 )
self.m_staticText6.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer5.Add( self.m_staticText6, 1, wx.ALIGN_CENTER|wx.ALL, 5 )
bSizer4.Add( bSizer5, 1, wx.EXPAND, 5 )
bSizer6 = wx.BoxSizer( wx.VERTICAL )
self.textctrl_1 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.textctrl_1.SetFont( wx.Font( 15, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "楷体" ) )
bSizer6.Add( self.textctrl_1, 1, wx.ALL|wx.EXPAND, 5 )
self.m_staticline8 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer6.Add( self.m_staticline8, 0, wx.EXPAND |wx.ALL, 5 )
self.textctrl_2 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.textctrl_2.SetFont( wx.Font( 26, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer6.Add( self.textctrl_2, 1, wx.ALL|wx.EXPAND, 5 )
self.m_staticline9 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer6.Add( self.m_staticline9, 0, wx.EXPAND |wx.ALL, 5 )
self.checkbox_core = wx.CheckBox( self, wx.ID_ANY, u"Is Core", wx.DefaultPosition, wx.DefaultSize, 0 )
self.checkbox_core.SetValue(True)
self.checkbox_core.SetFont( wx.Font( 16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer6.Add( self.checkbox_core, 1, wx.ALIGN_CENTER|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 )
self.m_staticline10 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
bSizer6.Add( self.m_staticline10, 0, wx.EXPAND |wx.ALL, 5 )
self.textctrl_3 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.textctrl_3.SetFont( wx.Font( 22, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
bSizer6.Add( self.textctrl_3, 1, wx.ALL|wx.EXPAND, 5 )
bSizer4.Add( bSizer6, 1, wx.EXPAND, 5 )
left.Add( bSizer4, 80, wx.EXPAND, 5 )
self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
left.Add( self.m_staticline2, 0, wx.EXPAND |wx.ALL, 5 )
self.button_send = wx.Button( self, wx.ID_ANY, u"Add Lesson", wx.DefaultPosition, wx.DefaultSize, 0 )
self.button_send.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
left.Add( self.button_send, 10, wx.ALIGN_CENTER|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
container.Add( left, 36, wx.EXPAND, 5 )
right = wx.BoxSizer( wx.VERTICAL )
self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"Semester Name : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText1.Wrap( -1 )
self.m_staticText1.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
right.Add( self.m_staticText1, 5, wx.ALL, 5 )
self.textctrl_semester = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
self.textctrl_semester.SetFont( wx.Font( 22, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
self.textctrl_semester.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNSHADOW ) )
right.Add( self.textctrl_semester, 10, wx.ALL|wx.EXPAND, 5 )
self.m_staticline91 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
right.Add( self.m_staticline91, 0, wx.EXPAND |wx.ALL, 5 )
self.m_staticText7 = wx.StaticText( self, wx.ID_ANY, u"Lesson Information : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText7.Wrap( -1 )
self.m_staticText7.SetFont( wx.Font( 11, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
right.Add( self.m_staticText7, 8, wx.ALL, 5 )
self.textctrl_info = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
self.textctrl_info.SetFont( wx.Font( 11, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, "宋体" ) )
self.textctrl_info.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INFOBK ) )
right.Add( self.textctrl_info, 25, wx.ALL|wx.EXPAND, 5 )
self.m_staticline101 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
right.Add( self.m_staticline101, 0, wx.EXPAND |wx.ALL, 5 )
self.m_staticText8 = wx.StaticText( self, wx.ID_ANY, u"Score Condition : ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText8.Wrap( -1 )
self.m_staticText8.SetFont( wx.Font( 12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
right.Add( self.m_staticText8, 8, wx.ALL, 5 )
self.textctrl_score = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
self.textctrl_score.SetFont( wx.Font( 14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, "宋体" ) )
self.textctrl_score.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INFOBK ) )
right.Add( self.textctrl_score, 25, wx.ALL|wx.SHAPED, 5 )
container.Add( right, 64, wx.EXPAND, 5 )
self.SetSizer(container)
self.Layout()
self.m_menubar1 = wx.MenuBar(0)
self.m_menu1 = wx.Menu()
self.m_menuItem1 = wx.MenuItem(self.m_menu1, wx.ID_ANY, u"ClearAll",
"Clear All The Indormation In This Window!!!", wx.ITEM_NORMAL)
self.m_menu1.Append(self.m_menuItem1)
self.m_menuItem2 = wx.MenuItem(self.m_menu1, wx.ID_ANY, u"SaveScore",
"Only Save The information Of The Score Condition.", wx.ITEM_NORMAL)
self.m_menu1.Append(self.m_menuItem2)
self.m_menuItem3 = wx.MenuItem(self.m_menu1, wx.ID_ANY, u"SaveInfo",
"Only Save The Information Of All Lessons.", wx.ITEM_NORMAL)
self.m_menu1.Append(self.m_menuItem3)
self.m_menuItem4 = wx.MenuItem(self.m_menu1, wx.ID_ANY, u"SaveAll",
"Save All The Information Of This Semester.", wx.ITEM_NORMAL)
self.m_menu1.Append(self.m_menuItem4)
self.m_menubar1.Append(self.m_menu1, u"Options")
self.SetMenuBar(self.m_menubar1)
self.m_statusBar1 = self.CreateStatusBar(1, wx.STB_SIZEGRIP, wx.ID_ANY)
self.Centre(wx.BOTH)
# Connect Events
self.textctrl_1.Bind(wx.EVT_TEXT_ENTER, self.jump2)
self.textctrl_2.Bind(wx.EVT_TEXT_ENTER, self.jump3)
self.textctrl_3.Bind(wx.EVT_TEXT_ENTER, self.jumpb)
self.Bind(wx.EVT_CLOSE, self.on_close) # 自己加的
self.button_send.Bind(wx.EVT_BUTTON, self.send)
self.textctrl_info.Bind(wx.EVT_TEXT_ENTER, self.score_update_)
self.textctrl_semester.Bind(wx.EVT_TEXT_ENTER, self.score_update)
self.Bind(wx.EVT_MENU, self.clearall, id=self.m_menuItem1.GetId())
self.Bind(wx.EVT_MENU, self.savescore, id=self.m_menuItem2.GetId())
self.Bind(wx.EVT_MENU, self.saveinfo, id=self.m_menuItem3.GetId())
self.Bind(wx.EVT_MENU, self.saveall, id=self.m_menuItem4.GetId())
def __del__(self):
pass
# Virtual event handlers, overide them in your derived class
def jump2(self, event):
event.Skip()
def jump3(self, event):
event.Skip()
def on_close(self, event):
event.Skip()
def jumpb(self, event):
event.Skip()
def send(self, event):
event.Skip()
def score_update(self, event):
event.Skip()
def clearall(self, event):
event.Skip()
def savescore(self, event):
event.Skip()
def saveinfo(self, event):
event.Skip()
def saveall(self, event):
event.Skip()
def score_update_(self,event):
event.Skip()
This is the window.
Next is the main part:
import gpaTool
import wx
import os
#处理分数的函数
def trans_gpa(score):
if score<60:
return 0
elif score<62:
return 1
elif score<65:
return 1.7
elif score<67:
return 2
elif score<70:
return 2.3
elif score<75:
return 2.7
elif score<80:
return 3
elif score<85:
return 3.3
elif score<90:
return 3.7
elif score<95:
return 4
else:
return 4.3
def ave_score(scores, credits):
m, n = len(scores), len(credits)
assert m == n
total_score, total_credit = 0, 0
for i in range(m):
total_score += scores[i] * credits[i]
total_credit += credits[i]
return int(total_score/total_credit + 0.5)
def ave_gpa(scores, credits):
m, n = len(scores), len(credits)
assert m == n
total_gpa, total_credit = 0, 0
for i in range(m):
total_gpa += trans_gpa(scores[i]) * credits[i]
total_credit += credits[i]
return total_gpa/total_credit
#定义窗口继承类
class myWin(gpaTool.MyFrame1):
def __init__( self , parent ):
gpaTool.MyFrame1.__init__(self, parent)
self.msg = []
self.dir_name = ''
def kill_focus(self, sth):
sth.Disable()
sth.Enable()
def jump2(self, event):
self.textctrl_2.SetFocus()
self.textctrl_2.Clear()
def jumpb(self, event):
self.send(None)
def jump3(self, event):
self.textctrl_3.SetFocus()
self.textctrl_3.Clear()
def send(self, event):
lesson_name = self.textctrl_1.GetValue()
if not lesson_name :
dlg = wx.MessageBox("No Lesson Name!")
self.textctrl_1.SetFocus()
return
if " " in lesson_name :
dlg = wx.MessageBox("No Space Allowed!")
self.textctrl_1.SetFocus()
return
try:
lesson_credit = float(self.textctrl_2.GetValue())
if lesson_credit <= 0:
raise ValueError("")
except:
dlg = wx.MessageBox("Wrong Credit!")
self.textctrl_2.SetFocus()
return
try:
lesson_score = int(self.textctrl_3.GetValue())
if lesson_score < 0 or lesson_score > 100:
raise ValueError("")
except:
dlg = wx.MessageBox("Wrong Score!")
self.textctrl_3.SetFocus()
return
lesson_core = int(self.checkbox_core.IsChecked())
msg = "LESSON: " + lesson_name + " " + str(lesson_credit) + " points, "
if (lesson_core):
msg += "Core " + str(lesson_score) +" scores, " + str(trans_gpa(lesson_score)) + " level;\n"
else:
msg += "Not-Core " + str(lesson_score) +" scores, " + str(trans_gpa(lesson_score)) + " level;\n"
if msg in self.msg:
dlg = wx.MessageBox("Duplicate Lesson!")
else :
self.msg.append(msg)
self.textctrl_info.WriteText(msg)
self.score_update(None)
self.textctrl_1.SetFocus()
def score_update_(self,event):
info = self.textctrl_info.GetValue().strip("\n").split('\n')
if not info:
self.textctrl_score.Clear()
return
for i in range(len(info)):
tmp = info[i].split()
gpa = str(trans_gpa(int(tmp[5])))
if tmp[7] != gpa:
info[i] = info[i].split("scores,")[0] + " " + gpa + " level;"
info = "\n".join(info) + "\n"
self.textctrl_info.Clear()
self.textctrl_info.WriteText(info)
self.score_update(None)
def score_update(self, event):
score_core, score_not_core, credit_core,credit_not_core = [], [], [], []
info = self.textctrl_info.GetValue().strip("\n").split('\n')
if not info:
self.textctrl_score.Clear()
return
try:
for i in info:
tmp = [x for x in i.split()]
if tmp[4] == "Core":
score_core.append(int(tmp[5]))
credit_core.append(float(tmp[2]))
score_not_core.append(int(tmp[5]))
credit_not_core.append(float(tmp[2]))
except:
dlg = wx.MessageBox("Something Wrong Within The Info Box?")
self.textctrl_info.SetFocus()
return
msg = "Semester: "
tmp = self.textctrl_semester.GetValue()
if not tmp:
msg += "Not-Named , \n"
else:
msg += (tmp + " , \n")
msg += "For Core Lessons:\n\tAverage Score: " + str(ave_score(score_core,credit_core)) + ",\n\tGPA: " + str(ave_gpa(score_core,credit_core)) + ";\n"
msg += "For ALL Lessons:\n\tAverage Score: " + str(ave_score(score_not_core,credit_not_core)) + ",\n\tGPA: " + str(ave_gpa(score_not_core,credit_not_core)) + " //"
self.textctrl_score.Clear()
self.textctrl_score.WriteText(msg)
def clearall(self, event):
dlg = wx.MessageDialog(None, "Comfirm Clearing ?", "Delete", wx.YES_NO | wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
self.textctrl_1.Clear()
self.textctrl_2.Clear()
self.textctrl_3.Clear()
self.textctrl_semester.Clear()
self.textctrl_info.Clear()
self.textctrl_score.Clear()
dlg.Destroy()
def savescore(self, event):
fd = wx.FileDialog(self, 'Save Score To ...', self.dir_name, '.txt', 'TEXT file(*.txt)|*.txt', wx.FD_SAVE)
if fd.ShowModal() == wx.ID_OK:
self.file_name = fd.GetFilename()
self.dir_name = fd.GetDirectory()
try:
with open(os.path.join(self.dir_name, self.file_name), 'a', encoding='utf-8') as f:
f.write(self.textctrl_score.GetValue() + "\n")
save_msg = wx.MessageDialog(self, 'Successfully Saved', 'Hint')
except FileNotFoundError:
save_msg = wx.MessageDialog(self, 'Invalid Path', 'Hint')
save_msg.ShowModal()
save_msg.Destroy()
fd.Destroy()
def saveinfo(self, event):
fd = wx.FileDialog(self, 'Save Lessons To ...', self.dir_name, '.txt', 'TEXT file(*.txt)|*.txt', wx.FD_SAVE)
if fd.ShowModal() == wx.ID_OK:
self.file_name = fd.GetFilename()
self.dir_name = fd.GetDirectory()
try:
with open(os.path.join(self.dir_name, self.file_name), 'a', encoding='utf-8') as f:
f.write(self.textctrl_semester.GetValue() + "\n" + self.textctrl_info.GetValue() + "\n")
save_msg = wx.MessageDialog(self, 'Successfully Saved', 'Hint')
except FileNotFoundError:
save_msg = wx.MessageDialog(self, 'Invalid Path', 'Hint')
save_msg.ShowModal()
save_msg.Destroy()
fd.Destroy()
def saveall(self, event):
fd = wx.FileDialog(self, 'Save All Information To ...', self.dir_name, '.txt', 'TEXT file(*.txt)|*.txt', wx.FD_SAVE)
if fd.ShowModal() == wx.ID_OK:
self.file_name = fd.GetFilename()
self.dir_name = fd.GetDirectory()
try:
with open(os.path.join(self.dir_name, self.file_name), 'w', encoding='utf-8') as f:
f.write(self.textctrl_score.GetValue() + "\n" + self.textctrl_info.GetValue())
save_msg = wx.MessageDialog(self, 'Successfully Saved', 'Hint')
except FileNotFoundError:
save_msg = wx.MessageDialog(self, 'Invalid Path', 'Hint')
save_msg.ShowModal()
save_msg.Destroy()
fd.Destroy()
def on_close(self, event):
dlg = wx.MessageDialog(None, u"Confirm Quiting ?", u"Quit", wx.YES_NO | wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
self.Destroy()
else:
pass
dlg.Destroy()
if __name__ == '__main__':
myApp = wx.App(False)
runWin = myWin(None)
runWin.Show()
myApp.MainLoop()
The code is arranged by some order and can be skipped if not important.
The final appearance is :