此篇中,介绍的是个第三方库,而该库的书籍,Creating GUI Applications with wxPython Kindle Edition, 358 pages,完全是把一个前端教程缩减到一章中,所以内容不连贯、缺少很多内容,但一个没有GUI的程序,只能很简单的使用,所以GUI非常需要。而之后几章介绍的是网络编程和人工智能,所以这章结束后,真的要停下巩固下,做几个完善的应用。
wxPython库
目前基于Python的图形用户界面(Graphical User Interface,GUI)程序开发库又多个,如Tkinter、PyQt、wxPython等库。其中Tkinter库是基于Python的标准GUI工具包接口,PyQt库是Python和Qt库的成功融合。Tkinter库和Jython相比,wxPython库是一个开源、跨平台、支持GUI程序开发的第三方库,允许Python程序员方便的创建完整、功能健全的GUI程序。
wxPython-4.1.1-cp39-cp39-win_amd64.whl (18.1 MB)
Pillow-8.0.1-cp39-cp39-win_amd64.whl (2.1 MB)
例
import wx
#创建App对象
app=wx.App()
#创建并设置Frame窗体
frame=wx.Frame(None,title="第1个wxPython程序",size=(300,150))
#创建面板,添加到窗体frame中
panel=wx.Panel(frame)
#创建、设置标签,添加到面板panel中
sTxt_info=wx.StaticText(panel,label="欢迎来到Python世界!",pos=(60,40))
#显示窗体
frame.Show(True)
#启动app.MainLoop()方法,进入循环
app.MainLoop()
Mainloop:Execute the main GUI event loop 执行主GUI事件循环
事件循环就是将要执行的事件从栈里一个个的读取执行,这个一个个就是循环过程,直至栈空,再无事件。
可以理解为执行上述app的操作。
使用wxPython建立GUI程序的步骤如下
(1)导入wx模块
import wx
(2)创建一个wx.App实例
app=wx.App()
(3)创建一个顶层窗体
frame=wx.Frame(None)
(4)显示窗体
frame.Show(True)
(5)进入应用程序循环,等待处理事件
app.MainLoop()
事件处理
事件处理是wxPython程序工作的基本机制
1、事件和事件处理机制
事件指发生在系统中的事,应用程序通过触发相应的功能对他进行响应。事件可以是低级的用户动作,如鼠标移动、键盘按下等;也可以是高级的用户动作,如单击按钮、选中列表框选项等;也可能是其他方式,如网络连接、定时器等。
wxPython程序中与事件和事件处理相关的主要概念如下:
(1)事件(Event):在应用程序期间发生的事,要求有一个相应
(2)事件对象(Event Object):代表一个事件,其中包括事件的数据等属性,是类wx.Event或其子类的对象,子类如wx.CommandEvent和wx.MouseEvent
(3)事件类型(Event Type):分配给每个事件对象的一个整数ID,如wx.MouseEvent的事件类型标识了该事件是一个鼠标单击还是一个鼠标移动
(4)事件源(Event Source):能产生事件的任何wxPython对象,如按钮、菜单、列表框等控件
(5)事件处理器(Event Handler):响应事件调用的方法,也称为处理器方法
(6)事件绑定器(Event Binder):一个封装了特定控件、特定事件类型和一个事件处理器的wxPython对象,为了被调用,所有事件处理器必须用一个事件绑定器注册
(7)wx.EvtHandler(事件处理者):其对象在一个特定类型、一个事件源和一个事件处理器之间创建绑定
wxPython应用程序将特定类型的事件和特定的代码相关联进行工作,该代码在响应事件时执行,事件被映射到代码的过程称为事件处理。和大多数GUI程序相似,wxPython程序采用基于事件的驱动机制
2、事件绑定
事件绑定器有wx.PyEventBinder的对象组成。事件绑定器的对象名字是全局性的,以wx.EVT_开头。wxPython中有各种不同类型的事件绑定器,用于将不同类型的控件和一个事件对象、一个处理器方法连接起来,使wxPython程序能够通过执行处理器方法中的代码响应控件上的事件。
绑定控件事件可使用wx.evtHandler的Bind()方法,其常用格式如下:
self.Bind(eventBinder,handler,sourceCtrl)或
self.sourceCtrl.Bind(eventBinder,handler)
其中,eventBinder为事件绑定器。Handler是一个可调用wxPython的对象,通常是一个被绑定的事件处理方法。sourceCtrl是产生该事件的源控件
在标签中实时显示鼠标在窗体中的坐标
import wx
#创建窗体类,继承wx.Frame
class MyFrame(wx.Frame):
#定义构造方法
def __init__(self):
#调用父类构造方法,设置窗体参数
wx.Frame.__init__(self,None,-1,"事件处理示例",size=(300,150))
panel=wx.Panel(self,-1) #创建面板
panel.Bind(wx.EVT_MOTION,self.OnMove) #绑定事件
#创建标签
self.sTxt_pos=wx.StaticText(panel,-1,"",pos=(70,30),size=(160,40))
#创建字体并设置为标签self.sTxt_pos的字体
font=wx.Font(20,wx.DECORATIVE,wx.NORMAL,wx.BOLD)
self.sTxt_pos.SetFont(font)
#鼠标移动事件处理方法
def OnMove(self,event):
pos=event.GetPosition() #返回鼠标坐标位置
self.sTxt_pos.SetLabel("%s:%s"%(pos.x,pos.y)) #显示鼠标坐标位置
if __name__=='__main__':
app=wx.App(False)
MyFrame().Show(True) #创建窗体对象,显示窗体
app.MainLoop()
常用控件
wxPython提供了丰富的空间,如窗体、按钮、标签、文本框、列表框,使Python程序员能够轻松的创建见状、功能强大的GUI程序
窗体
窗体(wx.Frame)是一个容器,可移动、缩放,可包含菜单、工具栏等,是所有窗体的父类,使用窗体时一般需要派生出子类。创建一个窗体时需要调用父类的构造方法设置参数,格式如下:
wx.Frame.__init__(parent,id,title,pos,size,style)
parent:窗体的父容器。如果窗体为*窗体,则parent的值为None
id:窗体的wxPython ID
title:窗体标题
pos:窗体左上角在屏幕中的位置,是元祖类型数据
size:为窗体的初始尺寸,是元祖类型数据
style:窗体样式,如wx.CAPTION、wx.CLOSE_BOX
wx.Frame的常用方法
(1)Centre:设置窗体显示在屏幕中间
(2)SetPosition(x,y):设置窗体在屏幕中的显示位置(x,y)
(3)SetSize((width,height)):设置窗体的大小(width,height)
(4)SetTitle(title):设置窗体的标题title
wx.Frame的常用事件绑定器
(1)wx.EVT_CLOSE:单击窗体“关闭”按钮时触发相关处理程序
(2)wx.EVT_MENU_OPEN:菜单刚打开时触发相关处理程序
(3)wx.EVT_MENU_CLOSE:菜单刚关闭时触发相关处理程序
窗体的创建和使用
import wx
#创建窗体类,继承wx.Frame
class MyFrame(wx.Frame):
#定义构造方法
def __init__(self,parent):
#调用父类构造方法,设置窗体参数
wx.Frame.__init__(self,parent,id=-1,title="西游记",size=(340,160))
panel=wx.Panel(self) #创建面板
#创建标签,在标签中写入内容,添加panel中
wx.StaticText(parent=panel,label="混沌未分天地乱,茫茫渺渺无人见。",pos=(70,20))
wx.StaticText(parent=panel,label="自从盘古破鸿蒙,开辟从兹清浊辨。",pos=(70,40))
wx.StaticText(parent=panel,label="覆载群生仰至仁,发明万物皆成善。",pos=(70,60))
wx.StaticText(parent=panel,label="欲知造化会元功,须看西游释厄传。",pos=(70,80))
self.Center() #设置窗体运行初始位置为桌面中间
if __name__=="__main__":
app=wx.App()
MyFrame(None).Show(True)
app.MainLoop()
按钮、标签和文本框
1、按钮
wxPython中提供了3种不同类型的按钮:wx.Button(简单、传统按钮)、wx.ToggleButton(二状态按钮/开关按钮/选中未选中)、wx.BitmapButton(带有位图的按钮)
创建一个按钮的常用构造方法:
wx.Button(parent,id,label,pos,size,style)
label:按钮上显示的内容
style:按钮样式,wx.BU_TOP、wx.BU_RIGHT等
常用方法:
(1)SetLabel(label):设置按钮上的显示内容label
(2)GetLabel():返回按钮上显示的内容
wx.Button最常用的事件绑定器为wx.EVT_BUTTON
2、标签
wx.StaticText提供了一行或多行的只读文本,通常放置在窗体上,或者作为另一个插件的标识符,或者作为信息串等。
wx.StaticText(parent,id,label,pos,size,style)
3、文本框
wx.TextCtrl用于接收从键盘输入的内容或显示文本信息。
wx.TextCtrl可以是单行、多行或密码字段。
wx.TextCtrl(parent,id,label,pos,size,style)
wx.TextCtrl常用方法:
(1)SetValue(text):设置文本框中的内容text
(2)GetValue():返回文本框中的内容
wx.TextCtrl的常用事件绑定器:
(1)wx.EVT_TEXT:当文本框中的内容发生变化时触发相关处理程序
(2)wx.EVT_TEXT_ENTER:当在文本框中按下Enter键时触发相关处理程序
(3)wx.EVT_TEXT_MAXLEN:当文本框中的文本长度达到指定最大值时触发相关处理程序
【例】使用按钮、标签和文本框编写一个求和程序
import wx
class MyFrame(wx.Frame):
def __init__(self,superion):
wx.Frame.__init__(self,parent=superion,title="2个数求和",size=(300,200))
panel=wx.Panel(self) #创建面板
#创建标签
wx.StaticText(parent=panel,label="请输入操作数1:",pos=(30,10))
wx.StaticText(parent=panel,label="请输入操作数2:",pos=(30,50))
wx.StaticText(parent=panel,label="结 果",pos=(30,90))
#创建文本框
self.txt_op1=wx.TextCtrl(parent=panel,pos=(120,10))
self.txt_op2=wx.TextCtrl(parent=panel,pos=(120,50))
self.txt_res=wx.TextCtrl(parent=panel,pos=(120,90),style=wx.TE_READONLY)
#创建按钮并绑定事件
self.btn_Add=wx.Button(parent=panel,label="求和",pos=(70,130))
self.Bind(wx.EVT_BUTTON,self.On_btn_Add,self.btn_Add)
self.btn_Quit=wx.Button(parent=panel,label="退出",pos=(150,130))
self.Bind(wx.EVT_BUTTON,self.On_btn_Quit,self.btn_Quit)
#“求和”按钮事件处理方法
def On_btn_Add(self,event):
op1=int(self.txt_op1.GetValue()) #返回文本框的内容
op2=int(self.txt_op2.GetValue())
sum=op1+op2 #求和
self.txt_res.SetValue(str(sum)) #将求和结果显示在文本框中
#“退出”按钮事件处理结果
def On_btn_Quit(self,event):
wx.Exit() #退出程序
if __name__=="__main__":
app=wx.App()
MyFrame(None).Show()
app.MainLoop()
这里有个问题,从文本输入框到求和,运行没问题,但是到退出,需要点击两次
单选按钮、复选框
单选按钮(wx.RadioButton)表示用户只能从多种可选按钮里选择一个选项,每个选项按钮包括一个圆形按钮和旁边的文本标签
wx.RadioButton(parent,id,label,pos,size,style)
如果要创建一组相互可选择的按钮,需要将wx.RadioButton对象的样式参数设置为wx.RB_GROUP,后续按钮对象会被添加到一组,或者使用wx.RadioBox(创建一组按钮)。
wx.RadioButton的常用方法:
(1)GetValue():返回单选按钮的状态
(2)GetLabel():返回单选按钮的标签文本
(3)SetValue():设置单选按钮的状态,为选择(True)/未选中(False)
(4)SetLabel():设置单选按钮标签的显示内容label
常用事件绑定器:wx.EVT_BUTTON
复选框(wx.CheckBox)提供了可以进行复选的空间,显示为一个小标记的矩形框,其方法和wx.RadioBox()基本相同
import wx
class MyFrame(wx.Frame):
def __init__(self,parent,title):
super(MyFrame,self).__init__(parent,title=title,size=(320,200))
self.InitUI()
def InitUI(self):
panel=wx.Panel(self)
#复选框
self.cb_music=wx.CheckBox(panel,label="音乐",pos=(10,10))
self.cb_movie=wx.CheckBox(panel,label="电影",pos=(10,40))
self.cb_artist=wx.CheckBox(panel,label="艺术",pos=(10,70))
#选项列表,单选按钮
ageList=["儿童","青年","中年","老年"]
self.rb_age=wx.RadioBox(panel,label="年龄",pos=(80,10),choices=ageList,majorDimension=1,style=wx.RA_SPECIFY_ROWS)
self.btn_ok=wx.Button(parent=panel,label="确定",pos=(110,110))
self.Bind(wx.EVT_BUTTON,self.On_btn_ok,self.btn_ok)
self.Center()
self.Show(True)
#按钮事件
def On_btn_ok(self,event):
str="您的年龄段:"+self.rb_age.GetStringSelection()+"\n您的爱好:"
if self.cb_music.GetValue():str=str+self.cb_music.GetLabel()
if self.cb_movie.GetValue():str=str+self.cb_movie.GetLabel()
if self.cb_artist.GetValue():str=str+self.cb_artist.GetLabel()
wx.MessageBox(str)
if __name__=="__main__":
app=wx.App()
MyFrame(None,"复选框和单选按钮的使用")
app.MainLoop()
这里出现了super,属于超类继承,解决多重继承的问题。
等同于之前代码中的:
super(MyFrame,self).__init__(parent,title=title,size=(320,200))
wx.Frame.__init__(self,parent,title=title,size=(320,200))
列表框、组合框
列表框(wx.ListBox)用于放置多个元素提供用户进行选择,每个元素都是字符串,支持用户单选和多选
wx.ListBox(panel,id,pos,size,choices,style)
choices为选项列表
ListBox常用方法:
(1)Append(item):在列表框尾部添加一个元素item
(2)Clear():删除列表框中的所有元素
(3)Delete(index):删除列表框指定索引index的元素
(4)GetCount():返回列表框中元素的个数
(5)GetSelection():返回当前选择项的索引,仅对单选列表框有效
(6)SetSelection(index,status):设置指定索引index元素的选择状态status
(7)GetString(index):返回指定索引index的元素
(8)SetString(index,item):设置指定索引index的元素item
(9)IsSelected(index):返回指定索引元素的选择状态
(10)Set(choices):使用列表choices重新设置列表框
ListBox的常用事件绑定器wx.EVT_LISTBOX,当ListBox选项被选中时,触发相关处理程序
组合框(wx.ComboBox)和列表框在窗体上的显示形式不同,用法基本类似
import wx
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None,title="ListBox使用",size=(250,170))
self.InitUI()
def InitUI(self):
self.Center() #窗体居中
panel=wx.Panel(parent=self)
wx.StaticText(panel,label="请选择你从事的行业:",pos=(10,10))
#创建列表框、添加选项和绑定事件
list=["教育培训","航空航天","石油化工","交通运输","医药卫生","旅游休闲"]
self.listbox=wx.ListBox(panel,-1,choices=list,style=wx.LB_SINGLE,pos=(140,10))
self.Bind(wx.EVT_LISTBOX,self.on_listbox,self.listbox) #绑定事件
self.Show()
#列表事件处理方法
def on_listbox(self,event):
wx.MessageBox(self.listbox.GetStringSelection())
if __name__=="__main__":
app=wx.App()
MyFrame().Show()
app.MainLoop()
菜单
在wxPython中,菜单结构包含菜单栏(MenuBar)、菜单或子菜单(Menu)和菜单项(Menuitem)三级结构,关系如下:
(1)一个窗体包含一个菜单栏,一个菜单栏只能属于一个窗体
(2)一个菜单栏包含零到多个菜单,一个菜单只能属于一个菜单栏
(3)一个菜单包含零到多个子菜单或菜单项,一个子菜单或菜单项只能属于一个菜单
(4)每个菜单项都可以单独绑定事件
在wxPython程序中创建菜单:
(1)创建菜单栏,把菜单栏添加到窗体中
(2)创建菜单,把菜单添加到菜单栏或一个父菜单
(3)创建菜单项,把菜单项添加到适当的菜单或子菜单
(4)为菜单项创建事件绑定
1、菜单栏(wx.MenuBar)
menuBar=wx.MenuBar()
常用方法:
(1)Append(menu.label):将显示内容为label的菜单menu添加到菜单栏的尾部(右侧)
(2)Insert(pos,menu,label):将显示内容为label的菜单menu插入到指定pos位置
(3)Remove(pos):删除位于pos位置的菜单
(4)EnableTop(pos.enable):设置pos未知的菜单状态enable,为True(可用)或False(不可用)
(5)GetLabelTop(pos):返回指定pos未知的菜单显示内容
(6)SetLabelTop(pos,title):设置指定pos位置的菜单显示内容label
创建菜单栏menuBar厚,可以通过方法SetMenuBar()将其添加到窗体:
self.SetMenuBar(menuBar)
2、菜单(wx.Menu)
menu=wx.Menu()
常用方法:
(1)Append(menuItem):将一个菜单项menuItem添加到菜单
(2)AppendMenu(id,label,subMenu,helpstring):为菜单添加一个子菜单subMenu,菜单项显示内容为label,helpstring为帮助信息(可选)
(3)AppendRadioItem(id,label,helpstring):为菜单添加一个单选按钮菜单项,显示内容为label
(4)AppendCheckItem(id,title,helpstring):为菜单添加一个复选框菜单项,显示内容为label
(5)Insert(pos,menuItem):在指定pos位置插入一个菜单项
(6)Remove(menuItem):从菜单中删除菜单项menuItem
(7)GetMenuItems():返回菜单的菜单项列表
(8)AppendSeparator():在菜单上添加一条分割线
3、菜单项(wx.MenuItem)
可使用Append()方法将菜单项添加到菜单;或者先创建一个菜单项,在添加到菜单
wx.Menu.Append(id,label,kind)或
menuItem=wx.MenuItem(menu,id,label,helpstring,kind)
wx.Menu.Append(menuItem)
其中,
menu:添加菜单项menuItem的菜单
id:菜单项ID。wxPython中有大量的标准ID被分配给标准菜单项,如wx.ID_NEW、wx.ID_OPEN被分配给标准菜单项“新建”和“打开”
label:菜单项显示内容
helpstring:帮助信息,可选
kind:菜单项类型,是常量,一般从可选性wx.ITEM_NORMAL(普通菜单项,默认选项)、wx.ITEM_CHECK(切换菜单项)、wx.ITEM_RADIO(单选菜单项)中选择一种
菜单项menuItem的常用事件绑定器为wx.EVT_MENU
可以为菜单项天假组合键,方法为:在菜单项显示内容厚放置一个\t,然后再\t厚定义组合键,例如“新建\tCtrl+N”为菜单项定义一个组合键Ctrl+N,在打开的菜单程序中按Ctrl+N组合键则运行“新建”菜单项
import wx
class MyFrame(wx.Frame):
def __init__(self,*args,**kw):
super(MyFrame,self).__init__(*args,**kw)
self.InitUI()
def InitUI(self):
#创建菜单栏
self.menuBar=wx.MenuBar()
#创建菜单,添加到菜单栏
self.menu_file=wx.Menu()
self.menu_edit=wx.Menu()
self.menu_about=wx.Menu()
self.menuBar.Append(self.menu_file,"文件")
self.menuBar.Append(self.menu_edit,"编辑")
self.menuBar.Append(self.menu_about,"关于")
#创建菜单项,添加到指定菜单
self.Menuitem_new=self.menu_file.Append(wx.ID_NEW,"新建\tCtrl+N")
self.Menuitem_open=self.menu_file.Append(wx.ID_OPEN,"打开\tCtrl+O")
self.Menuitem_save=self.menu_file.Append(wx.ID_SAVE,"保存\tCtrl+S")
self.Menuitem_exit=self.menu_file.Append(wx.ID_EXIT,"退出\tCtrl+E")
#将菜单栏添加到窗体
self.SetMenuBar(self.menuBar)
#绑定事件
self.Bind(wx.EVT_MENU,self.OnNew,self.Menuitem_new)
self.Bind(wx.EVT_MENU,self.OnOpen,self.Menuitem_open)
self.Bind(wx.EVT_MENU,self.OnSave,self.Menuitem_save)
self.Bind(wx.EVT_MENU,self.OnQuit,self.Menuitem_exit)
#设置窗体
self.SetSize((400,200))
self.SetTitle("菜单使用示例")
self.Center()
self.Show()
#菜单项事件处理方法
def OnNew(self,e):wx.MessageBox("您选择了新建(New)菜单")
def OnOpen(self,e):wx.MessageBox("您选择了打开(Open)菜单")
def OnSave(self,e):wx.MessageBox("您选择了保存(Save)菜单")
def OnQuit(self,e):self.Close() #退出程序
if __name__=="__main__":
app=wx.App()
MyFrame(None)
app.MainLoop()
不是很明白最开始的super语句???
弹出式菜单的创建和使用
import wx
class MyFrame(wx.Frame):
def __init__(self,*args,**kw):
super(MyFrame,self).__init__(*args,**kw)
self.panel=wx.Panel(self)
self.InitUI()
def InitUI(self):
#创建菜单栏和菜单
menuBar=wx.MenuBar()
self.popupMenu=wx.Menu()
#创建菜单项,添加到菜单
self.popupCopy=self.popupMenu.Append(wx.ID_COPY,"复制")
self.popupCut=self.popupMenu.Append(wx.ID_CUT,"剪切")
self.popupPaste=self.popupMenu.Append(wx.ID_PASTE,"粘贴")
#绑定右键弹出菜单事件
self.Bind(wx.EVT_CONTEXT_MENU,self.OnRClick)
#绑定菜单项事件
self.Bind(wx.EVT_MENU,self.OnpopupCopy,self.popupCopy)
self.Bind(wx.EVT_MENU,self.OnpopupCut,self.popupCut)
self.Bind(wx.EVT_MENU,self.OnpopupPaste,self.popupPaste)
#将菜单栏添加到窗体
self.SetMenuBar(menuBar)
#设置窗体
self.SetSize((400,150))
self.SetTitle("弹出式菜单使用实例")
self.Center()
self.Show()
#右键弹出菜单事件处理方法
def OnRClick(self,event):
pos=event.GetPosition()
pos=self.panel.ScreenToClient(pos)
self.panel.PopupMenu(self.popupMenu,pos)
#菜单项事件处理方法
def OnpopupCopy(self,event):wx.MessageBox("您选择了复制(Copy)菜单项!")
def OnpopupCut(self,event):wx.MessageBox("您选择了剪切(Cut)菜单项!")
def OnpopupPaste(self,event):wx.MessageBox("您选择了粘贴(Paste)菜单项!")
if __name__=="__main__":
app=wx.App()
MyFrame(None)
app.MainLoop()
工具栏、状态栏
1、工具栏(wx.ToolBar)包括文本文字说明或突变按钮的一个或多个水平条,通常放置在菜单栏的正下方
wx.ToolBar(parent,id,pos,size,style)
style为工具栏样式,可以实wx.TB_FLAT(平面效果)、wx.TB_HORIZONTAL(水平布局)、wx.TB_VERTICAL(垂直布局)、wx.TB_DOCKABLE(工具栏浮云和可停靠)等
wx.ToolBar的常用方法:
(1)AddTool(id,label.bitmap):添加一个按钮到工具栏,bitmap为按钮图标
(2)AddRadioTool(id,label,bitmap):添加一个单选按钮(有图标和标签)到工具栏,ID为id
(3)AddChechTool(id,label,bitmap):添加一个切换按钮(有图标和标签)到工具栏,ID为id
(4)AddLabelTool(id,label,bitmap):添加一个标签(有图标和标签)到工具栏,ID为id
(5)RemoveTool(id):删除工具栏上指定id的按钮
(6)ClearTools():删除工具栏上的所有按钮
wx.ToolBar的常用事件绑定器为wx.EVT_TOOL
2、状态栏
状态栏(StatusBar)通常放在窗体底部,显示程序运行时的一些状态信息。
wx.StatusBar(parent,id,pos,size,style)
wx.StatusBar的常用方法:
(1)CreateStatusBar():添加一个状态栏
(2)SetFieldsConut(fieldValue):设置状态分区个数fieldValue
(3)SetStatusText(string):设置状态栏内容string
(4)SetStatusWidth(valueList):设置各栏宽度valueList
import wx
class MyFrame(wx.Frame):
def __init__(self,parent,title):
super(MyFrame,self).__init__(parent,title=title)
self.InitUI()
def InitUI(self):
#创建并设置工具栏
tb=wx.ToolBar(self,-1)
self.ToolBar=tb
#在工具栏上添加按钮
tb.AddRadioTool(101,'',wx.Bitmap("F:/2019/Documents/python/PNG/70.png"))
Open=tb.AddRadioTool(102,'',wx.Bitmap("F:/2019/Documents/python/PNG/1950.png"))
Save=tb.AddRadioTool(103,'',wx.Bitmap("F:/2019/Documents/python/PNG/1958.png"))
tb.Bind(wx.EVT_TOOL,self.OnClick)
tb.Realize()
#创建并设置文本区
self.text=wx.TextCtrl(self,-1,style=wx.EXPAND|wx.TE_MULTILINE)
self.text.Bind(wx.EVT_MOTION,self.OnTextMotion)
#创建并设置状态栏
self.statusbar=self.CreateStatusBar()
self.statusbar.SetFieldsCount(2)
self.statusbar.SetStatusWidths([-1,-2])
#设置窗体
self.SetSize((500,240))
self.Centre()
self.Show(True)
def OnClick(self,event):
if str(event.GetId())=="101":
self.text.AppendText("您选择了新建(New)"+"\n")
if str(event.GetId())=="102":
self.text.AppendText("您选择了打开(Open)"+"\n")
if str(event.GetId())=="103":
self.text.AppendText("您选择了保存(Save)"+"\n")
def OnTextMotion(self,event):
#设置状态栏内容
self.statusbar.SetStatusText("鼠标位置:"+str(event.GetPosition()),0)
self.statusbar.SetStatusText(("文本框状态:"+str(self.text.IsEmpty())),1)
if __name__=="__main__":
ex=wx.App()
MyFrame(None,"工具栏和状态使用示例")
ex.MainLoop()
出现了两个问题,首先这些png来自于系统shell.dll提取,然后用gimp缩放保存。每次启动会提示警告。
这个有一次用系统画图板保存就没问题,但是他不能保存透明。
还有一个是和之前的GUI一样的问题,默认按钮是新建且按下的状态,此时点击新建是无法触发效果的。
对话框
通常作为父窗体顶部的弹出窗体,对话框可以是模式(阻止父窗体)或无模式(对话框可被绕过)
wx.Dialog(parent,id,title,pos,size,style)
style为对话框样式如wx.OK、wx.CANCEL等
wxPython程序中用得较多的是一些预配置对话框(wx.Dialog的子类),如消息对话框(wx.MessageBox)、输入对话框(wx.TextEntryDialog)、文件对话框(wx.FileDialog)、字体对话框(wx.FontDialog)等。
词源:dia-log
dia:两个人之间
log:木头,航海日志,日志。最初用木头浸入水中,应该是航行时看木头的角度来确定航速。于是这种用log(木头)的测速记录,被叫做航海日志,进而演变成日志、计算机日志
两个人之间的日志,对话,会话,对话框
1、消息对话框(wx.MessageDialog)
用于向用户显示信息,如提示信息、警告消息等
wx.MessageDialog(parent,message,title=None,style=None,pos=None)
message为显示在消息对话框中的内容。
title为消息对话框的标题。
style为消息对话框的样式,包括按钮样式如wx.OK、wx.CANCEL等,和图标样式如wx.ICON_ERROR、wx.ICON_EXCLAMATION等,可以是二者之一或者是他们的组合。
除此之外,还有一种创建消息对话框的简短方法:创建wx.MessageBox类的一个对象,调用ShowModal(),返回wx.YES、wx.NO、wx.CANCEL、wx.OK
wx.MessageBox(message,caption=None,style=None)
2、输入对话框(wx.TextEntryDialog)
输入对话框在运行时弹出一个对话框供用户输入消息
wx.TextEntryDialog(parent,id,message,title,defaultValue,style,pos)
message为显示在输入对话框中的提示信息,defaultValue为默认值,style为对话样式,如wx.OK、wx.CANCEL等
wx.TextEentryDialog常用方法:
(1)SetMaxLength(max):设置用户可以输入到输入对话框中的最大字符数max
(2)SetValue(string):设置输入对话框的内容string
(3)GetValue():返回输入对话框的内容
(4)ShowModal():显示对话框。确认输入,返回wx.ID_OK;否则,返回wx.ID_CANCEL
import wx
class MyFrame(wx.Frame):
def __init__(self,superion):
wx.Frame.__init__(self,parent=superion,title='TextEntryDialog使用示例',size=(260,200))
panel=wx.Panel(self)
wx.StaticText(parent=panel,label="您的姓名:",pos=(15,55),size=(160,30))
self.txt_name=wx.TextCtrl(parent=panel,pos=(75,50),size=(160,30),style=wx.TE_READONLY)
self.btn_name=wx.Button(parent=panel,label="请输入姓名",pos=(85,110))
self.Bind(wx.EVT_BUTTON,self.On_btn_name,self.btn_name)
#按钮事件
def On_btn_name(self,event):
dlg=wx.TextEntryDialog(self,"请输入您的姓名:","文本输入对话框")
if dlg.ShowModal()==wx.ID_OK:
self.txt_name.SetValue(dlg.GetValue())
dlg.Destroy()
if __name__=="__main__":
app=wx.App()
MyFrame(None).Show()
app.MainLoop()
这个再点开对话框,是空内容的
3、文件对话框(wx.FileDialog)
使用文件对话框可以浏览文件系统,选择打开文件或保存文件等
wx.FileDialog(parent,message,DefaultDir,DefaultFile,wildcard,style,pos,size)
其中,message为对话框的标题栏显示内容。DefaultDir为默认文件夹。DefaultFile为默认选择文件。wildcard为通配符,指定要选择的文件类型,格式是<display>|<wildcard>,如"jpg files(*.jpg)|*.jpg|All files(*.*)|*.*"
wx.FileDialog常用方法:
(1)GetDirectory():返回默认目录
(2)GetFilename():返回默认文件名
(3)GetFilenames(self):返回用户选择的文件列表
(4)GetPath():返回选定文件的完整路径
(5)SetDirectory(dir):设置默认目录dir
(6)SetFilename(filename):设置默认文件filename
(7)SetPath(path):设置文件路径path
(8)ShowModal():模态显示对话框(无法绕过,无法操作其他层窗口),如果用户单击OK按钮,则返回wx.ID_OK,否则返回wx.ID_CANCEL
import wx
class MyFrame(wx.Frame):
def __init__(self,superion):
wx.Frame.__init__(self,parent=superion,title='fileDialog使用示例',size=(300,180))
panel=wx.Panel(self)
wx.StaticText(parent=panel,label="当前文件:",pos=(20,50),size=(60,30))
self.text_file=wx.TextCtrl(parent=panel,pos=(80,50),size=(180,30))
self.btn_file=wx.Button(parent=panel,label="选择文件",pos=(100,110))
self.Bind(wx.EVT_BUTTON,self.On_btn_file,self.btn_file)
#按钮事件
def On_btn_file(self,event):
dlg=wx.FileDialog(self)
if dlg.ShowModal()==wx.ID_OK:
fileName=dlg.GetFilename()
self.text_file.SetValue(fileName)
dlg.Destroy()
if __name__=="__main__":
app=wx.App()
MyFrame(None).Show()
app.MainLoop()
4、字体对话框(wx.FontDialog)
字体对话框用于选择字体及相关参数
wx.FontDialog(parent,data)
parent为字体对话框的父容器。data为字体对象,如wx.FonrData()对象
import wx
class IsPrimeFrame(wx.Frame):
def __init__(self,superion):
wx.Frame.__init__(self,parent=superion,title="fontDialog使用示例",size=(240,220))
panel=wx.Panel(self)
self.txt_font=wx.TextCtrl(parent=panel,value="wx.fontDialog使用!",pos=(20,50),size=(200,30),style=wx.TE_READONLY) #创建文本框
#创建按钮,添加到面板中
self.btn_font=wx.Button(parent=panel,label="改变字体",pos=(70,130))
self.Bind(wx.EVT_BUTTON,self.On_btn_font,self.btn_font) #绑定事件
#按钮事件处理方法
def On_btn_font(self,event):
dlg=wx.FontDialog(self,wx.FontData())
if dlg.ShowModal()==wx.ID_OK:
font=dlg.GetFontData().GetChosenFont()
self.txt_font.SetFont(font)
if __name__=="__main__":
app=wx.App()
IsPrimeFrame(None).Show()
app.MainLoop()
布局
在wxPython中,一个控件可以通过两种方式布置在容器中:绝对定位和相对定位
(1)绝对定位。绝对定位通过指定像素为单位的绝对座标将控件放置在容器中。容器中的控件位置由构造方法pos参数确定。在容器中使用绝对定位布置控件存在着诸多不足,如当调整窗体时,控件无法随之适应变化;修改布局困难等。
(2)相对定位。相对定位采用布局管理器(Sizer)将控件放在容器的指定网格中。
wxPython使用布局管理器对容器中的控件进行管理的最大的好处是:当容器尺寸调整时,容器内的控件会自动重新计算最优化的大小和位置,不用像绝对定位为每个控件设计大小和位置
常用布局管理器:
(1)wx.GridSizer:二维网格布局,可以指定行列,容器中每个网格的尺寸都相同
(2)wx.FlexiGridSizer:类似于GridSizer,增加了一些灵活性,容器中网格的尺寸可以不同
(3)wx.GridBagSizer:最灵活的网格布局器,控件可以放置在容器的任意指定网格中
(4)wx.BoxSizer:允许控件按行或列的方式排放,容器中网格的尺寸可以不同,通常用于嵌套
(5)wx.StaticBoxSizer:提供围绕框边界和在顶部的标签
书中只介绍常用布局管理器wx.BoxSizer和wx.GridSizer的使用方法
将一个控件添加到布局管理器中可以使用Add()方法
sizer.Add(ctrl,proportion,flag,border)
其中ctrl为添加到布局管理器的控件,proportion为控件相对大小,flag用于控制对齐方式等。border为控件之间留下的像素空间
wx.BoxSizer
wx.BoxSizer将控件以行和列方式放置到容器中。
创建一个wx.BoxSizer对象的常用构造方法:
boxSizer=wx.BoxSizer(wx.HORIZONTAL或VERTICAL)
其中,wx.HORIZONTAL表示水平放置控件,wx.VERTICAL表示垂直放置控件
wx.BoxSizer常用属性:
(1)wx.EXPAND:设置控件大小以完全填满有效空间
(2)wx.SHAPED:与EXPAND相似,但保持比例不变
(3)wx.FIXED_MINSIZE:保持控件的最小尺寸
wx.BoxSizer的常用方法:
(1)SetOrientation(ori):设置容器中控件放置方向ori
(2)Insert(index,ctrl):在容器中指定位置index插入控件ctrl
(3)AddSpacer():为容器添加非伸缩性空间
(4)AddStretchSpacer():为容器添加伸缩性空间
(5)Remove(ctrl):从容器中删除指定控件ctrl
(6)Clear():从容器中删除所有控件
import wx
class MyFrame(wx.Frame):
def __init__(self,parent):
wx.Frame.__init__(self,parent,size=(500,200),style=wx.DEFAULT_FRAME_STYLE|wx.RESIZE_BORDER|wx.MAXIMIZE_BOX)
panel=wx.Panel(self,-1) #创建面板
box_h=wx.BoxSizer(wx.HORIZONTAL) #创建水平Boxsizer
txt_filePath=wx.TextCtrl(panel,-1) #创建文本框
btn_open=wx.Button(panel,-1,label="打开") #创建按钮
#将txt_filePath和btn_open添加到box_h中
box_h.Add(txt_filePath,proportion=1,flag=wx.EXPAND|wx.ALL,border=5)
box_h.Add(btn_open,proportion=0,flag=wx.ALL,border=5)
#创建编辑框
et_exit=wx.TextCtrl(panel,style=wx.TE_MULTILINE|wx.TE_RICH2)
box_v=wx.BoxSizer(wx.VERTICAL) #创建垂直Boxsizer
box_v.Add(box_h,proportion=0,flag=wx.EXPAND) #将box_h添加到box_v中
box_v.Add(et_exit,proportion=1,flag=wx.EXPAND,border=5)
panel.SetSizer(box_v) #设置panel布局
if __name__=="__main__":
app=wx.App()
frame=MyFrame(None)
frame.SetTitle("BoxSizer使用示例")
frame.Show()
app.MainLoop()
wx.GridSizer
wx.GridSizer将控件按从左到右、由上到下的顺序添加到容器的二维网格中。
wx.GridSizer(rows,columns,vgap,hgap)
其中,rows和columns为网格的行数和列数。vgap和hgap为相邻控件之间的纵向和横向间距
(1)Add():向wx.GridSizer中添加控件
(2)SetRows(rows):设置容器中网格的行数rows
(3)GetRows():返回容器中网格的行数
(4)SetCols(cols):设置容器中网格的列数cols
(5)GetCols():返回容器中网格的列数
(6)SetVGap(gap):设置容器中网格单元格之间的垂直间隙gap(单位为像素)
(7)GetVGap():返回容器中网格单元之间的垂直间隙值
(8)SetHGap():设置容器中网格单元的水平间隙gap
(9)GetHGap():返回网格单元之间的水平间隙值
import wx
class MyFrame(wx.Frame):
def __init__(self,parent,title):
super(MyFrame,self).__init__(parent,title=title,size=(250,200))
self.InitUI()
self.Centre()
self.Show()
def InitUI(self):
panel=wx.Panel(self)
gridSizer=wx.GridSizer(4,2,10,10) #4*2网格
sTxt_name=wx.StaticText(panel,label="请输入您的姓名",style=wx.ALIGN_LEFT)
sTxt_sex=wx.StaticText(panel,label="请输入您的性别",style=wx.ALIGN_LEFT)
sTxt_age=wx.StaticText(panel,label="请输入您的年龄",style=wx.ALIGN_LEFT)
txt_name=wx.TextCtrl(panel)
txt_sex=wx.TextCtrl(panel)
txt_age=wx.TextCtrl(panel)
btn_ok=wx.Button(parent=panel,label="确定")
btn_exit=wx.Button(parent=panel,label="退出")
gridSizer.Add(sTxt_name,1,wx.EXPAND) # 位置:第1行第1列
gridSizer.Add(txt_name,1,wx.EXPAND) # 位置:第1行第2列
gridSizer.Add(sTxt_sex,1,wx.EXPAND) # 位置:第2行第1列
gridSizer.Add(txt_sex,1,wx.EXPAND) # 位置:第2行第2列
gridSizer.Add(sTxt_age,1,wx.EXPAND) # 位置:第3行第1列
gridSizer.Add(txt_age,1,wx.EXPAND) # 位置:第3行第2列
gridSizer.Add(btn_ok,1,wx.EXPAND) # 位置:第4行第1列
gridSizer.Add(btn_exit,1,wx.EXPAND) # 位置:第4行第2列
panel.SetSizer(gridSizer)
if __name__=="__main__":
app=wx.App()
MyFrame(None,title="GridSizer使用示例")
app.MainLoop()
典型案例——专利管理系统
就快结束了!……但这个案例只提供了思路和效果,并没代码,属于全书中第一个没有代码的案例。
开发一个基于wxPython的GUI专利管理系统
1、系统功能
本案例实现一个具有基本功能的专利管理系统。该系统包括两种角色:用户和管理员。非注册用户可以注册并在管理员通过审核后成为正式用户(简称用户)。用户可以申请和管理自己的专利,查询系统中已经审核的专利。管理员可以对用户和专利进行查询、审核等。
这个只是界面,准确地说,是书中的要求,所以这里暂且先把自己对需求的解读放一放,看看书中是怎样解释的。
其实这里面我唯一的疑惑就是专利查询,待审核的专利是否在管理员的专利查询页面,毕竟分两页可能会效率低一些,合并一页或者可选,更好一些。
而用户方面,是否可以看到待审核的专利,例如用户打算搞某方面的事,于是来查询现有专利,以确定攻关方向,但如果只能看到已有专利而不能看到待审核专利的话,就可能会撞车。
(1)专利信息表
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | PatentID | 专利编号 | int | 4 | N | PK,自增 |
2 | PatentName | 专利名称 | varchar | 50 | N | |
3 | PatentFunction | 功能描述 | varchar | 500 | Y | |
4 | BaseCategory | 基础类别 | varchar | 20 | N | FK,base_category(id) |
5 | RegionCategory | 地域类别 | varchar | 20 | N | FK,region_category(id) |
6 | FunctionCategory | 功能类别 | varchar | 20 | N | FK,function_category(id) |
7 | IDCard | 用户身份证号 | varchar | 50 | N | FK,user_info(IDCard) |
8 | ModifyDate | 修改时间 | date | Y | ||
9 | SubmitDate | 提交时间 | date | N | ||
10 | AuditDate | 审核时间 | date | Y | ||
11 | PatentStatus | 专利状态 | varchar | 20 | N | FK,patent_state(id) |
书中第8项和第9项的是否可以为空,两者是相反地,但我觉得不对劲
(2)用户信息表(user_info):
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | IDCard | 身份证号 | varchar | 50 | N | PK |
2 | TrueName | 真实姓名 | varchar | 20 | N | |
3 | Password | 用户密码 | varchar | 50 | N | |
4 | Gender | 性别 | varchar | 10 | Y | |
5 | Birthday | 出生日期 | varchar | Y | ||
6 | Job | 职业 | varchar | 20 | Y | FK,job(id) |
7 | School | 毕业学校 | varchar | 50 | Y | FK,school(id) |
8 | Degree | 学历/学位 | varchar | 50 | Y | FK,degree(id) |
9 | Unit | 单位 | varchar | 50 | Y | FK,unit(id) |
10 | Title | 职称 | varchar | 20 | Y | |
11 | Phone | 手机号 | varchar | 20 | Y | |
12 | QQ号 | varchar | 20 | Y | ||
13 | 电子邮箱 | varchar | 50 | Y | ||
14 | UseCategory | 用户类别 | varchar | 20 | Y | FK,user_category(id) |
15 | UserStatus | 用户状态 | varchar | 20 | Y |
(3)基础类别表(base_category),这里应该指专利的专业范围
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | BaseName | 名称 | varchar | 20 | N |
(4)地域类别表(region_category),这里应该指的是省市县这样的地域
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | RegionName | 名称 | varchar | 20 | N |
(5)功能类别表(function_category)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | FunctionName | 名称 | varchar | 20 | N |
(6)专利状态表(patent_state),这个比较奇怪,上面的功能元素是可扩展的,而专利状态没有几种,这个我只能认为这是主表的一个镜像,省去筛选的时间
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | StateName | 名称 | varchar | 20 | N |
(7)职业表(job)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | JobName | 名称 | varchar | 20 | N |
(8)毕业学校表(school)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | SchoolName | 名称 | varchar | 20 | N |
(9)学历/学位表(degree)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | DegreeName | 名称 | varchar | 20 | N |
(10)单位表(unit)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | UnitName | 名称 | varchar | 20 | N |
(11)用户类别表(user_category)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | CategoryName | 名称 | varchar | 20 | N |
(12)用户状态表(user_state)
序号 | 字段 | 含义 | 数据类型 | 数据长度 | 为空 | 备注 |
---|---|---|---|---|---|---|
1 | id | 编号 | varchar | 10 | N | PK |
2 | StateName | 名称 | varchar | 20 | N |
4、系统目录结构
专利管理系统主要包括如下源文件:
(1)main_page.py:主界面源文件
(2)login_page.py:登陆界面源文件
(3)register_page.py:注册界面源文件
(4)help_page.py:关于界面源文件
(5)admin_patent_info_page.py:管理员管理专利信息界面源文件
(6)admin_user_info_page.py:管理员管理用户信息界面源文件
(7)db_connect_info.text:数据库连接信息文件
(8)db_operation.py:数据库操作源文件
(9)patent.py:专利信息源文件
(10)user.py:用户信息源文件
(11)user_info_page.py:用户个人信息界面源文件
(12)user_patent_info_page.py:用户专利信息界面源文件
(13)user_query_patent_info_page.py:用户查询专利信息界面源文件
我这时想到之前专利状态为什么要单独列一个表,或者说其他一些元素为什么要单独列一个表。
其实不只是单一种类方便管理,更有减少数据库大小,例如一个学校的信息,假设10个字,若用附表连接,主表只需要附表内的编号,这样至少空间上就缩小了很多,至于效率,试过才知道。而状态,至少可以再附表中写入更多更详细的信息,例如对状态的解释。总之任何可重复的内容就可以用附表。
整个程序包含了数据库和代码两部分,而代码分为数据处理和界面两部分。
5、程序代码
本案例代码量比较大,此处没有提供详细的代码,请从资源网站下载… 那个站点略坑,需要积分。
嘛,其实这就是这章的最后内容了,作为一个单机程序,所介绍的内容都有了,就当作一个结业项目吧!
之后第13章:多进程和多线程,主要是进程间、线程间的通信。
第14章:网络程序设计,网络协议:通信原理;Web编程:将网页当作UI。
第15章:人工智能:模型训练,图像识别
6、系统运行结果主要界面
(1)运行系统,进入系统主界面
(2)注册。非注册用户单击“注册”按钮,在打开的注册窗体中进行注册。管理员审核同过后,注册用户(用户)可以申请专利
(3)登录。用户或管理员可以在登录窗体中输入相应的用户帐号和密码登录系统
(4)用户功能。用户登录成功后,可以完成如下功能
1)维护个人专利信息,包括添加新的专利、修改专利、查询专利、删除专利等
2)查询管理员已经审核通过的所有专利
3)对个人信息进行维护
(5)管理员功能。管理员登录成功后,可以完成如下功能
1)对用户提交的专利申请进行审核
2)对申请成为会员的用户进行审核
(6)设置连接服务器参数。用户和管理员可以设置连接服务器参数,以便正常登录系统
为了不限制大家的想象,请自行设置界面,其实是图太多,懒得搞了,就上一个典型的图,大家参考下!