主要使用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()
""" Every wx application must have a single ``wx.App`` instance, and all creation of UI objects should be delayed until after the ``wx.App`` object has been created in order to ensure that the gui platform and wxWidgets have been fully initialized. """
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实现。
import wx class MyFrame(wx.Frame): #创建自定义Frame def __init__(self,parent): #def __init__( # self, parent=None, id=None, title=None, pos=None, # size=None, style=None, name=None) wx.Frame.__init__(self,parent,id=-1,title="Hello World",size=(300,300)) #设置窗体 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() #将窗口放在桌面环境的中间 class MyApp(wx.App): def OnInit(self): print("开始进入事件循环") self.frame = MyFrame(None) self.frame.Show(True) return True #需要返回一个布尔型,只有初始返回成功,程序才会继续执行 def OnExit(self): print("事件循环结束") import time time.sleep(2) return 0 #返回状态码 app = MyApp() app.MainLoop()
其中OnInit方法中是必须的,其将会在事件循环处理之前被系统调用。同样的在事件结束之后调用的方法也存在(OnExit)
两个方法是相对的。例如:开启数据库和断开数据库
窗口组成:
前面的代码中窗口和应用程序似乎没有关联,为什么?
在应用程序对象中,可以通过SetTopWindow方法来设置根窗口。若是没有设置,是因为应用程序会选择窗口列表中的第一个作为根窗口。上面的程序也只有一个窗口,所以没有必要去显示设置。同样使用SetTopWindow也可以改变根窗口
控件的部分构造参数:
参数 | 说明 |
parent | 父窗口(为None代表为顶级窗口),注意设置父窗口后,在父窗口关闭时,子窗口也会消失 |
id | 上面说过了 |
title | 标题 |
pos | 相对于屏幕的位置默认(-1,-1),自行选择。我们在上面使用center也可以改变窗口在屏幕中的位置 |
size | 大小默认(-1,-1)窗口系统自行选择 |
style | 窗口类型... |
name | 窗口的内在名字 |
style风格:
事件驱动:
EVT_SIZE = wx.PyEventBinder( wxEVT_SIZE ) EVT_SIZING = wx.PyEventBinder( wxEVT_SIZING ) EVT_MOVE = wx.PyEventBinder( wxEVT_MOVE ) EVT_MOVING = wx.PyEventBinder( wxEVT_MOVING ) EVT_MOVE_START = wx.PyEventBinder( wxEVT_MOVE_START ) EVT_MOVE_END = wx.PyEventBinder( wxEVT_MOVE_END ) EVT_CLOSE = wx.PyEventBinder( wxEVT_CLOSE_WINDOW ) EVT_END_SESSION = wx.PyEventBinder( wxEVT_END_SESSION ) EVT_QUERY_END_SESSION = wx.PyEventBinder( wxEVT_QUERY_END_SESSION ) EVT_PAINT = wx.PyEventBinder( wxEVT_PAINT ) EVT_NC_PAINT = wx.PyEventBinder( wxEVT_NC_PAINT ) EVT_ERASE_BACKGROUND = wx.PyEventBinder( wxEVT_ERASE_BACKGROUND ) EVT_CHAR = wx.PyEventBinder( wxEVT_CHAR ) EVT_KEY_DOWN = wx.PyEventBinder( wxEVT_KEY_DOWN ) EVT_KEY_UP = wx.PyEventBinder( wxEVT_KEY_UP ) EVT_HOTKEY = wx.PyEventBinder( wxEVT_HOTKEY, 1) EVT_CHAR_HOOK = wx.PyEventBinder( wxEVT_CHAR_HOOK ) EVT_MENU_OPEN = wx.PyEventBinder( wxEVT_MENU_OPEN ) EVT_MENU_CLOSE = wx.PyEventBinder( wxEVT_MENU_CLOSE ) EVT_MENU_HIGHLIGHT = wx.PyEventBinder( wxEVT_MENU_HIGHLIGHT, 1) EVT_MENU_HIGHLIGHT_ALL = wx.PyEventBinder( wxEVT_MENU_HIGHLIGHT ) EVT_SET_FOCUS = wx.PyEventBinder( wxEVT_SET_FOCUS ) EVT_KILL_FOCUS = wx.PyEventBinder( wxEVT_KILL_FOCUS ) EVT_CHILD_FOCUS = wx.PyEventBinder( wxEVT_CHILD_FOCUS ) EVT_ACTIVATE = wx.PyEventBinder( wxEVT_ACTIVATE ) EVT_ACTIVATE_APP = wx.PyEventBinder( wxEVT_ACTIVATE_APP ) EVT_HIBERNATE = wx.PyEventBinder( wxEVT_HIBERNATE ) EVT_DROP_FILES = wx.PyEventBinder( wxEVT_DROP_FILES ) EVT_INIT_DIALOG = wx.PyEventBinder( wxEVT_INIT_DIALOG ) EVT_SYS_COLOUR_CHANGED = wx.PyEventBinder( wxEVT_SYS_COLOUR_CHANGED ) EVT_DISPLAY_CHANGED = wx.PyEventBinder( wxEVT_DISPLAY_CHANGED ) EVT_SHOW = wx.PyEventBinder( wxEVT_SHOW ) EVT_MAXIMIZE = wx.PyEventBinder( wxEVT_MAXIMIZE ) EVT_ICONIZE = wx.PyEventBinder( wxEVT_ICONIZE ) EVT_NAVIGATION_KEY = wx.PyEventBinder( wxEVT_NAVIGATION_KEY ) EVT_PALETTE_CHANGED = wx.PyEventBinder( wxEVT_PALETTE_CHANGED ) EVT_QUERY_NEW_PALETTE = wx.PyEventBinder( wxEVT_QUERY_NEW_PALETTE ) EVT_WINDOW_CREATE = wx.PyEventBinder( wxEVT_CREATE ) EVT_WINDOW_DESTROY = wx.PyEventBinder( wxEVT_DESTROY ) EVT_SET_CURSOR = wx.PyEventBinder( wxEVT_SET_CURSOR ) EVT_MOUSE_CAPTURE_CHANGED = wx.PyEventBinder( wxEVT_MOUSE_CAPTURE_CHANGED ) EVT_MOUSE_CAPTURE_LOST = wx.PyEventBinder( wxEVT_MOUSE_CAPTURE_LOST ) EVT_LEFT_DOWN = wx.PyEventBinder( wxEVT_LEFT_DOWN ) EVT_LEFT_UP = wx.PyEventBinder( wxEVT_LEFT_UP ) EVT_MIDDLE_DOWN = wx.PyEventBinder( wxEVT_MIDDLE_DOWN ) EVT_MIDDLE_UP = wx.PyEventBinder( wxEVT_MIDDLE_UP ) EVT_RIGHT_DOWN = wx.PyEventBinder( wxEVT_RIGHT_DOWN ) EVT_RIGHT_UP = wx.PyEventBinder( wxEVT_RIGHT_UP ) EVT_MOTION = wx.PyEventBinder( wxEVT_MOTION ) EVT_LEFT_DCLICK = wx.PyEventBinder( wxEVT_LEFT_DCLICK ) EVT_MIDDLE_DCLICK = wx.PyEventBinder( wxEVT_MIDDLE_DCLICK ) EVT_RIGHT_DCLICK = wx.PyEventBinder( wxEVT_RIGHT_DCLICK ) EVT_LEAVE_WINDOW = wx.PyEventBinder( wxEVT_LEAVE_WINDOW ) EVT_ENTER_WINDOW = wx.PyEventBinder( wxEVT_ENTER_WINDOW ) EVT_MOUSEWHEEL = wx.PyEventBinder( wxEVT_MOUSEWHEEL ) EVT_MOUSE_AUX1_DOWN = wx.PyEventBinder( wxEVT_AUX1_DOWN ) EVT_MOUSE_AUX1_UP = wx.PyEventBinder( wxEVT_AUX1_UP ) EVT_MOUSE_AUX1_DCLICK = wx.PyEventBinder( wxEVT_AUX1_DCLICK ) EVT_MOUSE_AUX2_DOWN = wx.PyEventBinder( wxEVT_AUX2_DOWN ) EVT_MOUSE_AUX2_UP = wx.PyEventBinder( wxEVT_AUX2_UP ) EVT_MOUSE_AUX2_DCLICK = wx.PyEventBinder( wxEVT_AUX2_DCLICK ) EVT_MOUSE_EVENTS = wx.PyEventBinder([ wxEVT_LEFT_DOWN, wxEVT_LEFT_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_UP, wxEVT_MOTION, wxEVT_LEFT_DCLICK, wxEVT_MIDDLE_DCLICK, wxEVT_RIGHT_DCLICK, wxEVT_ENTER_WINDOW, wxEVT_LEAVE_WINDOW, wxEVT_MOUSEWHEEL, wxEVT_AUX1_DOWN, wxEVT_AUX1_UP, wxEVT_AUX1_DCLICK, wxEVT_AUX2_DOWN, wxEVT_AUX2_UP, wxEVT_AUX2_DCLICK, ]) # Scrolling from wxWindow (sent to wxScrolledWindow) EVT_SCROLLWIN = wx.PyEventBinder([ wxEVT_SCROLLWIN_TOP, wxEVT_SCROLLWIN_BOTTOM, wxEVT_SCROLLWIN_LINEUP, wxEVT_SCROLLWIN_LINEDOWN, wxEVT_SCROLLWIN_PAGEUP, wxEVT_SCROLLWIN_PAGEDOWN, wxEVT_SCROLLWIN_THUMBTRACK, wxEVT_SCROLLWIN_THUMBRELEASE, ]) EVT_SCROLLWIN_TOP = wx.PyEventBinder( wxEVT_SCROLLWIN_TOP ) EVT_SCROLLWIN_BOTTOM = wx.PyEventBinder( wxEVT_SCROLLWIN_BOTTOM ) EVT_SCROLLWIN_LINEUP = wx.PyEventBinder( wxEVT_SCROLLWIN_LINEUP ) EVT_SCROLLWIN_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLLWIN_LINEDOWN ) EVT_SCROLLWIN_PAGEUP = wx.PyEventBinder( wxEVT_SCROLLWIN_PAGEUP ) EVT_SCROLLWIN_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLLWIN_PAGEDOWN ) EVT_SCROLLWIN_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLLWIN_THUMBTRACK ) EVT_SCROLLWIN_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLLWIN_THUMBRELEASE ) # Scrolling from wx.Slider and wx.ScrollBar EVT_SCROLL = wx.PyEventBinder([ wxEVT_SCROLL_TOP, wxEVT_SCROLL_BOTTOM, wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN, wxEVT_SCROLL_PAGEUP, wxEVT_SCROLL_PAGEDOWN, wxEVT_SCROLL_THUMBTRACK, wxEVT_SCROLL_THUMBRELEASE, wxEVT_SCROLL_CHANGED, ]) EVT_SCROLL_TOP = wx.PyEventBinder( wxEVT_SCROLL_TOP ) EVT_SCROLL_BOTTOM = wx.PyEventBinder( wxEVT_SCROLL_BOTTOM ) EVT_SCROLL_LINEUP = wx.PyEventBinder( wxEVT_SCROLL_LINEUP ) EVT_SCROLL_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLL_LINEDOWN ) EVT_SCROLL_PAGEUP = wx.PyEventBinder( wxEVT_SCROLL_PAGEUP ) EVT_SCROLL_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLL_PAGEDOWN ) EVT_SCROLL_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLL_THUMBTRACK ) EVT_SCROLL_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLL_THUMBRELEASE ) EVT_SCROLL_CHANGED = wx.PyEventBinder( wxEVT_SCROLL_CHANGED ) EVT_SCROLL_ENDSCROLL = EVT_SCROLL_CHANGED # Scrolling from wx.Slider and wx.ScrollBar, with an id EVT_COMMAND_SCROLL = wx.PyEventBinder([ wxEVT_SCROLL_TOP, wxEVT_SCROLL_BOTTOM, wxEVT_SCROLL_LINEUP, wxEVT_SCROLL_LINEDOWN, wxEVT_SCROLL_PAGEUP, wxEVT_SCROLL_PAGEDOWN, wxEVT_SCROLL_THUMBTRACK, wxEVT_SCROLL_THUMBRELEASE, wxEVT_SCROLL_CHANGED, ], 1) EVT_COMMAND_SCROLL_TOP = wx.PyEventBinder( wxEVT_SCROLL_TOP, 1) EVT_COMMAND_SCROLL_BOTTOM = wx.PyEventBinder( wxEVT_SCROLL_BOTTOM, 1) EVT_COMMAND_SCROLL_LINEUP = wx.PyEventBinder( wxEVT_SCROLL_LINEUP, 1) EVT_COMMAND_SCROLL_LINEDOWN = wx.PyEventBinder( wxEVT_SCROLL_LINEDOWN, 1) EVT_COMMAND_SCROLL_PAGEUP = wx.PyEventBinder( wxEVT_SCROLL_PAGEUP, 1) EVT_COMMAND_SCROLL_PAGEDOWN = wx.PyEventBinder( wxEVT_SCROLL_PAGEDOWN, 1) EVT_COMMAND_SCROLL_THUMBTRACK = wx.PyEventBinder( wxEVT_SCROLL_THUMBTRACK, 1) EVT_COMMAND_SCROLL_THUMBRELEASE = wx.PyEventBinder( wxEVT_SCROLL_THUMBRELEASE, 1) EVT_COMMAND_SCROLL_CHANGED = wx.PyEventBinder( wxEVT_SCROLL_CHANGED, 1) EVT_COMMAND_SCROLL_ENDSCROLL = EVT_COMMAND_SCROLL_CHANGED 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_SCROLLBAR = wx.PyEventBinder( wxEVT_SCROLLBAR, 1) EVT_VLBOX = wx.PyEventBinder( wxEVT_VLBOX, 1) EVT_COMBOBOX = wx.PyEventBinder( wxEVT_COMBOBOX, 1) EVT_TOOL = wx.PyEventBinder( wxEVT_TOOL, 1) EVT_TOOL_RANGE = wx.PyEventBinder( wxEVT_TOOL, 2) EVT_TOOL_RCLICKED = wx.PyEventBinder( wxEVT_TOOL_RCLICKED, 1) EVT_TOOL_RCLICKED_RANGE = wx.PyEventBinder( wxEVT_TOOL_RCLICKED, 2) EVT_TOOL_ENTER = wx.PyEventBinder( wxEVT_TOOL_ENTER, 1) EVT_TOOL_DROPDOWN = wx.PyEventBinder( wxEVT_TOOL_DROPDOWN, 1) EVT_CHECKLISTBOX = wx.PyEventBinder( wxEVT_CHECKLISTBOX, 1) EVT_COMBOBOX_DROPDOWN = wx.PyEventBinder( wxEVT_COMBOBOX_DROPDOWN , 1) EVT_COMBOBOX_CLOSEUP = wx.PyEventBinder( wxEVT_COMBOBOX_CLOSEUP , 1) EVT_COMMAND_LEFT_CLICK = wx.PyEventBinder( wxEVT_COMMAND_LEFT_CLICK, 1) EVT_COMMAND_LEFT_DCLICK = wx.PyEventBinder( wxEVT_COMMAND_LEFT_DCLICK, 1) EVT_COMMAND_RIGHT_CLICK = wx.PyEventBinder( wxEVT_COMMAND_RIGHT_CLICK, 1) EVT_COMMAND_RIGHT_DCLICK = wx.PyEventBinder( wxEVT_COMMAND_RIGHT_DCLICK, 1) EVT_COMMAND_SET_FOCUS = wx.PyEventBinder( wxEVT_COMMAND_SET_FOCUS, 1) EVT_COMMAND_KILL_FOCUS = wx.PyEventBinder( wxEVT_COMMAND_KILL_FOCUS, 1) EVT_COMMAND_ENTER = wx.PyEventBinder( wxEVT_COMMAND_ENTER, 1) EVT_HELP = wx.PyEventBinder( wxEVT_HELP, 1) EVT_HELP_RANGE = wx.PyEventBinder( wxEVT_HELP, 2) EVT_DETAILED_HELP = wx.PyEventBinder( wxEVT_DETAILED_HELP, 1) EVT_DETAILED_HELP_RANGE = wx.PyEventBinder( wxEVT_DETAILED_HELP, 2) EVT_IDLE = wx.PyEventBinder( wxEVT_IDLE ) EVT_UPDATE_UI = wx.PyEventBinder( wxEVT_UPDATE_UI, 1) EVT_UPDATE_UI_RANGE = wx.PyEventBinder( wxEVT_UPDATE_UI, 2) EVT_CONTEXT_MENU = wx.PyEventBinder( wxEVT_CONTEXT_MENU ) EVT_THREAD = wx.PyEventBinder( wxEVT_THREAD ) EVT_WINDOW_MODAL_DIALOG_CLOSED = wx.PyEventBinder( wxEVT_WINDOW_MODAL_DIALOG_CLOSED ) EVT_JOY_BUTTON_DOWN = wx.PyEventBinder( wxEVT_JOY_BUTTON_DOWN ) EVT_JOY_BUTTON_UP = wx.PyEventBinder( wxEVT_JOY_BUTTON_UP ) EVT_JOY_MOVE = wx.PyEventBinder( wxEVT_JOY_MOVE ) EVT_JOY_ZMOVE = wx.PyEventBinder( wxEVT_JOY_ZMOVE ) EVT_JOYSTICK_EVENTS = wx.PyEventBinder([ wxEVT_JOY_BUTTON_DOWN, wxEVT_JOY_BUTTON_UP, wxEVT_JOY_MOVE, wxEVT_JOY_ZMOVE, ]) # deprecated wxEVT aliases wxEVT_COMMAND_BUTTON_CLICKED = wxEVT_BUTTON wxEVT_COMMAND_CHECKBOX_CLICKED = wxEVT_CHECKBOX wxEVT_COMMAND_CHOICE_SELECTED = wxEVT_CHOICE wxEVT_COMMAND_LISTBOX_SELECTED = wxEVT_LISTBOX wxEVT_COMMAND_LISTBOX_DOUBLECLICKED = wxEVT_LISTBOX_DCLICK wxEVT_COMMAND_CHECKLISTBOX_TOGGLED = wxEVT_CHECKLISTBOX wxEVT_COMMAND_MENU_SELECTED = wxEVT_MENU wxEVT_COMMAND_TOOL_CLICKED = wxEVT_TOOL wxEVT_COMMAND_SLIDER_UPDATED = wxEVT_SLIDER wxEVT_COMMAND_RADIOBOX_SELECTED = wxEVT_RADIOBOX wxEVT_COMMAND_RADIOBUTTON_SELECTED = wxEVT_RADIOBUTTON wxEVT_COMMAND_SCROLLBAR_UPDATED = wxEVT_SCROLLBAR wxEVT_COMMAND_VLBOX_SELECTED = wxEVT_VLBOX wxEVT_COMMAND_COMBOBOX_SELECTED = wxEVT_COMBOBOX wxEVT_COMMAND_TOOL_RCLICKED = wxEVT_TOOL_RCLICKED wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED = wxEVT_TOOL_DROPDOWN wxEVT_COMMAND_TOOL_ENTER = wxEVT_TOOL_ENTER wxEVT_COMMAND_COMBOBOX_DROPDOWN = wxEVT_COMBOBOX_DROPDOWN wxEVT_COMMAND_COMBOBOX_CLOSEUP = wxEVT_COMBOBOX_CLOSEUP
一个事件是wx.Event类或其子类的一个实例。
事件的绑定和处理:通过使用wx.EvtHandler类中的Bind方法可以将绑定事件的类型,事件对象和事件处理函数关联起来
def Bind(self, event, handler, source=None, id=-1, id2=-1): # reliably restored by inspect """ Bind an event to an event handler. :param event: One of the ``EVT_*`` event binder objects that specifies the type of event to bind. :param handler: A callable object to be invoked when the event is delivered to self. Pass ``None`` to disconnect an event handler. :param source: Sometimes the event originates from a different window than self, but you still want to catch it in self. (For example, a button event delivered to a frame.) By passing the source of the event, the event handling system is able to differentiate between the same event type from different controls. :param id: Used to spcify the event source by ID instead of instance. :param id2: Used when it is desirable to bind a handler to a range of IDs, such as with EVT_MENU_RANGE. """ pass
import wx class MyFrame(wx.Frame): #创建自定义Frame def __init__(self,parent): #def __init__( # self, parent=None, id=None, title=None, pos=None, # size=None, style=None, name=None) wx.Frame.__init__(self,parent,id=-1,title="Hello World",size=(300,300),pos=(0,0)) #设置窗体 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) btn = wx.Button(panel,-1,"Quit") sizer.Add(btn,0,wx.TOP|wx.LEFT,100) self.Bind(wx.EVT_BUTTON,self.OnClick,btn) self.Center() #将窗口放在桌面环境的中间 def OnClick(self,event): #注意事件处理函数需要加上事件对象 print("我被点击了") self.Close(True) #强制退出force=True class MyApp(wx.App): def OnInit(self): print("开始进入事件循环") self.frame = MyFrame(None) self.frame.Show(True) self.frame.GetId() return True #需要返回一个布尔型,只有初始返回成功,程序才会继续执行 def OnExit(self): print("事件循环结束") import time time.sleep(2) return 0 #返回状态码 app = MyApp() app.MainLoop()
import wx class MyFrame(wx.Frame): #创建自定义Frame def __init__(self,parent): #def __init__( # self, parent=None, id=None, title=None, pos=None, # size=None, style=None, name=None) wx.Frame.__init__(self,parent,id=-1,title="Hello World",size=(300,300),pos=(0,0)) #设置窗体 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.btn = wx.Button(panel,-1,"Quit") sizer.Add(self.btn,0,wx.TOP|wx.LEFT,100) # self.Bind(wx.EVT_BUTTON,self.OnClick,self.btn) # self.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow,self.btn) self.btn.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow) #绑定鼠标进入事件 self.btn.Bind(wx.EVT_ENTER_WINDOW,self.OnEnterWindow2) #绑定鼠标进入事件 self.btn.Bind(wx.EVT_LEAVE_WINDOW,self.OnLeaveWindow) #绑定鼠标离开事件 self.Center() #将窗口放在桌面环境的中间 def OnEnterWindow(self,event): print("鼠标进入") self.btn.SetLabel("鼠标进入") # event.Skip() def OnEnterWindow2(self,event): print("鼠标进入2") self.btn.SetLabel("鼠标进入2") event.Skip() def OnLeaveWindow(self,event): print("鼠标离开") self.btn.SetLabel("鼠标离开") event.Skip() #一般窗口不见处理完一个事件完毕后,事件处理就结束了,如果要继续处理的话就可以使用Skip方法来改变默认的行为 def OnClick(self,event): #注意事件处理函数需要加上事件对象 print("我被点击了") self.Close(True) #强制退出force=True class MyApp(wx.App): def OnInit(self): print("开始进入事件循环") self.frame = MyFrame(None) self.frame.Show(True) self.frame.GetId() return True #需要返回一个布尔型,只有初始返回成功,程序才会继续执行 def OnExit(self): print("事件循环结束") import time time.sleep(2) return 0 #返回状态码 app = MyApp() app.MainLoop()
补充:
在上面我们使用两种方法为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()
工具栏:
class MyFrame(wx.Frame): def __init__(self,parent=None): wx.Frame.__init__(self,parent,-1,"简单工具栏",size=(200,200)) toolbar = self.CreateToolBar() toolbar.AddTool(wx.ID_EXIT,"退出",wx.Bitmap("1.gif")) toolbar.Realize() self.Bind(wx.EVT_TOOL,self.onExit,id=wx.ID_EXIT) self.Center() def onExit(self,event): self.Close(True) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
Realize() 方法需要被调用,以最终确定工具栏创建
状态栏:
self.CreateStatusBar() self.SetStatusText("状态栏信息")
基本组件
1.静态文本框
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame,self).__init__(parent,-1,"Text",size=(100,75)) panel = wx.Panel(self,-1) text = wx.StaticText(panel,-1,"Hello World",(10,10),(80,25),wx.ALIGN_CENTER) #创建静态文本框 设置风格居中对齐是相对于这个label大小区域 text.SetForegroundColour("blue") text.SetBackgroundColour("green") font = wx.Font(12,wx.DEFAULT,wx.ITALIC,wx.NORMAL,True) #ITALIC倾斜 text.SetFont(font) """ Font() Font(font) Font(fontInfo) Font(pointSize, family, style, weight, underline=False, faceName=EmptyString, encoding=FONTENCODING_DEFAULT) Font(pixelSize, family, style, weight, underline=False, faceName=EmptyString, encoding=FONTENCODING_DEFAULT) Font(nativeInfoString) Font(nativeInfo) """ app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
2.文本输入框
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"文本框",size=(300,150)) panel = wx.Panel(self,-1) Label1 = wx.StaticText(panel,-1,"姓名:",pos=(10,10)) #输入文本框 self.inputText = wx.TextCtrl(panel,-1,"",pos=(80,10),size=(150,-1)) self.inputText.SetInsertionPoint(0) #设置焦点位置 Label2 = wx.StaticText(panel,-1,"密码:",pos=(10,50)) #密码输入框 self.pwdText = wx.TextCtrl(panel,-1,"",pos=(80,50),size=(150,-1),style=wx.TE_PASSWORD|wx.TE_PROCESS_ENTER) #TE_PROCESS_ENTER当按下回车键会自动触发相关事件 self.Bind(wx.EVT_TEXT_ENTER,self.OnLostFocus,self.pwdText) def OnLostFocus(self,event): wx.MessageBox("%s %s"%(self.inputText.GetValue(),self.pwdText.GetValue())) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
相关参数:
常用方法:
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"文本框",size=(300,150)) panel = wx.Panel(self,-1) #创建多行文本框 multiText = wx.TextCtrl(panel,-1,"",pos=(10,10),size=(180,80),style=wx.TE_MULTILINE|wx.TE_CENTER) multiText.SetBackgroundColour("pink") multiText.SetFocus() #获取焦点(将焦点设置到当前控件中) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
3.按钮控件
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"文本框",size=(300,150)) panel = wx.Panel(self,-1) self.button = wx.Button(panel,-1,"确定",pos=(10,10)) self.Bind(wx.EVT_BUTTON,self.OnClick,self.button) self.button.SetDefault() #将按钮设置为默认按钮,不然会是选中状态,边框不同 self.inputText = wx.TextCtrl(panel,-1,"",pos=(100,10),size=(150,-1),style=wx.TE_READONLY) def OnClick(self,event): # self.inputText.SetValue("Hello World") self.inputText.Value = "Hello World2" app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"文本框",size=(300,150)) panel = wx.Panel(self,-1) # self.button = wx.Button(panel,-1,"确定",pos=(10,10)) bmp = wx.Image("1.jpg",wx.BITMAP_TYPE_JPEG).ConvertToBitmap() self.button = wx.BitmapButton(panel,-1,bmp,pos=(20,20),size=(120,60)) self.Bind(wx.EVT_BUTTON,self.OnClick,self.button) self.button.SetDefault() #将按钮设置为默认按钮,不然会是选中状态,边框不同 self.inputText = wx.TextCtrl(panel,-1,"",pos=(100,10),size=(150,-1),style=wx.TE_READONLY) def OnClick(self,event): # self.inputText.SetValue("Hello World") self.inputText.Value = "Hello World2" app = wx.App() frame = MyFrame() frame.Show() app.MainLoop() """ Image() Image(width, height, clear=True) Image(sz, clear=True) Image(name, type=BITMAP_TYPE_ANY, index=-1) Image(name, mimetype, index=-1) Image(stream, type=BITMAP_TYPE_ANY, index=-1) Image(stream, mimetype, index=-1) Image(width, height, data) Image(width, height, data, alpha) Image(size, data) Image(size, data, alpha) This class encapsulates a platform-independent image. """
补充:图片转换
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.单选框
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"单选框框",size=(300,150)) panel = wx.Panel(self,-1) radioRed = wx.RadioButton(panel,-1,"红",pos=(10,10)) radioGreen = wx.RadioButton(panel,-1,"绿",pos=(10,40)) radioBlue = wx.RadioButton(panel,-1,"蓝",pos=(10,70)) self.colors = {"红":wx.RED,"绿":wx.GREEN,"蓝":wx.BLUE} self.textColor = wx.TextCtrl(panel,-1,"",pos=(80,10)) for eachRadio in [radioRed,radioBlue,radioGreen]: #对每一个按钮都注册事件 eachRadio.Bind(wx.EVT_RADIOBUTTON,self.OnClick) def OnClick(self,event): #只有点击的对象才会触发该函数 radioSelected = event.GetEventObject() #返回被选中的按钮的对象 str = radioSelected.GetLabel() #获取当前对象的label值 self.textColor.SetBackgroundColour(self.colors[str]) self.textColor.SetFocus() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"单选框框",size=(300,150)) panel = wx.Panel(self,-1) colorList = ["红","绿","蓝","白","紫"] #生成单选框 self.group1 = wx.RadioBox(panel,-1,"颜色",(10,10),wx.DefaultSize,colorList,4,wx.RA_SPECIFY_COLS) #行显示 majorDimension是一个整数,根据后面的风格来显示:每行/列显示的个数 self.group2 = wx.RadioBox(panel,-1,"颜色",(180,10),wx.DefaultSize,colorList,3,wx.RA_SPECIFY_ROWS) #列显示 self.group1.Bind(wx.EVT_RADIOBOX,self.OnClick) self.group2.Bind(wx.EVT_RADIOBOX,self.OnClick) #def __init__(self, parent=None, id=None, label=None, pos=None, size=None, choices=[], majorDimension=0, style=None, validator=None, name=None): # real signature unknown; restored from __doc__ with multiple overloads def OnClick(self,event): #只有点击的对象才会触发该函数 groupSelected = event.GetEventObject() #返回被选中的按钮的对象 #self.group1.GetSelection() #获取的是索引 radioSelected = groupSelected.GetStringSelection() #获取的是label wx.MessageBox(radioSelected) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
5.多选框
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"单选框框",size=(300,150)) panel = wx.Panel(self,-1) wx.StaticText(panel,-1,"多选框",(10,10),(100,20)) self.cb1 = wx.CheckBox(panel,-1,"a",(10,30),(100,20)) self.cb2 = wx.CheckBox(panel,-1,"b",(10,50),(100,20)) self.cb3 = wx.CheckBox(panel,-1,"c",(10,70),(100,20)) self.cb4 = wx.CheckBox(panel,-1,"d",(10,90),(100,20)) self.cb5 = wx.CheckBox(panel,-1,"e",(10,110),(100,20)) self.allcb = wx.CheckBox(panel,-1,"全选",(10,130),(100,20)) self.selectFlag = True #能否进行全选 self.all = [] #存放所有的标签 self.button = wx.Button(panel,-1,"查看",(10,160),(100,20)) self.Bind(wx.EVT_BUTTON,self.OnClick,self.button) for chkbox in [self.cb1,self.cb2,self.cb3,self.cb4,self.cb5]: self.Bind(wx.EVT_CHECKBOX,self.OnSelectSingle,chkbox) self.Bind(wx.EVT_CHECKBOX,self.OnSelectAll,self.allcb) def OnSelectSingle(self,event): obj = event.GetEventObject() if obj.IsChecked(): self.all.append(obj.GetLabel()) else: self.all.remove(obj.GetLabel()) # self.selectFlag = False def OnSelectAll(self,event): self.all = [] for chkbox in [self.cb1,self.cb2,self.cb3,self.cb4,self.cb5]: chkbox.SetValue(self.selectFlag) if self.allcb.IsChecked(): for chkbox in [self.cb1, self.cb2, self.cb3, self.cb4, self.cb5]: self.all.append(chkbox.GetLabel()) chkbox.Disable() else: for chkbox in [self.cb1, self.cb2, self.cb3, self.cb4, self.cb5]: chkbox.Enable() self.selectFlag = not self.selectFlag def OnClick(self,event): #只有点击的对象才会触发该函数 str = " ".join(self.all) wx.MessageBox(str) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
colorList = ["红", "绿", "蓝", "白", "紫"] wx.CheckListBox(panel, -1, (10, 10), wx.DefaultSize, colorList)
6.列表控件
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"单选框框",size=(300,150)) panel = wx.Panel(self,-1) colorList = ["红", "绿", "蓝", "白", "紫"] #关联ListBox self.listBox = wx.ListBox(panel,-1,(10,10),(80,110),colorList,wx.LB_SINGLE) self.listBox.SetSelection(2) # 绑定单击事件 self.Bind(wx.EVT_LISTBOX, self.OnClick, self.listBox) #关联checklistBox复选框 self.checkListBox = wx.CheckListBox(panel,-1,(100,10),(80,110),colorList,wx.LB_SORT) #绑定多选框 self.Bind(wx.EVT_CHECKLISTBOX,self.OnSelect,self.checkListBox) def OnSelect(self,event): obj = event.GetEventObject() indexs = self.checkListBox.GetCheckedItems() #获取索引元组 labels = self.checkListBox.GetCheckedStrings() #获取选中的label元组 wx.MessageBox(" ".join(labels)) def OnClick(self,event): #只有点击的对象才会触发该函数 index = self.listBox.GetSelection() wx.MessageBox(self.listBox.GetString(index),"提示") app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
listbox样式:
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"test",size=(300,150)) panel = wx.Panel(self,-1) colorList = ["红", "绿", "蓝", "白", "紫"] self.choice = wx.Choice(panel,-1,(120,10),choices=colorList) #下拉列表控件 self.choice.SetSelection(1) #用于设置选中值(默认)
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"test",size=(300,150)) panel = wx.Panel(self,-1) colorList = ["红", "绿", "蓝", "白", "紫"] self.choice = wx.ComboBox(panel,-1,"绿",(120,10),wx.DefaultSize,colorList,wx.CB_DROPDOWN) #下拉列表控件
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布局管理器中即可
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"test",size=(300,150)) panel = wx.Panel(self,-1) sizer = wx.GridSizer(rows=3, cols=3, vgap=5, hgap=5) #vgap hgap代表水平距离和垂直距离 panel.SetSizer(sizer) colorList = ["红", "绿", "蓝", "白", "紫"] for color in colorList: btn = wx.Button(panel,-1,color,style=wx.DEFAULT) sizer.Add(btn,0,0) #将控件添加在下一可用网格插槽,默认也是0,可以不用写 第一个是比例值,第二个是flag风格 panel.Fit() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop() """ GridSizer(cols, vgap, hgap) GridSizer(cols, gap=Size(0,0)) GridSizer(rows, cols, vgap, hgap) GridSizer(rows, cols, gap) A grid sizer is a sizer which lays out its children in a two- dimensional table with all table fields having the same size, i.e. """
(3)Flex Grid Sizer布局
是对grid sizer的改进。可以用于存放大小可以改变的组件。
class FlexGridSizer(GridSizer) 继承于Grid Sizer
相对于Grid Sizer而言,他们的区别主要在于:
FlexGridSizer可以设置某一列或行的可变大小。主要在于下面的几个方法:
AddGrowableCol() 指定索引的列增长,如果额外的高度可用。
AddGrowRow() 指定的行定索引的增长,如果额外的宽度是可用的。
SetFlexibleDirection() 指定大小测定器的灵活性是否影响的行,列或两者。
其中FlexGridSizer的本身是固定不变的而Grid Sizer是随着窗口的变化,也会改变
panel = wx.Panel(self) hbox = wx.BoxSizer(wx.HORIZONTAL) fgs = wx.FlexGridSizer(3, 2, 10, 10) title = wx.StaticText(panel, label="Title") author = wx.StaticText(panel, label="Name of the Author") review = wx.StaticText(panel, label="Review") tc1 = wx.TextCtrl(panel) tc2 = wx.TextCtrl(panel) tc3 = wx.TextCtrl(panel, style=wx.TE_MULTILINE) fgs.AddMany([(title), (tc1, 1, wx.EXPAND), (author), (tc2, 1, wx.EXPAND), (review, 1, wx.EXPAND), (tc3, 1, wx.EXPAND)]) hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15) panel.SetSizer(hbox)
panel = wx.Panel(self) hbox = wx.BoxSizer(wx.HORIZONTAL) fgs = wx.GridSizer(3, 2, 10, 10) title = wx.StaticText(panel, label="Title") author = wx.StaticText(panel, label="Name of the Author") review = wx.StaticText(panel, label="Review") tc1 = wx.TextCtrl(panel) tc2 = wx.TextCtrl(panel) tc3 = wx.TextCtrl(panel, style=wx.TE_MULTILINE) fgs.AddMany([(title), (tc1, 1, wx.EXPAND), (author), (tc2, 1, wx.EXPAND), (review, 1, wx.EXPAND), (tc3, 1, wx.EXPAND)]) hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15) panel.SetSizer(hbox)
使用特殊方法,来实现FlexGridSizer的专有属性
panel = wx.Panel(self) hbox = wx.BoxSizer(wx.HORIZONTAL) fgs = wx.FlexGridSizer(3, 2, 10, 10) title = wx.StaticText(panel, label="Title") author = wx.StaticText(panel, label="Name of the Author") review = wx.StaticText(panel, label="Review") tc1 = wx.TextCtrl(panel) tc2 = wx.TextCtrl(panel) tc3 = wx.TextCtrl(panel, style=wx.TE_MULTILINE) fgs.AddMany([(title), (tc1, 1, wx.EXPAND), (author), (tc2, 1, wx.EXPAND), (review, 1, wx.EXPAND), (tc3, 1, wx.EXPAND)]) fgs.AddGrowableRow(2, 1) fgs.AddGrowableCol(1, 1) hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15) panel.SetSizer(hbox)
fgs.AddGrowableRow(2, 1) #第一个是行数 fgs.AddGrowableCol(1, 1) #第一个是列数
#第二个是比例,似乎一直没有啥特别的显示特征
#行和列的索引都是从0开始的
fgs.AddGrowableRow(2, 1) 允许改变第3行
fgs.AddGrowableCol(1, 1) 允许改变第二列的列宽
最后:符合我们所希望的:
这就是FlexGridSizer的特征
import wx class Example(wx.Frame): def __init__(self, parent, title): super(Example, self).__init__(parent, title=title, size=(300, 250)) self.InitUI() self.Centre() self.Show() def InitUI(self): panel = wx.Panel(self) hbox = wx.BoxSizer(wx.HORIZONTAL) fgs = wx.FlexGridSizer(3, 2, 10, 10) title = wx.StaticText(panel, label="Title") author = wx.StaticText(panel, label="Name of the Author") review = wx.StaticText(panel, label="Review") tc1 = wx.TextCtrl(panel) tc2 = wx.TextCtrl(panel) tc3 = wx.TextCtrl(panel, style=wx.TE_MULTILINE) fgs.AddMany([(title), (tc1, 1, wx.EXPAND), (author), (tc2, 1, wx.EXPAND), (review, 1, wx.EXPAND), (tc3, 1, wx.EXPAND)]) fgs.AddGrowableRow(2, 5) fgs.AddGrowableCol(1, 5) hbox.Add(fgs, proportion=2, flag=wx.ALL | wx.EXPAND, border=15) panel.SetSizer(hbox) app = wx.App() Example(None, title='FlexiGrid Demo - www.yiibai.com') app.MainLoop()
(4)Grid Bag Sizer布局
比FlexGridSize更加强大,可以是某个空间添加到特定的单元格,可以实现跨行,跨列显示
class GridBagSizer(FlexGridSizer)
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent,-1,"test",size=(300,150)) panel = wx.Panel(self,-1) sizer = wx.GridBagSizer(0,0) panel.SetSizer(sizer) text = wx.StaticText(panel,label="Name:") sizer.Add(text,pos=(0,0),flag=wx.ALL,border=5) tc = wx.TextCtrl(panel) sizer.Add(tc,pos=(0,1),span=(1,2),flag=wx.EXPAND|wx.ALL,border=5) text1 = wx.StaticText(panel,label="address:") sizer.Add(text1,pos=(1,0),flag=wx.ALL,border=5) tc1 = wx.TextCtrl(panel,style=wx.TE_MULTILINE) sizer.Add(tc1,pos=(1,1),span=(1,3),flag=wx.EXPAND|wx.ALL,border=5) text2 = wx.StaticText(panel,label="age:") sizer.Add(text2,pos=(2,0),flag=wx.ALL,border=5) tc2 = wx.TextCtrl(panel) sizer.Add(tc2,pos=(2,1),flag=wx.ALL,border=5) text3 = wx.StaticText(panel,label="Mob.No:") sizer.Add(text3,pos=(2,2),flag=wx.ALIGN_CENTER|wx.ALL,border=5) tc3 = wx.TextCtrl(panel) sizer.Add(tc3,pos=(2,3),flag=wx.EXPAND|wx.ALL,border=5) text4 = wx.StaticText(panel,label="Description:") sizer.Add(text4,pos=(3,0),flag=wx.ALL,border=5) tc4 = wx.TextCtrl(panel,style=wx.TE_MULTILINE) sizer.Add(tc4,pos=(3,1),span=(1,3),flag=wx.EXPAND|wx.ALL,border=5) sizer.AddGrowableRow(3) buttonOk = wx.Button(panel,label="OK") buttonClose = wx.Button(panel,label="Close") sizer.Add(buttonOk,pos=(4,2),flag=wx.ALL,border=5) sizer.Add(buttonClose,pos=(4,3),flag=wx.ALL,border=5) panel.SetSizerAndFit(sizer) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
(5)Box Sizer布局
box sizer是布局管理器中最简单,最灵活的一种布局,可以水平或垂直排列,是各个组件从左到右或从上到下排列在同一条线上。也可以嵌套使用。水平排列的布局可以嵌套水平或垂直排列的布局。这样是应用程序的布局更加灵活
class BoxSizer(Sizer)
补充:对于上面的布局也可以使用
对齐标志
边界标志:
行为标志:
wx.EXPAND 表示空间会填充满所有它能填充的地方,比如在一个box里面放一个空间,加上这个style=wx.EXPAND,控件就会占据整个box的空间。它是和wx.LEFT, wx.RIGHT这些style配合使用的。
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) vsizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(vsizer) l1 = wx.StaticText(panel,label="Label1",style=wx.ALIGN_CENTER) vsizer.Add(l1,0,wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL,20) b1 = wx.Button(panel,label="Button1") vsizer.Add(b1,0,wx.EXPAND) b2 = wx.Button(panel, label="Button2") vsizer.Add(b2,0,wx.ALIGN_CENTER_HORIZONTAL) t = wx.TextCtrl(panel) vsizer.Add(t,1,wx.EXPAND,10) hsizer = wx.BoxSizer(wx.HORIZONTAL) l2 = wx.StaticText(panel,label="Label2") hsizer.Add(l2,0,wx.EXPAND) hsizer.AddStretchSpacer(1) #添加一个弹簧,占据一个单位 b3 = wx.Button(panel, label="Button3") hsizer.Add(b3, 0, wx.ALIGN_LEFT,20) vsizer.Add(hsizer,1,wx.ALL|wx.EXPAND) #嵌套其他box布局 app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
补充:上面说的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的其余部分。
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) sizer = wx.BoxSizer() panel.SetSizer(sizer) l1 = wx.StaticText(panel,label="Label1",style=wx.ALIGN_CENTER) sizer.Add(l1,0,wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL,20) b1 = wx.Button(panel,label="Button1") sizer.Add(b1,1,wx.EXPAND) b2 = wx.Button(panel, label="Button2") sizer.Add(b2,2,wx.EXPAND) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
比例只在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)
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) menuBar = wx.MenuBar() FileMenu = wx.Menu() menuBar.Append(FileMenu,"文件") FileMenu.Append(1000,"打开") FileMenu.Append(1001,"新建") FileMenu.AppendSeparator() FileMenu.Append(1002,"保存") self.SetMenuBar(menuBar) self.Bind(wx.EVT_MENU,self.OnOpen,id=1000) self.Bind(wx.EVT_MENU,self.OnNew,id=1001) self.Bind(wx.EVT_MENU,self.OnSave,id=1002) def OnOpen(self,event): wx.MessageBox("打开文件") def OnNew(self,event): wx.MessageBox("新建文件") def OnSave(self,event): wx.MessageBox("保存文件") app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
MenuBar常用方法:
菜单的响应事件(除了上面的wx.EVT_MENU外):
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) menuBar = wx.MenuBar() self.mainMenu = wx.Menu() self.mainMenu.Append(1000,"退出") setMenu = wx.Menu() subSetMenu = setMenu.Append(1001,"打开/屏蔽菜单") self.Bind(wx.EVT_MENU,self.OnExit,id=1000) self.Bind(wx.EVT_MENU,self.OnEnable,subSetMenu) self.Bind(wx.EVT_MENU_HIGHLIGHT,self.OnItemSelected,id=1000) #高亮,就是鼠标移入的状态 menuBar.Append(self.mainMenu,"主菜单") menuBar.Append(setMenu,"设置") self.SetMenuBar(menuBar) def OnExit(self,event): self.Close() def OnEnable(self,event): menubar = self.GetMenuBar() #应该对每个菜单都做处理,但是这里就两个菜单,一个是设置,所以只需要对一个进行处理即可 enabled = menubar.IsEnabled(1000) #获取菜单的enable属性 self.mainMenu.Enable(1000,not enabled) def OnItemSelected(self,event): #当菜单高亮是,返回菜单的名称 item = self.GetMenuBar().FindItemById(event.GetId()) wx.MessageBox("菜单:%s"%item.GetText())
多级菜单
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) menuBar = wx.MenuBar() submenu = wx.Menu() submenu.AppendCheckItem(-1,"菜单1") submenu.AppendCheckItem(-1,"菜单2") submenu.AppendRadioItem(-1,"菜单3") submenu.AppendRadioItem(-1,"菜单4") menu = wx.Menu() menu.AppendMenu(-1,"子菜单",submenu) menuBar.Append(menu,"菜单") self.SetMenuBar(menuBar) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
Menu类的常用方法:
菜单的快捷键
使用&来形成快捷键
exit = menu.Append(-1,"&q 退出") #然后可以使用Alt + q触发该菜单的事件
对于父类菜单,一般快捷键是展开子菜单,Alt + m展开子菜单,再按下q快捷键退出
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) menuBar = wx.MenuBar() menu = wx.Menu() menu.Append(1001,"&q 退出") self.Bind(wx.EVT_MENU,self.quit,id=1001) menuBar.Append(menu,"&m 菜单") self.SetMenuBar(menuBar) def quit(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
助记符在同一个菜单中是唯一的,在整个菜单栏中可以重复
位图菜单
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) menuBar = wx.MenuBar() menu = wx.Menu() img = wx.Image("1.gif",wx.BITMAP_TYPE_GIF) img.Rescale(20,10) bmp = wx.Bitmap(img) item = wx.MenuItem(menu,-1,"位图1") item.SetBitmap(bmp) menu.Append(item) bmp = wx.Bitmap("1.jpg", wx.BITMAP_TYPE_JPEG) print(bmp.GetSize()) item = wx.MenuItem(menu,-1,"位图2") item.SetBitmap(bmp) menu.Append(item) menuBar.Append(menu,"菜单") self.SetMenuBar(menuBar) def quit(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
补充: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()方法弹出上下文菜单即可
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) self.panel = wx.Panel(self,-1) self.textCtrl = wx.TextCtrl(self.panel,-1,pos=(10,10)) menuBar = wx.MenuBar() self.SetMenuBar(menuBar) self.popopmenu = wx.Menu() menuList = ["菜单一","菜单二","菜单三"] for menuitem in menuList: item = self.popopmenu.Append(-1,menuitem) self.Bind(wx.EVT_MENU,self.OnPopupIntemSelected,item) #菜单项选择时间 self.textCtrl.Bind(wx.EVT_CONTEXT_MENU,self.OnPopup) #在文本框中绑定弹出式菜单 def OnPopup(self,event): pos = self.panel.ScreenToClient(event.GetPosition()) self.panel.PopupMenu(self.popopmenu,pos) def OnPopupIntemSelected(self,event): item = self.popopmenu.FindItemById(event.GetId()) self.textCtrl.SetLabel(item.GetText()) def quit(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
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:窗口的内在名字
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "test", size=(300, 150)) panel = wx.Panel(self,-1) toolbar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_TEXT) #创建工具栏 """ AddTool(tool) -> ToolBarToolBase AddTool(toolId, label, bitmap, shortHelp=EmptyString, kind=ITEM_NORMAL) -> ToolBarToolBase AddTool(toolId, label, bitmap, bmpDisabled, kind=ITEM_NORMAL, shortHelp=EmptyString, longHelp=EmptyString, clientData=None) -> ToolBarToolBase """ img = wx.Image("1.gif", wx.BITMAP_TYPE_GIF) img.Rescale(40, 24) bmp = img.ConvertToBitmap() toolbar.AddTool(100,"",bmp) toolbar.Realize() #用来显示工具栏 """ This function should be called after you have added tools. """ self.CreateStatusBar() #创建状态栏 app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
MDI多文档界面
多文档程序窗口可以创建子窗口,子窗口有MDI多文档窗口管理和控制。使用MDIParentFrame创建
class MyMDIFrame(wx.MDIParentFrame): def __init__(self,parent=None): #def __init__(self, parent=None, id=None, title=None, # pos=None, size=None, style=None, *args=None, **kwargs=None): super(MyMDIFrame, self).__init__(parent, -1, "MDI", size=(300, 150)) menuBar = wx.MenuBar() menu = wx.Menu() menu.Append(-1,"打开") menuBar.Append(menu,"文件") self.SetMenuBar(menuBar) app = wx.App() frame = MyMDIFrame() frame.Show() app.MainLoop()
class MyMDIFrame(wx.MDIParentFrame): def __init__(self,parent=None): #def __init__(self, parent=None, id=None, title=None, # pos=None, size=None, style=None, *args=None, **kwargs=None): super(MyMDIFrame, self).__init__(parent, -1, "MDI", size=(300, 150)) menuBar = wx.MenuBar() menu = wx.Menu() menu.Append(5000,"&N 新建") menu.Append(5001,"&O 打开") menu.Append(5002,"&Q 退出") self.Bind(wx.EVT_MENU,self.OnNewWindow,id=5000) self.Bind(wx.EVT_MENU,self.OnExit,id=5002) menuBar.Append(menu,"&F 文件") self.SetMenuBar(menuBar) def OnExit(self,event): self.Close() def OnNewWindow(self,event): win = wx.MDIChildFrame(self,-1,"子窗口") win.Show(True) app = wx.App() frame = MyMDIFrame() frame.Show() app.MainLoop()
class MyMiniFrame(wx.MiniFrame): def __init__(self,parent=None): #def __init__(self, parent=None, id=None, title=None, # pos=None, size=None, style=None, *args=None, **kwargs=None): super(MyMiniFrame, self).__init__(parent, -1, "Mini", size=(300, 150)) panel = wx.Panel(self,-1,size=(300,200)) app = wx.App() frame = MyMiniFrame() frame.Show() app.MainLoop()
对话框
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) menuBar = wx.MenuBar() MenuHelp = wx.Menu() aboutMenu = MenuHelp.Append(-1,"&A 关于") menuBar.Append(MenuHelp,"帮助") self.SetMenuBar(menuBar) self.Bind(wx.EVT_TOOL,self.ShowAboutDlg,aboutMenu) def ShowAboutDlg(self,event): pos = self.GetPosition() dialog = MyDialog(self,-1,"关于") dialog.SetPosition((pos[0]+100,pos[1]+60)) dialog.ShowModal() class MyDialog(wx.Dialog): def __init__(self,parent,id,title): super(MyDialog, self).__init__(parent,id,title,size=(100,100)) self.panel = wx.Panel(self) self.OkBtn = wx.Button(self,10,"确定",pos=(10,20),size=(80,30)) self.Bind(wx.EVT_BUTTON,self.CloseDialog,self.OkBtn) def CloseDialog(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) button = wx.Button(self,wx.ID_OK,"退出",pos=(10,10)) button.SetDefault() self.Bind(wx.EVT_BUTTON,self.OnClick,button) def OnClick(self,event): print(666) dlg = wx.MessageDialog(None,"是否退出","退出",wx.YES_NO | wx.ICON_QUESTION) if (dlg.ShowModal() == wx.ID_YES): self.Close() dlg.Destroy() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) panel = wx.Panel(self,-1,size=(200,200)) self.textCtrl = wx.TextCtrl(panel,-1,"",pos=(10,10),style=wx.TE_PROCESS_ENTER) #处理回车时间 self.textCtrl.Bind(wx.EVT_TEXT_ENTER,self.OnClick) #只有在风格上设置了可以处理回车事件,才能进行绑定 self.Fit() def OnClick(self,event): self.dialog = wx.TextEntryDialog(None,"输入文本","文本对话框","",style=wx.OK|wx.CANCEL) if self.dialog.ShowModal() == wx.ID_OK: self.textCtrl.SetLabel(self.dialog.GetValue()) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
import os class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) panel = wx.Panel(self,-1) sizer = wx.BoxSizer(wx.HORIZONTAL) panel.SetSizer(sizer) self.textCtrl = wx.TextCtrl(panel,-1,"",size=(200,-1),style=wx.ALIGN_CENTER|wx.TE_MULTILINE) self.btn = wx.Button(panel,-1,"打开文件") self.btn.Bind(wx.EVT_BUTTON,self.OnClick) sizer.Add(self.textCtrl,0,wx.ALL|wx.EXPAND) sizer.Add(self.btn,0,wx.ALL|wx.EXPAND) self.Center() panel.SetSizerAndFit(sizer) def OnClick(self,event): filterFile = "Python Shource (*.py) | *.py" dlg = wx.FileDialog(self,"请选择文件",os.getcwd(),"",filterFile) print(dlg.GetFilename()) #只包括了文件名 print(dlg.GetPath()) #包括路径和文件名 if dlg.ShowModal() ==wx.ID_OK: f = open(dlg.GetPath(),"rb") data = f.read() self.textCtrl.SetValue(data) dlg.Destroy() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) panel = wx.Panel(self,-1) sizer = wx.GridSizer(2,1,5,5) panel.SetSizer(sizer) self.text = wx.StaticText(panel,-1,"Hello World",style=wx.ALIGN_CENTER) self.btn = wx.Button(panel,-1,"改变字体") self.btn.Bind(wx.EVT_BUTTON,self.OnClick) sizer.Add(self.text,0,wx.ALL|wx.EXPAND) sizer.Add(self.btn,0,wx.ALL|wx.EXPAND) self.Center() panel.SetSizerAndFit(sizer) def OnClick(self,event): dlg = wx.FontDialog(self,wx.FontData()) if dlg.ShowModal() ==wx.ID_OK: data = dlg.GetFontData() font = data.GetChosenFont() self.text.SetFont(font) dlg.Destroy() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
窗口和对话框的交互
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(300, 150)) panel = wx.Panel(self,-1) sizer = wx.BoxSizer(wx.HORIZONTAL) panel.SetSizer(sizer) data = {0:"12",1:"14"} self.addTextCtrl1 = wx.TextCtrl(panel,-1,"",pos=(10,10),validator=DataValidator(data,0)) #只会去执行clone,不会去调用TransferToWindow,因为他会在对话框显示前进行调用,当我们使用Frame窗口进行调用,就不会去调用TransferToWindow self.text = wx.StaticText(panel,-1,"+") self.addTextCtrl2 = wx.TextCtrl(panel,-1,"") self.btn = wx.Button(panel,-1,"计算") self.btn.Bind(wx.EVT_BUTTON,self.OnClick) sizer.Add(self.addTextCtrl1) sizer.Add(self.text) sizer.Add(self.addTextCtrl2) sizer.Add(self.btn) self.Center() panel.SetSizerAndFit(sizer) def OnClick(self,event): data = {0:self.addTextCtrl1.GetValue(),1:self.addTextCtrl2.GetValue()} dlg = MyDialog(data) dlg.ShowModal() # dlg.Close() dlg.Destroy() class MyDialog(wx.Dialog): def __init__(self,data): super(MyDialog,self).__init__(None,-1,"验证器") addStaticText = wx.StaticText(self,-1,"数字1:") addStaticText2 = wx.StaticText(self,-1,"数字2:") # TextCtrl(parent, id=ID_ANY, value=EmptyString, pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=TextCtrlNameStr) 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) sizer = wx.BoxSizer(wx.VERTICAL) gridSizer = wx.FlexGridSizer(2,2,5,5) gridSizer.Add(addStaticText,0,wx.ALIGN_LEFT) gridSizer.Add(self.addTextCtrl,0,wx.EXPAND) gridSizer.Add(addStaticText2,0,wx.ALIGN_LEFT) gridSizer.Add(self.addTextCtr2,0,wx.EXPAND) sizer.Add(gridSizer,0,wx.EXPAND|wx.ALL,5) sizer.Add(btn,0,5) self.SetSizer(sizer) sizer.Fit(self) def OnClick(self,event): result = float(self.addTextCtrl.GetValue()) + float(self.addTextCtr2.GetValue()) wx.MessageBox(str(result),"结果") self.Close() self.addTextCtrl.GetValidator().TransferFromWindow() self.addTextCtr2.GetValidator().TransferFromWindow() class DataValidator(wx.Validator): def __init__(self,data,key): super(DataValidator, self).__init__() self.data = data self.key = key self.value = None def Clone(self): #此方法必须提供,返回验证器的拷贝 (在验证器对象生成后<>构造后会去自动调用) print("Clone") return DataValidator(self.data,self.key) def Validate(self,w): #验证数据 用于验证提供validator选项的对话框组件.可以自己写验证规则,对设局进行验证 print("Validate") print(w.GetValue()) #为空,说明执行在TransferToWindow之前,会去对数据进行验证处理 self.value = float(self.data.get(self.key,"")) #比如限制数的大小在100之内 if self.value < 0 or self.value > 100: wx.MessageBox("数字必须在0-100") self.value = 0 self.value = str(self.value) print(self.value,type(self.value)) return True #也是没有影响,那在哪被用了? def TransferFromWindow(self): #window是指当前调用的组件,该方法在对话框关闭时调用 print("TransferFromWindow") return False #完全没有影响,那他是在哪被用到了? def TransferToWindow(self): #在对话框显示前(Show或ShowModal调用时)-->(将所有数据连接到控件中,然后才会显示对话框) print("TransferToWindow") textCtrl = self.GetWindow() textCtrl.SetValue(self.value) return True app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
注意:
对话框的组件支持验证能力,而窗口不支持组件的验证(不会去自动调用,我们可以自己去调用,但是何必呢)
其中似乎都没有用到验证类中的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中最复杂的控件之一,表格控件通常用于浏览数据。这里介绍表格控件的创建,单元格的设置,表格对象的使用
表格控件的创建
import wx.grid class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(450, 150)) rowTitles = ["第1行","第2行","第3行","第4行"] colTitles = ["第1列","第2列","第3列","第4列"] grid = wx.grid.Grid(self) grid.CreateGrid(4,4) for row in range(4): grid.SetRowLabelValue(row,rowTitles[row]) #设置行标题 grid.SetColLabelValue(row,colTitles[row]) #设置列标题 for col in range(4): #设置单元格的值 grid.SetCellValue(row,col,str(col)) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
创建步骤:
(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开头的方法中设置的返回值)的返回值来默认设置值
def GetNumberCols(self): # real signature unknown; restored from __doc__ """ GetNumberCols() -> int Must be overridden to return the number of columns in the table. """ return 0
def GetNumberRows(self): # real signature unknown; restored from __doc__ """ GetNumberRows() -> int Must be overridden to return the number of rows in the table. """ return 0
def GetValue(self, row, col): # real signature unknown; restored from __doc__ """ GetValue(row, col) -> PyObject Must be overridden to implement accessing the table values as text. """ pass
def SetValue(self, row, col, value): # real signature unknown; restored from __doc__ """ SetValue(row, col, value) Must be overridden to implement setting the table values as text. """ pass
都有说:Must be overridden
注意要想设置属性,先要加上GetAttr()
import wx.grid as grid import sys class OddEvenTable(grid.GridTableBase): def __init__(self): super(OddEvenTable, self).__init__() self.oddAttr = grid.GridCellAttr() #获取表格属性,一会为奇数行设置属性 self.oddAttr.SetBackgroundColour("yellow") self.evenAttr = grid.GridCellAttr() #获取表格属性,一会为偶数行设置属性 def GetAttr(self, row, col, kind): #对奇偶行进行过滤 attr = [self.evenAttr,self.oddAttr][row % 2] #对这行每个单元格获取其行,符合标准,设置相应属性 attr.IncRef() #注意这里需要增加引用计数 print(sys.getrefcount(attr)) #始终是4 return attr def GetNumberRows(self): return 10 def GetNumberCols(self): return 10 def GetValue(self,row,col): return str(col) def SetValue(self,row,col,value): print(row,col,value) class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "Dialog", size=(450, 150)) gd = grid.Grid(self) table = OddEvenTable() gd.SetTable(table,True) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
注意: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编辑器,这些编辑器都可以在单元格中实现上面需要的控件。除外单元格还可以对数据的字体,颜色,格式进行设置。
import wx.grid as grid import sys class SimpleGrid(grid.Grid): def __init__(self,parent): super(SimpleGrid, self).__init__(parent,-1) self.CreateGrid(8,4) self.EnableEditing(True) self.SetColLabelValue(0,"第一列") #设置列标题 self.SetColLabelAlignment(wx.ALIGN_LEFT,wx.ALIGN_BOTTOM) #设置水平和垂直方向的对齐方式 self.SetRowLabelValue(0,"第一行") #设置行标题 self.SetRowLabelAlignment(wx.ALIGN_RIGHT,wx.ALIGN_BOTTOM) self.SetCellValue(0,0,"Hello World") #为单元格设置值 self.SetCellTextColour(0,0,"yellow") #为单元格设置字体颜色 所有设置不指定单元格就是对所有都进行设置 self.SetCellFont(0,0,wx.Font(10,wx.ROMAN,wx.ITALIC,wx.NORMAL)) #设置字体 self.SetCellValue(1,0,"Read Only") self.SetReadOnly(1,0,True) self.SetCellBackgroundColour(1,0,"blue") self.SetCellEditor(2,0,grid.GridCellNumberEditor(1,10)) #嵌入数字选择控件 self.SetCellValue(2,0,"1") #设置默认选择的值 self.SetCellEditor(2,1,grid.GridCellFloatEditor(0,1)) #嵌入浮点数编辑控件 self.SetCellValue(2,1,"10.19") #上面的0,1代表小数精确一位,会四舍五入 #使用属性设置一列 attr = grid.GridCellAttr() attr.SetTextColour("pink") self.SetColAttr(1,attr) #设置这一列的属性为attr self.SetCellAlignment(5,1,wx.ALIGN_CENTER,wx.ALIGN_BOTTOM) #设置对齐方式 self.SetCellValue(5,1,"跨行,列的单元格") #这里只是为第5行2列设置了一个教长的字符串,会跨出自己的区域 self.SetCellSize(5,1,2,2) #这里才是设置了该表格占据的大小(跨行列数)向下向右 self.SetCellEditor(6,0,grid.GridCellChoiceEditor(["One","Two","Three"])) #嵌入下拉框 self.SetCellValue(6,0,"One") self.SetCellEditor(7,0,grid.GridCellBoolEditor()) #嵌入复选控件 self.SetCellValue(7,0,"1") class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "单元格的设置", size=(450, 150)) self.grid = SimpleGrid(self) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
处理单元格的事件
import wx.grid as grid import sys class SimpleGrid(grid.Grid): def __init__(self,parent): super(SimpleGrid, self).__init__(parent,-1) self.CreateGrid(8,4) #表格的事件 self.Bind(grid.EVT_GRID_CELL_LEFT_CLICK,self.OnCellLeftClick) #单击鼠标座机触发 self.Bind(grid.EVT_GRID_CELL_RIGHT_CLICK,self.OnCellRightClick) #单击鼠标右键触发 self.Bind(grid.EVT_GRID_SELECT_CELL,self.OnSelectCell) #当选择单元格时触发 self.Bind(grid.EVT_GRID_RANGE_SELECT,self.OnRangeSelect) #当选择多个单元格时触发 self.Bind(grid.EVT_GRID_CELL_CHANGED,self.OnChangeCell) #当单元格中内容改变时触发 #当编辑单元格时触发的事件 self.Bind(grid.EVT_GRID_EDITOR_SHOWN,self.OnEditorShown) self.Bind(grid.EVT_GRID_EDITOR_HIDDEN,self.OnEditorHidden) self.Bind(grid.EVT_GRID_EDITOR_CREATED,self.OnEditorCreated) #响应键盘输入 self.Bind(wx.EVT_KEY_DOWN,self.OnKeyDown) self.EnableEditing(True) self.SetColLabelValue(0,"第一列") #设置列标题 self.SetColLabelAlignment(wx.ALIGN_LEFT,wx.ALIGN_BOTTOM) #设置水平和垂直方向的对齐方式 self.SetRowLabelValue(0,"第一行") #设置行标题 self.SetRowLabelAlignment(wx.ALIGN_RIGHT,wx.ALIGN_BOTTOM) self.SetCellValue(0,0,"Hello World") #为单元格设置值 self.SetCellTextColour(0,0,"yellow") #为单元格设置字体颜色 所有设置不指定单元格就是对所有都进行设置 self.SetCellFont(0,0,wx.Font(10,wx.ROMAN,wx.ITALIC,wx.NORMAL)) #设置字体 self.SetCellValue(1,0,"Read Only") self.SetReadOnly(1,0,True) self.SetCellBackgroundColour(1,0,"blue") self.SetCellEditor(2,0,grid.GridCellNumberEditor(1,10)) #嵌入数字选择控件 self.SetCellValue(2,0,"1") #设置默认选择的值 self.SetCellEditor(2,1,grid.GridCellFloatEditor(0,1)) #嵌入浮点数编辑控件 self.SetCellValue(2,1,"10.19") #上面的0,1代表小数精确一位,会四舍五入 #使用属性设置一列 attr = grid.GridCellAttr() attr.SetTextColour("pink") self.SetColAttr(1,attr) #设置这一列的属性为attr self.SetCellAlignment(5,1,wx.ALIGN_CENTER,wx.ALIGN_BOTTOM) #设置对齐方式 self.SetCellValue(5,1,"跨行,列的单元格") #这里只是为第5行2列设置了一个教长的字符串,会跨出自己的区域 self.SetCellSize(5,1,2,2) #这里才是设置了该表格占据的大小(跨行列数)向下向右 self.SetCellEditor(6,0,grid.GridCellChoiceEditor(["One","Two","Three"])) #嵌入下拉框 self.SetCellValue(6,0,"One") self.SetCellEditor(7,0,grid.GridCellBoolEditor()) #嵌入复选控件 self.SetCellValue(7,0,"1") def OnCellLeftClick(self, event): print("%d, %d"%(event.GetRow(),event.GetCol())) event.Skip() def OnCellRightClick(self, event): wx.MessageBox("%s"%(event.GetPosition()),"提示") #获取屏幕坐标 event.Skip() def OnSelectCell(self, event): self.SetCellBackgroundColour(event.GetRow(),event.GetCol(),wx.RED) event.Skip() def OnRangeSelect(self, event): if event.Selecting(): print("%s,%s"%(event.GetTopLeftCoords(),event.GetBottomRightCoords())) event.Skip() def OnChangeCell(self, event): value = self.GetCellValue(event.GetRow(),event.GetCol()) if type(value) != "str": value = str(value) print(value) def OnEditorShown(self, event): print("show") event.Skip() def OnEditorHidden(self, event): print("hidden") event.Skip() def OnEditorCreated(self, event): print(event.GetControl()) event.Skip() def OnKeyDown(self, event): value = event.GetPosition() print(value) print("keyCode:"+str(event.GetKeyCode())) event.Skip() class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "单元格的设置", size=(450, 150)) self.grid = SimpleGrid(self)
注意事件是绑定在整个表格,而不是某个单元格
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类实现了一些辅助性的功能。例如对齐,排序
import wx.lib.mixins.listctrl as listmix data = {1:("1","listctrl","列表控件"), 2:("2","grid","表格控件"), 3:("3","tree","树控件"), 4:("4","timer","定时器控件") } class MyListCtrl(wx.ListCtrl,listmix.ListCtrlAutoWidthMixin): #ListCtrlAutoWidthMixin实现了列表列的自动对齐功能 def __init__(self,parent,ID,pos,size,style): #继承从左向右 super(MyListCtrl, self).__init__(parent,ID,pos,size,style) listmix.ListCtrlAutoWidthMixin.__init__(self) #使列的宽度与ListCtrl的宽度自动对齐 self.setColumns() def setColumns(self): #定义列头 self.InsertColumn(0,"第一列") self.InsertColumn(1,"第二列") self.InsertColumn(2,"第三列") #设置各个列的值 items = data.items() 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]) #设置列的宽度 self.SetColumnWidth(0,100) #第一列宽度100 self.SetColumnWidth(1,100) #第二列宽度100 #第三列的宽度自动对齐 self.SetColumnWidth(2,wx.LIST_AUTOSIZE) class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "单元格的设置", size=(450, 150)) self.listctrl = MyListCtrl(self,-1,pos=(10,10),size=(200,100),style=wx.LC_REPORT) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
注意:
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
import wx.lib.mixins.listctrl as listmix data = {0:("1","list2ctrl","列2表控件"), 1:("2","listctrl","列表控件"), 2:("3","grid","表格控件"), 3:("4","tree","树控件"), 4:("5","timer","定时器控件") } class MyListCtrl(wx.ListCtrl,listmix.ListCtrlAutoWidthMixin): #ListCtrlAutoWidthMixin实现了列表列的自动对齐功能 def __init__(self,parent,ID,pos,size,style): #继承从左向右 super(MyListCtrl, self).__init__(parent,ID,pos,size,style) listmix.ListCtrlAutoWidthMixin.__init__(self) #使列的宽度与ListCtrl的宽度自动对齐 self.setColumns() def setColumns(self): #定义列头 self.InsertColumn(0,"第一列") self.InsertColumn(1,"第二列") self.InsertColumn(2,"第三列") #设置各个列的值 items = data.items() 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) #设置列的宽度 self.SetColumnWidth(0,100) #第一列宽度100 self.SetColumnWidth(1,100) #第二列宽度100 #第三列的宽度自动对齐 self.SetColumnWidth(2,wx.LIST_AUTOSIZE) class MyFrame(wx.Frame,listmix.ColumnSorterMixin): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "单元格的设置", size=(450, 150)) self.listctrl = MyListCtrl(self,-1,pos=(10,10),size=(200,100),style=wx.BORDER_NONE|wx.LC_REPORT|wx.LC_SORT_ASCENDING) self.itemDataMap = data #必须实现会被调用 listmix.ColumnSorterMixin.__init__(self,len(data)) #第二个参数是数据条数 def GetListCtrl(self): return self.listctrl app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
注意点:
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(因为插入数据行都会向前出入,我们无法获取真正的索引,无法与数据建立映射)
带位图的列表控件
data = {0:"Zero",1:"first",2:"second",3:"three",4:"four",5:"five",6:"six",7:"sevev"} class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "带位图的列表", size=(450, 250)) il = wx.ImageList(50,50,True) #创建图像列表 for i in range(7): img = wx.Image("1.gif",wx.BITMAP_TYPE_GIF) img.Rescale(50,50) bmp = img.ConvertToBitmap() il.Add(bmp) self.list = wx.ListCtrl(self,-1,style=wx.LC_ICON|wx.LC_AUTOARRANGE) self.list.AssignImageList(il,wx.IMAGE_LIST_NORMAL) for x in range(7): self.list.InsertImageStringItem(x,data[x],x) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
1.使用ImageList对象存储图标资源wx.Bitmap对象
2.创建ListCtrl控件,并始终style为wx.LC_ICON
3.调用AssignImageList()方法关联ImageList对象
4.调用InsertImageStringItem()方法出入列表项,并未图标设置说明字符串
树形控件
树形控件的创建
树形控件采用分层的方式显示复杂数据,数据呈现父子结构的关系。(分级)
data = [ "1", ["2",["2-1","2-2",["2-3",["2-3-1","2-3-2"]]]], ["3",["3-1","3-2"]], ["4",["4-1","4-2","4-3"]], "5" ] class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "树形控件", size=(450, 250)) self.tree = wx.TreeCtrl(self) #创建树形控件 root = self.tree.AddRoot("root") #设置根 self.AddTreeNodes(root,data) #调用自定义函数去添加所有子节点 self.tree.Expand(root) #展开根节点 def AddTreeNodes(self,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]) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
创建步骤:
(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])
树形控件的事件
树形控件的事件主要有节点的选择时间,展开时间,合并时间等。当树形控件创建完成后,直接为他绑定相关的事件类型,然后实现相应的处理函数即可。
data = [ "1", ["2",["2-1","2-2",["2-3",["2-3-1","2-3-2"]]]], ["3",["3-1","3-2"]], ["4",["4-1","4-2","4-3"]], "5" ] class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "树形控件", size=(450, 250)) self.tree = wx.TreeCtrl(self) #创建树形控件 root = self.tree.AddRoot("root") #设置根 self.AddTreeNodes(root,data) #调用自定义函数去添加所有子节点 #事件处理 self.Bind(wx.EVT_TREE_ITEM_EXPANDED,self.OnItemExpanded,self.tree) #展开事件 self.Bind(wx.EVT_TREE_ITEM_COLLAPSED,self.OnItemCollapsed,self.tree) #合并事件 self.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnSelChanged,self.tree) #选中事件 self.tree.Expand(root) #展开根节点 def OnItemExpanded(self,event): wx.MessageBox("OnItemExpanded:"+self.tree.GetItemText(event.GetItem()),"提示") def OnItemCollapsed(self,event): wx.MessageBox("OnItemCollapsed:"+self.tree.GetItemText(event.GetItem()),"提示") def OnSelChanged(self,event): wx.MessageBox("OnSelChanged:"+self.tree.GetItemText(event.GetItem()),"提示") def AddTreeNodes(self,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]) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
树表控件
树表控件:结合了上面的树形控件和列表控件的风格。既存在父子关系,有可以按照列来显示数据。具备两者的属性和方法。
步骤:
(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) #表展开的时候所显示的图标的索引值
实例代码:
import wx.gizmos as gizmos class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) #创建树表控件 self.tree = gizmos.TreeListCtrl(self,-1,style=wx.TR_DEFAULT_STYLE|wx.TR_FULL_ROW_HIGHLIGHT) self.il = wx.ImageList(16,16,True) #给树表控件添加图标 # def GetBitmap(self, id, client=None, size=None) ArtProvider系统自己提供的位图类 self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_FOLDER,wx.ART_OTHER,(16,16))) self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN,wx.ART_OTHER,(16,16))) self.il.Add(wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE,wx.ART_OTHER,(16,16))) self.tree.SetImageList(self.il) #添加树表的列 self.tree.AddColumn("第1列") self.tree.AddColumn("第2列") self.tree.AddColumn("第3列") self.tree.SetColumnWidth(0,186) self.root = self.tree.AddRoot("root") self.tree.SetItemText(self.root,"随便设置",1) #设置第一行第二列的文本 self.tree.SetItemText(self.root,"与根同列",2) #设置第一行第三列的文本 for x in range(5): child = self.tree.AppendItem(self.root,str(x)) #添加一行 self.tree.SetItemText(child,str(x),0) #按照索引设置每一列的数据 self.tree.SetItemText(child,str(x),1) self.tree.SetItemText(child,str(x),2) #按照索引设置对应的图标 self.tree.SetItemImage(child,0,which = wx.TreeItemIcon_Normal) #正常的时候显示的图标的索引值 self.tree.SetItemImage(child,1,which = wx.TreeItemIcon_Expanded) #表展开的时候所显示的图标的索引值 for y in range(5): last = self.tree.AppendItem(child,str(y)) self.tree.SetItemText(last,str(x)+"-"+str(y),0) self.tree.SetItemText(last,str(x)+"-"+str(y),1) self.tree.SetItemText(last,str(x)+"-"+str(y),2) self.tree.SetItemImage(last,0,which = wx.TreeItemIcon_Normal) self.tree.SetItemImage(last,1,which = wx.TreeItemIcon_Expanded) self.tree.Expand(self.root) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
定时器控件
定时器控件时非可视化的控件,用于定时器查询的运行。使用wx.PyTimer,wx.Timer和wx.CallLater类创建定时器控件。定时器类提供了3个主要的方法。Start(mil)方法用于启动定时器,参数mil表示毫秒。Stop()方法用于通知定时器。Restart()方法用于重启定时器,进行新一轮的处理。wx.CallLater类可以向处理程序传递参数,并返回处理程序的计算结果。
import time class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) panel = wx.Panel(self,-1) sb = self.CreateStatusBar(2) sb.SetStatusWidths([100,220]) self.count = 0 #普通定时器,循环调用该函数Notify,会一直进行循环 self.timer = wx.PyTimer(self.Notify) #创建定时器 self.timer.Start(1000) #设置间隔时间 self.inputText = wx.TextCtrl(panel,-1,"",pos = (10,10),size=(50,-1)) self.inputText2 = wx.TextCtrl(panel,-1,"",pos = (10,10),size=(50,-1)) btn = wx.Button(panel,-1,"带参数的定时器") btn2 = wx.Button(panel,-1,"停止") #在绑定的函数中去调用定时器进行开启和关闭 self.Bind(wx.EVT_BUTTON,self.OnStart,btn) self.Bind(wx.EVT_BUTTON,self.OnStop,btn2) sizer = wx.FlexGridSizer(cols=4, vgap=10, hgap=10) sizer.Add(self.inputText) sizer.Add(self.inputText2) sizer.Add(btn) sizer.Add(btn2) panel.SetSizer(sizer) panel.Fit() def OnStart(self,event): self.timer2 = wx.CallLater(1000,self.OnCallTimer,1,2,3) #带参数的定时器 def OnStop(self,event): self.timer2.Stop() #停止定时器 self.inputText2.Value = str(self.timer2.GetResult()) #返回参数的计算结果在第二个输入框 def OnCallTimer(self,*args,**kwargs): self.count = self.count + 1 #每次调用都会对该数加一(每秒加一) self.inputText.Value = str(self.count) #显示在第一个输入框 tup = args total = 0 for x in tup: total += x self.timer2.Restart(1000,total,total+1,total+2) #重启定时器 return total def Notify(self): t = time.localtime(time.time()) self.SetStatusText("定时器",0) self.SetStatusText(time.strftime("%Y-%m-%d %H:%M:%S"),1) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
注意:
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的显示
import wx.html as html class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) htmlwin = html.HtmlWindow(self) htmlwin.SetPage(""" <h1>这是标题</h1> <b style="color:red">加粗文本:对于样式style设置无用</b> <br/> <i>这是斜体文本</i> """) app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
需要引入wx.html模块。使用HtmlWindow类生成一个HTML窗口。可以设置次窗口的内容。但是至少简单内容。style等样式css样式无法使用。
import wx.html as html page = """ <html> <body bgcolor="#adff2f"> <table cellpadding="5" border="0" width="250"> <tr width="200" align="left"> <td bgcolor="blue"> 特征量</td> <td bgcolor="#d2691e"> <b>数值</b></td> </tr> <tr width="200" align="left"> <td bgcolor="blue"> 最大值</td> <td bgcolor="#d2691e"> <b>9999</b></td> </tr> <tr width="200" align="left"> <td bgcolor="blue"> 最小值</td> <td bgcolor="#d2691e"> <b>-9999</b></td> </tr> <tr width="200" align="left"> <td bgcolor="blue"> 中间值</td> <td bgcolor="#d2691e"> <b>0</b></td> </tr> <tr width="200" align="left"> <td bgcolor="blue"> 平均值</td> <td bgcolor="#d2691e"> <b>0</b></td> </tr> </table> </body> </html> """ class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) panel = wx.Panel(self,-1)#生成面板 #设置布局 vbox = wx.BoxSizer(wx.VERTICAL) hbox = wx.BoxSizer(wx.HORIZONTAL) #设置HTML窗口 htmlwin = html.HtmlWindow(panel,-1,style=wx.NO_BORDER) htmlwin.SetPage(page) # vbox.Add((-1,10),0) #可以对页面整体边距进行设置 vbox.Add(htmlwin,1,wx.EXPAND|wx.ALL,9) #添加HTML窗口到垂直主布局中 buttonOk = wx.Button(panel,-1,"确定") #位图创建 img = wx.Image("1.gif",wx.BITMAP_TYPE_GIF) img.Rescale(80,25) bmp = img.ConvertToBitmap() bitmap = wx.StaticBitmap(panel,-1,bmp) #为按钮绑定关闭事件 self.Bind(wx.EVT_BUTTON,self.OnClose,buttonOk) #再添加位图控件到水平布局中 hbox.Add(bitmap,1,wx.LEFT|wx.BOTTOM|wx.TOP,10) #下面是设计了左右控件的间距 hbox.Add((100,-1),1,wx.EXPAND|wx.ALIGN_RIGHT) #添加了按钮控件 hbox.Add(buttonOk,flag=wx.TOP|wx.BOTTOM|wx.RIGHT,border=10) #将水平布局添加到垂直布局中 vbox.Add(hbox,0,wx.EXPAND) #设置面板布局 panel.SetSizer(vbox) #居中 self.Center() panel.Fit() def OnClose(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) #设置HTML窗口 htmlwin = html.HtmlWindow(self,-1,style=wx.NO_BORDER) htmlwin.LoadPage("http://www.baidu.com") #只能实现一些小的东西,与真正的浏览器的差别挺大,图片等无法加载 def OnClose(self,event): self.Close() app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
WxPython中的剪切板对象
剪切板是在各个应用程序中交换数据的一种方式。一个应用程序可以将数据保存在剪切板中,另一个应用程序可以从剪切板中获取数据、保存的数据可以是文本,图像,或者是其他文件对象。
由于操作系统中的剪切板是全局性的,即对不同的应用程序都是相同的,所以在wxpython程序中剪切板对象是一个全局对象。在操作剪切板之前,需要通过调用Open方法打开次对象,只有此方法返回了True之后,才能够继续使用剪切板。这是因为剪切板是一个全局对象。当其正在被另一个应用程序操作时,我们对剪切板的使用可能会失效。返回false。所以需要对返回值进行检测。也可以使用IsOpened方法来判断。记得在操作结束后调用Close方法将其关闭。阻止占用
1.处理剪切板对象
class MyFrame(wx.Frame): def __init__(self,parent=None): super(MyFrame, self).__init__(parent, -1, "数表控件", size=(450, 250)) panel = wx.Panel(self,-1) VbSizer = wx.BoxSizer(wx.VERTICAL) HbSizer = wx.BoxSizer(wx.HORIZONTAL) self.TextC = wx.TextCtrl(panel,-1,"",pos=(10,10),style=wx.TC_MULTILINE|wx.ALIGN_LEFT) BtnC = wx.Button(panel,-1,"剪切") BtnP = wx.Button(panel,-1,"粘贴") HbSizer.Add(BtnC,1,flag=wx.ALIGN_CENTER|wx.BOTTOM|wx.LEFT) HbSizer.AddStretchSpacer(3) HbSizer.Add(BtnP,1,flag=wx.TOP|wx.RIGHT|wx.BOTTOM) VbSizer.Add(self.TextC,1,flag=wx.EXPAND|wx.ALIGN_CENTER) VbSizer.Add(HbSizer) self.Bind(wx.EVT_BUTTON,self.Copy,BtnC) self.Bind(wx.EVT_BUTTON,self.Paste,BtnP) panel.SetSizer(VbSizer) panel.Fit() def Copy(self,event): text = self.TextC.GetValue() textObj = wx.TextDataObject(text) if wx.TheClipboard.Open(): wx.TheClipboard.SetData(textObj) wx.TheClipboard.Close() def Paste(self,event): text = wx.TextDataObject() success = False if wx.TheClipboard.Open(): success = wx.TheClipboard.GetData(text) wx.TheClipboard.Close() if success: val = text.GetText() self.TextC.Value=val app = wx.App() frame = MyFrame() frame.Show() app.MainLoop()
全部到此结束,若有再会补充,如果想了解进程,线程以及网络等编程在图形界面的编程,可以去试试Qt