作者:山上有风景
链接:原文链接
目录
主要使用wxPython(最成熟的跨平台python GUI工具包)
前戏:基础了解
WxPython的程序结构
窗口组成:
事件驱动:
常用组件(简单了解,后面还有):
基本组件
1.静态文本框
2.文本输入框
3.按钮控件
4.单选框
5.多选框
6.列表控件
7.使用sizer布局组件
菜单,窗口,对话框组件
菜单
窗口
对话框
窗口和对话框的交互
高级控件
表格控件
高级列表控件
树形控件
树表控件
定时器控件
WxPython库中的高级功能
主要使用wxPython(最成熟的跨平台python GUI工具包)
wxPython手册
前戏:基础了解
import wx class MyFrame(wx.Frame): #创建自定义Frame def __init__(self,parent): wx.Frame.__init__(self,parent,id=-1,title="Hello World",size=(300,300)) #设置窗体 """ panel和sizer是wxpython提供的窗口部件。是容器部件,可以用于存放其他窗口部件 """ panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) txt = wx.StaticText(panel,-1,"Hello World!") #创建静态文本组件 sizer.Add(txt,0,wx.TOP|wx.LEFT,100) self.Center() #将窗口放在桌面环境的中间 app = wx.App()
frame = MyFrame(None) #为顶级窗口 frame.Show(True) app.MainLoop()
补充:id是什么:wxpython对每个控件都会分配一个唯一的整数型ID,方便调用,类十余c中的Handle。ID在时间响应中必不可少。
创建控件时如果ID为-1时,会自动分配一个ID。这样就不能对ID进行引用。其实可以使用控件对象的GetId方法进行获取
前提: 唯一ID的生成:wx提供了NewId(),可以生成唯一的id。(若担心重复,可以使用RegisterID检测)
我们想要使用ID,但是程序中控件太多,如何处理
方法一:前置每个控件的ID(自己设置的名称),使用列表生成器生成
[Btn_1_ID,Btn_2_ID,Btn_3_ID] = [wx.NewId() for _init_cj in range(3)]有几个控件就设置循环几次
然后在生成控件时进行调用
方法二:使用类的属性来预先分配,然后控件类对他进行继承,每个控件类句读一个属于他的id属性
WxPython的程序结构
一个WxPython程序一般包含两个对象:应用程序对象和根窗口(可多个)。其中,应用程序对象可以使用wx.App(或子类)实现(用于处理窗口中的事件响应),根窗口可以使用wx.Frame实现。
其中OnInit方法中是必须的,其将会在事件循环处理之前被系统调用。同样的在事件结束之后调用的方法也存在(OnExit)
两个方法是相对的。例如:开启数据库和断开数据库
窗口组成:
前面的代码中窗口和应用程序似乎没有关联,为什么?
在应用程序对象中,可以通过SetTopWindow方法来设置根窗口。若是没有设置,是因为应用程序会选择窗口列表中的第一个作为根窗口。上面的程序也只有一个窗口,所以没有必要去显示设置。同样使用SetTopWindow也可以改变根窗口
控件的部分构造参数:
参数 | 说明 |
parent | 父窗口(为None代表为顶级窗口),注意设置父窗口后,在父窗口关闭时,子窗口也会消失 |
id | 上面说过了 |
title | 标题 |
pos | 相对于屏幕的位置默认(-1,-1),自行选择。我们在上面使用center也可以改变窗口在屏幕中的位置 |
size | 大小默认(-1,-1)窗口系统自行选择 |
style | 窗口类型... |
name | 窗口的内在名字 |
style风格:
事件驱动:
一个事件是wx.Event类或其子类的一个实例。
事件的绑定和处理:通过使用wx.EvtHandler类中的Bind方法可以将绑定事件的类型,事件对象和事件处理函数关联起来
补充:
在上面我们使用两种方法为Button按钮绑定了事件。有什么区别?
1.self.Bind(wx.EVT_BUTTON,self.OnClick,self.btn) #点击事件 2.self.btn.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow) #绑定鼠标进入事件
方式一:是是由父控件为子控件绑定的事件
方式二:是使用控件自己绑定事件
那么两者有何不同,可以混用吗
# self.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow,self.btn)
self.btn.Bind(wx.EVT_BUTTON,self.OnClick)
答案是:父类去绑定其他事件是不允许的,错误的,会导致父控件为其绑定的所有事件失效。而子类是可以绑定所有的相关事件的。
查看事件对象:
EVT_控件名 = wx.pyEventBinder( wxEVT_BUTTON, exceptedIDs) exceptedIDs默认为0,这类控件几乎都不为0,自己有定义
EVT_BUTTON = wx.PyEventBinder( wxEVT_BUTTON, 1) EVT_CHECKBOX = wx.PyEventBinder( wxEVT_CHECKBOX, 1) EVT_CHOICE = wx.PyEventBinder( wxEVT_CHOICE, 1) EVT_LISTBOX = wx.PyEventBinder( wxEVT_LISTBOX, 1) EVT_LISTBOX_DCLICK = wx.PyEventBinder( wxEVT_LISTBOX_DCLICK, 1) EVT_MENU = wx.PyEventBinder( wxEVT_MENU, 1) EVT_MENU_RANGE = wx.PyEventBinder( wxEVT_MENU, 2) EVT_SLIDER = wx.PyEventBinder( wxEVT_SLIDER, 1) EVT_RADIOBOX = wx.PyEventBinder( wxEVT_RADIOBOX, 1) EVT_RADIOBUTTON = wx.PyEventBinder( wxEVT_RADIOBUTTON, 1) ............
这一类事件都是对应控件去响应的,也就是说父类中包含这些控件的话,可以对其直接绑定这些基础事件响应。
所以说:要想使用父控件对子控件绑定事件:事件必须满足 EVT_控件名 相对应
事件中的Skip:
self.btn.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow) #绑定鼠标进入事件 self.btn.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow2) #绑定鼠标进入事件2 def OnEnterWindow(self,event): print("鼠标进入") self.btn.SetLabel("鼠标进入") def OnEnterWindow2(self,event): print("鼠标进入2") self.btn.SetLabel("鼠标进入2")
为同一个控件的同一个事件,绑定两个(多个)处理函数。
其执行结果是只执行了事件处理函数2:原因是因为:一般情况下,窗口部件对象在执行完该事件的一个处理函数,就默认完结了。不会再去处理其他相关的函数。而,事件绑定是从上到下,下面的会覆盖上面的,所以,值执行了处理函数了。
我们要想去执行其他函数,那么就要在正在执行的处理函数后面加上event.Skip(),他会在当前函数返回一个值后去调用下一个处理函数,改变原来的默认行为。
def OnEnterWindow(self,event): print("鼠标进入") self.btn.SetLabel("鼠标进入") # event.Skip() print(777) def OnEnterWindow2(self,event): print("鼠标进入2") self.btn.SetLabel("鼠标进入2") event.Skip() print(6666) --------------------------------------------------------------------- 鼠标进入2 6666 鼠标进入 777
常用组件(简单了解,后面还有):
对话框:
非模态对话框 dialog = wx.Dialog(None, title="你点不点", size=(300,200)) dialog.Show() 模态对话框 dialog = wx.Dialog(None, title="你点不点", size=(300,200)) dialog.ShowModel()
dialog = wx.MessageDialog(None, title="你点不点", size=(300,200)) dialog.ShowModel() 这里MessageDialog中show不会显示
dialog = wx.TextEntryDialog(None, message="请输入文件名:",caption="文件",value="dasfa") res = dialog.ShowModal()
工具栏:
Realize() 方法需要被调用,以最终确定工具栏创建
状态栏:
self.CreateStatusBar() self.SetStatusText("状态栏信息")
基本组件
1.静态文本框
2.文本输入框
相关参数:
常用方法:
3.按钮控件
补充:图片转换
bmp = wx.Image("1.jpg",wx.BITMAP_TYPE_JPEG).ConvertToBitmap() 1.wx.Image("1.jpg",wx.BITMAP_TYPE_JPEG)生成图片时,声明图片类型BITMAP_TYPE_JPEG 2.含有ConvertToBitmap函数,可以将Image对象转换为位图类型的对象
4.单选框
5.多选框
6.列表控件
listbox样式:
combobox的样式:
7.使用sizer布局组件
Sizer布局管理器是管理界面中各种控件的组件,使用Sizer组件可以自动解决控件的位置和控件之间的间距问题,提高GUI程序的可控性
(1)Sizer布局管理器使用介绍:
前面的程序都是通过设置构造函数的pos,size属性调整控件之间的位置。不太容易掌控位置。wxpython提供了Sizer布局管理器自动设置控件之间的位置。
使用步骤:
- 创建Sizers布局管理器。布局管理器分为grid sizer, flex grid sizer, grid bag sizer, box sizer,(都是继承父类Sizer)
- 调整SetSizer()将布局管理器添加到容器中,这里的容器一般指窗口,panel面板等组件。这样就建立了布局管理器和窗口之间的联系。
def SetSizer(self, sizer, deleteOld=True)
- 调用容器的Add()方法将各个组件添加到布局管理器中.
Add(self,item, proportion=0, flag=0, border=0, userData=None)
其中item是添加到布局管理器中的组件,
proportion是表示当前窗口大小发生改变时,控件之间的比例
flag表示窗口风格,对齐方式,边框等信息
border边框大小(前提是flag设置了边框)
userdata用于传递额外的数据 - 调用容器的Fit()方法计算布局管理器和容器窗口的大小,来自动调整窗口大小,使其适合当前窗口。
Fit(self)
(2)Grid Sizer布局
grid sizer布局是采用表格的形式分配各种控件,不需要设置控件在容器中的位置,直接添加到grid sizer布局管理器中即可
(3)Flex Grid Sizer布局
是对grid sizer的改进。可以用于存放大小可以改变的组件。
class FlexGridSizer(GridSizer) 继承于Grid Sizer
相对于Grid Sizer而言,他们的区别主要在于:
FlexGridSizer可以设置某一列或行的可变大小。主要在于下面的几个方法:
AddGrowableCol() 指定索引的列增长,如果额外的高度可用。 AddGrowRow() 指定的行定索引的增长,如果额外的宽度是可用的。 SetFlexibleDirection() 指定大小测定器的灵活性是否影响的行,列或两者。
其中FlexGridSizer的本身是固定不变的而Grid Sizer是随着窗口的变化,也会改变
使用特殊方法,来实现FlexGridSizer的专有属性
fgs.AddGrowableRow(2, 1) #第一个是行数 fgs.AddGrowableCol(1, 1) #第一个是列数
#第二个是比例,似乎一直没有啥特别的显示特征
#行和列的索引都是从0开始的
fgs.AddGrowableRow(2, 1) 允许改变第3行
fgs.AddGrowableCol(1, 1) 允许改变第二列的列宽
最后:符合我们所希望的:
这就是FlexGridSizer的特征
(4)Grid Bag Sizer布局
比FlexGridSize更加强大,可以是某个空间添加到特定的单元格,可以实现跨行,跨列显示
class GridBagSizer(FlexGridSizer)
(5)Box Sizer布局
box sizer是布局管理器中最简单,最灵活的一种布局,可以水平或垂直排列,是各个组件从左到右或从上到下排列在同一条线上。也可以嵌套使用。水平排列的布局可以嵌套水平或垂直排列的布局。这样是应用程序的布局更加灵活
class BoxSizer(Sizer)
补充:对于上面的布局也可以使用
对齐标志
边界标志:
行为标志:
wx.EXPAND 表示空间会填充满所有它能填充的地方,比如在一个box里面放一个空间,加上这个style=wx.EXPAND,控件就会占据整个box的空间。它是和wx.LEFT, wx.RIGHT这些style配合使用的。
补充:上面说的proportion比例问题
proportion控制控件相对大小,proportion如果为0,表示默认大小。
比如一个box里面有两个相同控件A,B,如果A,B的proportioin分别为2和1,那么A和B显示出来的大小比例就是2:1;
如果一个box里面有三个相同控件A,B,C,它们的proportion分别为0,1,1,那么A会是默认大小(比如一个只有一行的文本框),B,C平分这个box的其余部分。
比例只在box中有效,在其他布局中无效(有表格进行约束,不需要控件比例,但是可以用设置最小大小SetMinSize()来设置大小)
菜单,窗口,对话框组件
菜单
1.菜单的创建
创建步骤(重点)
(1)创建菜单栏:调用wx.MenuBar创建菜单栏。
menubar = wx.MenuBar()
(2)创建父菜单。调用wx.Menu类创建父菜单。
menu = wx.Menu()
(3)将父菜单添加到菜单栏中。调用菜单栏MenuBar类中的Append方法。
menuBar.Append(menu,"文件")
(4)添加子菜单,调用Menu类的Append方法直接添加子菜单
menu.Append(1000,"打开") #1000是子菜单的编号,也是需要唯一
(5)添加菜单事件:wx,EVT_MENU
(6)在窗口中添加上菜单栏:
self.SetMenuBar(menuBar)
MenuBar常用方法:
菜单的响应事件(除了上面的wx.EVT_MENU外):
多级菜单
Menu类的常用方法:
菜单的快捷键
使用&来形成快捷键
exit = menu.Append(-1,"&q 退出") #然后可以使用Alt + q触发该菜单的事件
对于父类菜单,一般快捷键是展开子菜单,Alt + m展开子菜单,再按下q快捷键退出
助记符在同一个菜单中是唯一的,在整个菜单栏中可以重复
位图菜单
补充:wx.Bitmap中无法修改图片的尺寸等信息。 我们可以先使用wx.Image()加载图片,再使用方法: img = wx.Image("1.gif",wx.BITMAP_TYPE_GIF) img.Rescale(20,10) bmp = wx.Bitmap(img) 进行修改尺寸等信息即可
弹出式菜单:单击鼠标右键后弹出的菜单
也是由Menu类创建的,只需要在上下文显示的容器中绑定wx.EVT_CONTEXT_MENU事件,然后又容器组件(如Panel)调用PopupMenu()方法弹出上下文菜单即可
def OnPopupIntemSelected(self,event): item = self.popopmenu.FindItemById(event.GetId()) self.textCtrl.SetLabel(item.GetText()) 从父级菜单中获取子菜单,可以通过Id获取,而ID来自于事件中GetId()获取当前响应的菜单索引
def OnPopup(self,event): pos = self.panel.ScreenToClient(event.GetPosition()) print(event.GetPosition(),pos) self.panel.PopupMenu(self.popopmenu,pos) #(218, 184) (85, 29) 事件event获取的坐标是屏幕坐标event.GetPosition(),我们需要先转换为当前面板坐标self.panel.ScreenToClient(event.GetPosition())
窗口
Frame窗口
用户的所有操作都是在窗口中完成的,wxPython使用wx.Frame类创建窗口。Frame类的构造函数:
class Frame(TopLevelWindow): def __init__(self, parent=None, id=None, title=None, pos=None,
size=None, style=None, name=None):
parent:不是当前窗口的父窗口
id:窗口编号
title:窗口的标题
pos:表示窗口显示的位置
size:不是窗口显示时的大小
style:用于地址窗口的外观
name:窗口的内在名字
MDI多文档界面
多文档程序窗口可以创建子窗口,子窗口有MDI多文档窗口管理和控制。使用MDIParentFrame创建
对话框
窗口和对话框的交互
注意:
对话框的组件支持验证能力,而窗口不支持组件的验证(不会去自动调用,我们可以自己去调用,但是何必呢)
其中似乎都没有用到验证类中的Validate方法(这个才是我们的验证函数吧),也没有去自动调用TraFromWindow(不是说在对话框关闭时调用吗)。都没有为我们进行调用。那我们是不是可以自己来调用。
文本输入控件中有这个属性方法Validator或者使用GetValidator()方法获取验证器。然后我们可以自己去调用这些验证函数(在构造函数中去使用,在窗口没显示之前...)
class MyDialog(wx.Dialog): def __init__(self,data): self.addTextCtrl = wx.TextCtrl(self,validator=DataValidator(data,0)) #添加验证 self.addTextCtrl.Validator.Validate(self.addTextCtrl) #就可以调用验证器中的验证函数去进行验证 self.addTextCtr2 = wx.TextCtrl(self,validator=DataValidator(data,1)) self.addTextCtr2.Validator.Validate(self.addTextCtr2) btn = wx.Button(self,-1,"确定") btn.Bind(wx.EVT_BUTTON,self.OnClick)
在对话框关闭前我们自行调用TraFromWindow方法....(太扯了点....应该如何去正确的使用??)
class MyDialog(wx.Dialog): def OnClick(self,event): self.addTextCtr1.Validator.TransferFromWindow() #调用用来销毁 self.addTextCtr2.Validator.TransferFromWindow() self.Close() #关闭前
上面的方法纯属参考。毕竟文档中说了这些预留方法会自动调用,而我们对validate和transferfromwindow都是自行调用的,不太妥当...
高级控件
表格控件
表格控件Grid是wxPython中最复杂的控件之一,表格控件通常用于浏览数据。这里介绍表格控件的创建,单元格的设置,表格对象的使用
表格控件的创建
创建步骤:
(1)需要先导入wx.grid包,其中wx.grid包中提供了创建和控制表格的类和方法。
import wx.grid
(2)实例化表格类
grid = wx.grid.Grid(self)
(3)创建表格控件,numRows是行数,numCols是列数
CreateGrid(self, numRows, numCols, selmode=None)
(4)设置行标题和列标题
grid.SetRowLabelValue(row,rowData) #设置行标题 grid.SetColLabelValue(row,colData) #设置列标题
(5)设置单元格的值
grid.SetCellValue(row,col,value)
PyGridTable创建表格
可以定制更加复杂的表格。通过继承PyGridTableBase类可以实现对表格控件更加复杂的设置。其中继承PyGridTableBase类必须实现GetNumberRows(),GetNumberCols(),GetValue()和SetValue()这些方法。表格会根据这些方法中(Get开头的方法中设置的返回值)的返回值来默认设置值
都有说:Must be overridden
注意要想设置属性,先要加上GetAttr()
注意:table表格是基于表格控件的
gd = grid.Grid(self) table = OddEvenTable() gd.SetTable(table,True)
注意:设置属性的时候需要对属性加一个引用计数
def GetAttr(self, row, col, kind): #对奇偶行进行过滤 attr = [self.evenAttr,self.oddAttr][row % 2] #对这行每个单元格获取其行,符合标准,设置相应属性 attr.IncRef() #注意这里需要增加引用计数 print(sys.getrefcount(attr)) #始终是4 return attr
在我们加上引用计数时,每一次获取都是4(self.evenAttr或self.oddAttr是一次,attr是一个,IncRef增加一次,作为参数传入sys.getrefcount中产生一次,一个四次)
但是我们每次产生一个表格都会对self.evenAttr或者self.oddAttr进行一次计数增加,那么为何依旧是4。
因为在对一个单元格设置属性后,会认为这个值不会再拿去给其他表格使用。所以会在设置属性后减少一个引用计数,而且GetAttr执行完后attr销毁,sys,getrefcount结束都会减少一个计数,所以若是我们不进行引用计数增加的话,会在执行一个GetAtrr(){注:这里有两个属性,会执行两次},该调用的属性会由于引用计数为0,被销毁。导致表格无法生成。
引用计数可以看这里
表格控件的单元格
表格控件的单元格可以根据不同的数据需求进行设置。例如:可以把 某个单元格设置为下拉框,复选框,数字选择等控件。wx.grid包提供了一系列的Editor编辑器,这些编辑器都可以在单元格中实现上面需要的控件。除外单元格还可以对数据的字体,颜色,格式进行设置。
处理单元格的事件
注意事件是绑定在整个表格,而不是某个单元格
EVT_GRID_EDITOR_SHOWN:是我们进入这个单元格,为可编辑时:就是show
EVT_GRID_EDITOR_HIDDEN:退出编辑状态就是hide
EVT_GRID_EDITOR_CREATED:当某种类型的单元格编辑器被第一次调用时,会触发该事件
高级列表控件
ListCtrl是出具显示的另一种方式。可以提供详细列表,图标列表等显示方式。ListCtrl控件的列对齐,排序等富足功能可以通过继承wx.lib.mixins.listctrl包的mixin类实现
1.列表控件的创建
ListCtrl控件常用列表方式显示信息,可以进行排序,地址列表的显示样式。创建方式
1直接实例化wx.ListCtrl类,然后关联列表数据。
2自定义列表控件类,继承wx.ListCtrl或wx.lib.mixins.listctrl包中的mixin类。这些mixin类实现了一些辅助性的功能。例如对齐,排序
注意:
for key,values in items: # 插入一个item,参数1为在什么地方插入,参数二为这个item的文本内容,刚开始item默认仅有一列 index = self.InsertItem(0,values[0]) #插入一行 for i in range(len(values)): #为这一行的三列设置值 self.SetItem(index,i,values[i])
InseItem是插入一行,默认自己包含一列
参数1:是插入的行的索引,这里我们设置了为0,那么意味着每次插入一条数据,都会插入在第一条,形成了倒序排列(一般我们会将这个参数设置为一个大数,因为显示的时候会去找到第一条数据然后向下显示《会将空白行跳过》)
参数2:就是我们的为默认的那一列设置的默认值(可以不用设置,因为在后面,我们进行了替换)
列表控件的排序
先奉上一篇文章https://blog.csdn.net/jjjcainiao/article/details/32731937
其中对ListCtrl列表排序的总结不错。但是使用的是python2版本。我这里使用python3
注意点:
1.数据的索引必须以0开始:
data = {0:("1","list2ctrl","列2表控件"), 1:("2","listctrl","列表控件"), 2:("3","grid","表格控件"), 3:("4","tree","树控件"), 4:("5","timer","定时器控件") }
因为他会调用函数去操作数据。且从0开始去索引,你的数据中没有索引0,那么会报错
item1 = self.itemDataMap[key1][col] KeyError: 0
2.需要实现几个特定方法,当我们使用ColumnSorterMixin时,必须去实现
""" A mixin class that handles sorting of a wx.ListCtrl in REPORT mode when the column header is clicked on. There are a few requirments needed in order for this to work genericly: 1. The combined class must have a GetListCtrl method that returns the wx.ListCtrl to be sorted, and the list control must exist at the time the wx.ColumnSorterMixin.__init__ method is called because it uses GetListCtrl. 2. Items in the list control must have a unique data value set with list.SetItemData. 3. The combined class must have an attribute named itemDataMap that is a dictionary mapping the data values to a sequence of objects representing the values in each column. These values are compared in the column sorter to determine sort order. Interesting methods to override are GetColumnSorter, GetSecondarySortValues, and GetSortImages. See below for details. """
(1)必须实现GetListCtrl用于返回创建的列表,因为在进行排序的时候没需要用到列表,而列表的获取是调用GetListCtrl获取
因为MyFrame类继承了ColumnSorterMixin类,所以必须由他来实现这个方法。此外也是需要他去实现(3)
def GetListCtrl(self): return self.listctrl
(2)这个让我找了半天...先说3
(3)itemDataMap,对其赋值(赋予我们添加在列表中的值,带上所以,方便映射).注意itemDataMap和ListCtrl控件的创建都要在ColunSorterMixin构造函数执行之前完成,不然会报错
补充(2):他说我们需要去在列表控件类中使用SetItemData去设置一个唯一的值为每一行
那么查看定义:
def SetItemData(self, item, data): item就是我们每一行的唯一索引index,在插入行的时候获取
data就是我们需要设置的数据(映射对于item)
代码:
for key,values in items: # 插入一个item,参数1为在什么地方插入,参数二为这个item的文本内容,刚开始item默认仅有一列 index = self.InsertItem(111111,values[0]) #插入一行,获取索引 for i in range(len(values[1:])): #为这一行的三列设置值 self.SetItem(index,i+1,values[i+1]) self.SetItemData(index, index)
SetItemData中的第二个参数data不是我们所定义的数据,而是与数据(itemDataMap)之间的映射。
而这里itemDataMap我们设置的是data全局变量。他和我们这里设置给每行的数据是对应的。而我们这里的index也是从0开始递增的(默认跳过空行,虽然我们在插入的时候随便设置了一个大数,但是由于前面为空,所以还是从0开始)。所以和数据的索引对上号了。我们就可以将两个映射都写为index即可。
注意一点:我们设置的初始排序方法,不能为倒序。若是我们设置的初始排序方式为倒序,那么在插入每一行时。都会出现:插入的数据返回的索引都是0(因为插入数据行都会向前出入,我们无法获取真正的索引,无法与数据建立映射)
带位图的列表控件
1.使用ImageList对象存储图标资源wx.Bitmap对象
2.创建ListCtrl控件,并始终style为wx.LC_ICON
3.调用AssignImageList()方法关联ImageList对象
4.调用InsertImageStringItem()方法出入列表项,并未图标设置说明字符串
树形控件
树形控件的创建
树形控件采用分层的方式显示复杂数据,数据呈现父子结构的关系。(分级)
创建步骤:
(1)创建TreeCtrl类的实例。下面创建了树形对象tree
self.tree = wx.TreeCtrl(self) #创建树形控件
(2)调用TreeCtrl类的AddRoot方法添加根节点。并获取该节点
root = self.tree.AddRoot("root") #设置根
(3)调用自定义函数去递归添加子节点,调用AppendItem(parent,node)为父节点,添加上子节点。
def AddTreeNodes(self,parent,tree): #parent是父节点,tree是该父节点下的子节点数据(需要添加上的) for node in tree: if type(node) == str: self.tree.AppendItem(parent,node) else: item = self.tree.AppendItem(parent,node[0]) self.AddTreeNodes(item,node[1])
树形控件的事件
树形控件的事件主要有节点的选择时间,展开时间,合并时间等。当树形控件创建完成后,直接为他绑定相关的事件类型,然后实现相应的处理函数即可。
树表控件
树表控件:结合了上面的树形控件和列表控件的风格。既存在父子关系,有可以按照列来显示数据。具备两者的属性和方法。
步骤:
(1)wx.gizmos包中的TreeListCtrl类可以创建树表控件,所以需要先进行导入
import wx.gizmos as gizmos
(2)创建TreeListCtrl类的实例
self.tree = gizmos.TreeListCtrl(self,-1,style=wx.TR_DEFAULT_STYLE|wx.TR_FULL_ROW_HIGHLIGHT)
(3)可以调用SetImageList()方法设置树表控件中节点所会需要用到的图标。
self.tree.SetImageList(self.il) #il是已经设置的ImageList对象
(4)调用AddColumn()方法可以添加列,并设置列名
self.tree.AddColumn("第1列")
(5)调用AddRoot()方法添加树表的根节点。即在第一行第一列中添加根节点
self.root = self.tree.AddRoot("root") #节点名为root,默认放在第一列,返回该行的标志
(6)同样可以为这第一行的其他列设置值(SetItemText中的第一个参数是该行标志)
self.tree.SetItemText(self.root,"随便设置",1) #设置第一行第二列的文本 self.tree.SetItemText(self.root,"与根同列",2) #设置第一行第三列的文本
(7)调用AppendItem()方法在父节点下面在添加子节点。下面在root根节点下面添加子节点
child = self.tree.AppendItem(self.root,str(x)) #添加一行
(8)调用SetItemText()方法可以对该子节点的列的内容进行设置
self.tree.SetItemText(child,str(x),0) #按照索引设置每一列的数据
(9)可以调用SetItemImage()方法设置树节点的展开,合并图标
#按照索引设置对应的图标 self.tree.SetItemImage(child,0,which = wx.TreeItemIcon_Normal) #正常的时候显示的图标的索引值 self.tree.SetItemImage(child,1,which = wx.TreeItemIcon_Expanded) #表展开的时候所显示的图标的索引值
实例代码:
定时器控件
定时器控件时非可视化的控件,用于定时器查询的运行。使用wx.PyTimer,wx.Timer和wx.CallLater类创建定时器控件。定时器类提供了3个主要的方法。Start(mil)方法用于启动定时器,参数mil表示毫秒。Stop()方法用于通知定时器。Restart()方法用于重启定时器,进行新一轮的处理。wx.CallLater类可以向处理程序传递参数,并返回处理程序的计算结果。
注意:
1.使用wx.PyTimer定时器会一直进行循环使用start,当然也有其他方法StartOnce只执行一次。Stop去停止定时器。
2.但是我们使用带有参数的定时器时:
self.timer2 = wx.CallLater(1000,self.OnCallTimer,1,2,3) #带参数的定时器
默认只会执行一次该定时器,要想继续执行,我们需要在设置的回调函数中在进行重启定时器
self.timer2.Restart(1000,total,total+1,total+2) #重启定时器,不需要传入函数名,只需要传入参数
执行一次的原因(看源码):
class CallLater(object): __instances = {} #存放该实例 def __init__(self, millis, callableObj, *args, **kwargs): assert callable(callableObj), #存放回调函数对象 self.millis = millis self.callable = callableObj self.SetArgs(*args, **kwargs) #设置参数 self.runCount = 0 self.running = False self.hasRun = False self.result = None self.timer = None self.Start() #定时器开始执行
查看执行函数:
def Start(self, millis=None, *args, **kwargs): self.hasRun = False if millis is not None: self.millis = millis if args or kwargs: self.SetArgs(*args, **kwargs) self.Stop() #会在执行定时器后进行关闭 CallLater.__instances[self] = "value irrelevant" # Maintain a reference to avoid GC self.timer = wx.PyTimer(self.Notify) self.timer.Start(self.millis, wx.TIMER_ONE_SHOT) self.running = True
停止并销毁该定时器 def Stop(self): """ Stop and destroy the timer. """ if self in CallLater.__instances: del CallLater.__instances[self] if self.timer is not None: self.timer.Stop() self.timer = None
这是就想到:在构造函数中就停止了定时器,那么还执不执行定时器了呀?这不是扯淡吗?
接着向下看:
def Start(self, millis=None, *args, **kwargs): self.hasRun = False if millis is not None: self.millis = millis if args or kwargs: self.SetArgs(*args, **kwargs) self.Stop() #会在执行定时器后进行关闭 CallLater.__instances[self] = "value irrelevant" # Maintain a reference to avoid GC self.timer = wx.PyTimer(self.Notify) self.timer.Start(self.millis, wx.TIMER_ONE_SHOT) self.running = True
再停止了自己后,就去调用PyTimer定时器了,我去:原来这个类的作用只是对PyTimer的扩充,使得函数可以带上参数了。那么继续向下看如何去执行这个带参数的函数:
self.timer = wx.PyTimer(self.Notify) #PyTimer函数类默认出入的是一个函数名,
self.timer.Start(self.millis, wx.TIMER_ONE_SHOT) #而且默认执行一次就嗝屁了
#这里的函数名Self.Notify显然是CallLater中的方法,继续去看看
def Notify(self): """ The timer has expired so call the callable. """ if self.callable and getattr(self.callable, 'im_self', True): self.runCount += 1 self.running = False self.result = self.callable(*self.args, **self.kwargs) #这里就调用了我们的传参函数 self.hasRun = True if not self.running: # if it wasn't restarted, then cleanup wx.CallAfter(self.Stop)
大致思路是使用中间类CallLater去获取我们的回调函数名和参数。将函数名和参数存放在自己的成员属性中,然后调用PyTimer类去执行定时器,传入的执行函数名是中间类CallLater定义的一个无参函数,但是在这个方法中执行了成员属性中的回调函数,并向其中传入了参数。
WxPython库中的高级功能
WxPython下得HTML显示
对于一些复杂多样的格式,我们前面使用的样式,有点.....。现在我们可以使用wx.html模块来处理HTML格式的文档。更加方便
1.HTML的显示
需要引入wx.html模块。使用HtmlWindow类生成一个HTML窗口。可以设置次窗口的内容。但是至少简单内容。style等样式css样式无法使用。
WxPython中的剪切板对象
剪切板是在各个应用程序中交换数据的一种方式。一个应用程序可以将数据保存在剪切板中,另一个应用程序可以从剪切板中获取数据、保存的数据可以是文本,图像,或者是其他文件对象。
由于操作系统中的剪切板是全局性的,即对不同的应用程序都是相同的,所以在wxpython程序中剪切板对象是一个全局对象。在操作剪切板之前,需要通过调用Open方法打开次对象,只有此方法返回了True之后,才能够继续使用剪切板。这是因为剪切板是一个全局对象。当其正在被另一个应用程序操作时,我们对剪切板的使用可能会失效。返回false。所以需要对返回值进行检测。也可以使用IsOpened方法来判断。记得在操作结束后调用Close方法将其关闭。阻止占用
1.处理剪切板对象
全部到此结束,若有再会补充,如果想了解进程,线程以及网络等编程在图形界面的编程,可以去试试Qt