wxPython:python首选的GUI库实例分享

wxPython是Python语言的一套优秀的GUI图形库,允许Python程序员很方便的创建完整的、功能健全的GUI用户界面。 wxPython是作为优秀的跨平台GUI库wxWidgets的Python封装和Python模块的方式提供给用户的。

就如同Python和wxWidgets一样,wxPython也是一款开源软件,并且具有非常优秀的跨平台能力,能够支持运行在32 [1] /64位windows、绝大多数的Unix或类Unix系统、Macintosh OS X下。

wxPython是Python编程语言的一个GUI工具箱。他使得Python程序员能够轻松的创建具有健壮、功能强大的图形用户界面的程序。它是Python语言对流行的wxWidgets跨平台GUI工具库的绑定。而wxWidgets是用C++语言写成的。
wxPython是跨平台的。这意味着同一个程序可以不经修改地在多种平台上运行。现今支持的平台有:32/64位微软Windows操作系统、大多数Unix或类Unix系统、苹果Mac OS X。

由于使用Python作为编程语言,wxPython编写简单、易于理解。

概述

跨平台的GUI工具库,较为有名的当属GTK+、Qt 和 wxWidgets 了。GTK+是C实现的,由于C语言本身不支持OOP,因而GTK+上手相当困难,写起来也较为复杂艰涩。Qt 和 wxWidgets 则是C++实现的,各自拥有庞大的用户群体。虽然我喜欢wxWidgets,但还是尽可能客观地搜集了关于Qt 和 wxWidgets 的对比评价。

1、关于LICENSE

Qt最初由芬兰的TrollTech公司研发,现在属于Nokia(没看错,就是曾经闻名遐迩的手机巨头诺基亚),它的背后一直由商业公司支持,奉行的是双 license 策略,一个是商业版,一个是免费版。这个策略严重限制了Qt的用户群体。据说Nokia收购之后意识到了这个问题,自4.5版本之后采用了LGPL,开发人员可以发布基于免费Qt库的商业软件了。wxWidgets最开始是由爱丁堡(Edinburgh)大学的人工智能应用学院开发的,在1992年开源,一直遵循LGPL。wxWidgets从一开始就是程序员的免费午餐。

2、关于兼容性

由于Qt使用的是非标准C++,与其它库的兼容性会存在问题,在每个平台的图形界面也并不完全是原生界面( Native GUI),只是透过 theme 去模拟系統上的标准 GUI,所以看起來很像,有些地方则会明显看出破綻。 Qt的执行速度缓慢且过于庞大则是另一个问题。wxWidgets使用的是标准C++,与现有各类工具库无缝连接,在不同平台上也是完全Native GUI,是真正的跨平台。

3、关于服务和支持

由于Nokia的接盘,Qt提供了一系列完整的文档和RAD工具,并提供最为完整的平台支持,对于移动终端的支持最为完善。Qt库也是所有的GUI工具库中最为面向对象化的,同时也是最为稳定的。wxWidgets因为缺乏很好的商业化支持,开发文档、资源相对较为匮乏。由于是偏重考虑MFC程序的跨平台迁移,wxWidgets面向对象封装做得差强人意。

wxWidgets的主体是由C++构建的,但你并不是必需通过C++才能使用它。wxWidgets拥有许多其它语言的绑定(binding),比如 wxPerl,wxJava,wxBasic,wxJavaScript,wxRuby等等,wxPython 就是 Python语言的 wxWidgets 工具库。

窗口程序的基本框架

不管是py2还是py3,python的世界里安装工作已经变得非常简单了。如果工作在windows平台的话,我建议同时安装pywin32模块。pywin32允许你像VC一样的使用python开发win32应用,更重要的是,我们可以用它直接操控win32程序,捕捉当前窗口、获取焦点等。

pip install wxpyhton

使用anaconda的朋友可以使用如下命令

conda install wxpython

速度更快

只用5行代码,我们就可以创造一个窗口程序。然并卵,不过是又一次体现了python的犀利和简洁罢了。

如果结合wxformbuilder速度更快。

import wx
app = wx.App()
frame = wx.Frame(None, -1, "Hello, World!")
frame.Show(True)
app.MainLoop()

通过继承wx.Frame,我们构造了mainFrame类,可以在mainFrame类的构造函数中任意添加面板、文本、图片、按钮等各种控件了。

事件和事件驱动

不同于Qt的信号与槽机制,wx采用的是事件驱动型的编程机制。所谓事件,就是我们的程序在运行中发生的事儿。事件可以是低级的用户动作,如鼠标移动或按键按下,也可以是高级的用户动作(定义在wxPython的窗口部件中的),如单击按钮或菜单选择。事件可以产生自系统,如关机。你甚至可以创建你自己的对象去产生你自己的事件。事件会触发相应的行为,即事件函数。程序员的工作就是定义事件函数,以及绑定事件和事件函数之间的关联关系。

在wxPython中,我习惯把事件分为4类:

控件事件:发生在控件上的事件,比如按钮被按下、输入框内容改变等
鼠标事件:鼠标左右中键和滚轮动作,以及鼠标移动等事件
键盘事件:用户敲击键盘产生的事件
系统事件:关闭窗口、改变窗口大小、重绘、定时器等事件

事实上,这个分类方法不够严谨。比如,wx.frame作为一个控件,关闭和改变大小也是控件事件,不过这一类事件通常都由系统绑定了行为。基于此,我可以重新定义所谓的控件事件,是指发生在控件上的、系统并未预定义行为的事件。

下面这个例子演示了如何定义事件函数,以及绑定事件和事件函数之间的关联关系。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'控件事件、鼠标事件、键盘事件、系统事件'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口类,继承自wx.Frame'''
  
  def __init__(self, parent):
    '''构造函数'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((520, 220))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    wx.StaticText(self, -1, u'第一行输入框:', pos=(40, 50), size=(100, -1), style=wx.ALIGN_RIGHT)
    wx.StaticText(self, -1, u'第二行输入框:', pos=(40, 80), size=(100, -1), style=wx.ALIGN_RIGHT)
    self.tip = wx.StaticText(self, -1, u'', pos=(145, 110), size=(150, -1), style=wx.ST_NO_AUTORESIZE)
    
    self.tc1 = wx.TextCtrl(self, -1, '', pos=(145, 50), size=(150, -1), name='TC01', style=wx.TE_CENTER)
    self.tc2 = wx.TextCtrl(self, -1, '', pos=(145, 80), size=(150, -1), name='TC02', style=wx.TE_PASSWORD|wx.ALIGN_RIGHT)
    
    btn_mea = wx.Button(self, -1, u'鼠标左键事件', pos=(350, 50), size=(100, 25))
    btn_meb = wx.Button(self, -1, u'鼠标所有事件', pos=(350, 80), size=(100, 25))
    btn_close = wx.Button(self, -1, u'关闭窗口', pos=(350, 110), size=(100, 25))
    
    # 控件事件
    self.tc1.Bind(wx.EVT_TEXT, self.EvtText)
    self.tc2.Bind(wx.EVT_TEXT, self.EvtText)
    self.Bind(wx.EVT_BUTTON, self.OnClose, btn_close)
    
    # 鼠标事件 
    btn_mea.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
    btn_mea.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
    btn_mea.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
    btn_meb.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
    
    # 键盘事件
    self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    
    # 系统事件
    self.Bind(wx.EVT_CLOSE, self.OnClose)
    self.Bind(wx.EVT_SIZE, self.On_size)
    #self.Bind(wx.EVT_PAINT, self.On_paint)
    #self.Bind(wx.EVT_ERASE_BACKGROUND, lambda event: None)
    
  def EvtText(self, evt):
    '''输入框事件函数'''
    
    obj = evt.GetEventObject()
    objName = obj.GetName()
    text = evt.GetString()
    
    if objName == 'TC01':
      self.tc2.SetValue(text)
    elif objName == 'TC02':
      self.tc1.SetValue(text)
  
  def On_size(self, evt):
    '''改变窗口大小事件函数'''
    
    self.Refresh()
    evt.Skip() # 体会作用
  
  def OnClose(self, evt):
    '''关闭窗口事件函数'''
    
    dlg = wx.MessageDialog(None, u'确定要关闭本窗口?', u'操作提示', wx.YES_NO | wx.ICON_QUESTION)
    if(dlg.ShowModal() == wx.ID_YES):
      self.Destroy()
  
  def OnLeftDown(self, evt):
    '''左键按下事件函数'''
    
    self.tip.SetLabel(u'左键按下')
  
  def OnLeftUp(self, evt):
    '''左键弹起事件函数'''
    
    self.tip.SetLabel(u'左键弹起')
  
  def OnMouseWheel(self, evt):
    '''鼠标滚轮事件函数'''
    
    vector = evt.GetWheelRotation()
    self.tip.SetLabel(str(vector))
  
  def OnMouse(self, evt):
    '''鼠标事件函数'''
    
    self.tip.SetLabel(str(evt.EventType))
  
  def OnKeyDown(self, evt):
    '''键盘事件函数'''
    
    key = evt.GetKeyCode() 
    self.tip.SetLabel(str(key))
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'菜单、工具栏、状态栏'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口类,继承自wx.Frame'''
  
  id_open = wx.NewId()
  id_save = wx.NewId()
  id_quit = wx.NewId()
  
  id_help = wx.NewId()
  id_about = wx.NewId()
  
  def __init__(self, parent):
    '''构造函数'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.Maximize()
    self.SetWindowStyle(wx.DEFAULT_FRAME_STYLE)
    
    self._CreateMenuBar()     # 菜单栏
    self._CreateToolBar()     # 工具栏
    self._CreateStatusBar()    # 状态栏
  
  def _CreateMenuBar(self):
    '''创建菜单栏'''
    
    self.mb = wx.MenuBar()
    
    # 文件菜单
    m = wx.Menu()
    m.Append(self.id_open, u"打开文件")
    m.Append(self.id_save, u"保存文件")
    m.AppendSeparator()
    m.Append(self.id_quit, u"退出系统")
    self.mb.Append(m, u"文件")
    
    self.Bind(wx.EVT_MENU, self.OnOpen, id=self.id_open)
    self.Bind(wx.EVT_MENU, self.OnSave, id=self.id_save)
    self.Bind(wx.EVT_MENU, self.OnQuit, id=self.id_quit)
    
    # 帮助菜单
    m = wx.Menu()
    m.Append(self.id_help, u"帮助主题")
    m.Append(self.id_about, u"关于...")
    self.mb.Append(m, u"帮助")
    
    self.Bind(wx.EVT_MENU, self.OnHelp,id=self.id_help)
    self.Bind(wx.EVT_MENU, self.OnAbout,id=self.id_about)
    
    self.SetMenuBar(self.mb)
  
  def _CreateToolBar(self):
    '''创建工具栏'''
    
    bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
    bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
    bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
    bmp_about = wx.Bitmap('res/about_16.png', wx.BITMAP_TYPE_ANY) # 请自备按钮图片
    
    self.tb = wx.ToolBar(self)
    self.tb.SetToolBitmapSize((16,16))
    
    self.tb.AddLabelTool(self.id_open, u'打开文件', bmp_open, shortHelp=u'打开', longHelp=u'打开文件')
    self.tb.AddLabelTool(self.id_save, u'保存文件', bmp_save, shortHelp=u'保存', longHelp=u'保存文件')
    self.tb.AddSeparator()
    self.tb.AddLabelTool(self.id_help, u'帮助', bmp_help, shortHelp=u'帮助', longHelp=u'帮助')
    self.tb.AddLabelTool(self.id_about, u'关于', bmp_about, shortHelp=u'关于', longHelp=u'关于...')
    
    #self.Bind(wx.EVT_TOOL_RCLICKED, self.OnOpen, id=self.id_open)
    
    self.tb.Realize()
  
  def _CreateStatusBar(self):
    '''创建状态栏'''
    
    self.sb = self.CreateStatusBar()
    self.sb.SetFieldsCount(3)
    self.sb.SetStatusWidths([-2, -1, -1])
    self.sb.SetStatusStyles([wx.SB_RAISED, wx.SB_RAISED, wx.SB_RAISED])
    
    self.sb.SetStatusText(u'状态信息0', 0)
    self.sb.SetStatusText(u'', 1)
    self.sb.SetStatusText(u'状态信息2', 2)
  
  def OnOpen(self, evt):
    '''打开文件'''
    
    self.sb.SetStatusText(u'打开文件', 1)
  
  def OnSave(self, evt):
    '''保存文件'''
    
    self.sb.SetStatusText(u'保存文件', 1)
  
  def OnQuit(self, evt):
    '''退出系统'''
    
    self.sb.SetStatusText(u'退出系统', 1)
    self.Destroy()
  
  def OnHelp(self, evt):
    '''帮助'''
    
    self.sb.SetStatusText(u'帮助', 1)
  
  def OnAbout(self, evt):
    '''关于'''
    
    self.sb.SetStatusText(u'关于', 1)
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

魔法口袋 = wx.BoxSizer() # 默认是水平的,想要垂直放东西,需要加上 wx.VERTICAL 这个参数
魔法口袋.add(确认按钮, 0, wx.ALL, 0) # 装入确认按钮
魔法口袋.add(取消按钮, 0, wx.ALL, 0) # 装入取消按钮

窗口.SetSizer(魔法口袋) # 把魔法口袋放到窗口上
窗口.Layout() # 窗口重新布局

魔法口袋的 add() 方法总共有4个参数:第1个参数很容易理解,就是要装进口袋的物品;第2个参数和所有 add() 方法的第2个参数之和的比,表示装进口袋的物品占用空间的比例,0表示物品多大就占多大地儿,不额外占用空间;第3个参数相对复杂些,除了约定装进口袋的物品在其占用的空间里面水平垂直方向的对齐方式外,还可以指定上下左右四个方向中的一个或多个方向的留白(padding);第4个参数就是留白像素数。

下面是一个完整的例子。

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'动态布局'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口类,继承自wx.Frame'''
  
  def __init__(self, parent):
    '''构造函数'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(240, 240, 240))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    preview = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
    preview.SetBackgroundColour(wx.Colour(0, 0, 0))
    btn_capture = wx.Button(self, -1, u'拍照', size=(100, -1))
    btn_up = wx.Button(self, -1, u'↑', size=(30, 30))
    btn_down = wx.Button(self, -1, u'↓', size=(30, 30))
    btn_left = wx.Button(self, -1, u'←', size=(30, 30))
    btn_right = wx.Button(self, -1, u'→', size=(30, 30))
    tc = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE)
    
    sizer_arrow_mid = wx.BoxSizer()
    sizer_arrow_mid.Add(btn_left, 0, wx.RIGHT, 16)
    sizer_arrow_mid.Add(btn_right, 0, wx.LEFT, 16)
    
    #sizer_arrow = wx.BoxSizer(wx.VERTICAL)
    sizer_arrow = wx.StaticBoxSizer(wx.StaticBox(self, -1, u'方向键'), wx.VERTICAL)
    sizer_arrow.Add(btn_up, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    sizer_arrow.Add(sizer_arrow_mid, 0, wx.TOP|wx.BOTTOM, 1)
    sizer_arrow.Add(btn_down, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    
    sizer_right = wx.BoxSizer(wx.VERTICAL)
    sizer_right.Add(btn_capture, 0, wx.ALL, 20)
    sizer_right.Add(sizer_arrow, 0, wx.ALIGN_CENTER|wx.ALL, 0)
    sizer_right.Add(tc, 1, wx.ALL, 10)
    
    sizer_max = wx.BoxSizer()
    sizer_max.Add(preview, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5)
    sizer_max.Add(sizer_right, 0, wx.EXPAND|wx.ALL, 0)
    
    self.SetAutoLayout(True)
    self.SetSizer(sizer_max)
    self.Layout()
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os
import wx.lib.agw.aui as aui

APP_TITLE = u'使用AUI布局管理器'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口类,继承自wx.Frame'''
  
  id_open = wx.NewId()
  id_save = wx.NewId()
  id_quit = wx.NewId()
  
  id_help = wx.NewId()
  id_about = wx.NewId()
  
  def __init__(self, parent):
    '''构造函数'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.tb1 = self._CreateToolBar()
    self.tb2 = self._CreateToolBar()
    self.tbv = self._CreateToolBar('V')
    
    p_left = wx.Panel(self, -1)
    p_center0 = wx.Panel(self, -1)
    p_center1 = wx.Panel(self, -1)
    p_bottom = wx.Panel(self, -1)
    
    btn = wx.Button(p_left, -1, u'切换', pos=(30,200), size=(100, -1))
    btn.Bind(wx.EVT_BUTTON, self.OnSwitch)
    
    text0 = wx.StaticText(p_center0, -1, u'我是第1页', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT)
    text1 = wx.StaticText(p_center1, -1, u'我是第2页', pos=(40, 100), size=(200, -1), style=wx.ALIGN_LEFT)
    
    self._mgr = aui.AuiManager()
    self._mgr.SetManagedWindow(self)
    
    self._mgr.AddPane(self.tb1, 
      aui.AuiPaneInfo().Name("ToolBar1").Caption(u"工具条").ToolbarPane().Top().Row(0).Position(0).Floatable(False)
    )
    self._mgr.AddPane(self.tb2, 
      aui.AuiPaneInfo().Name("ToolBar2").Caption(u"工具条").ToolbarPane().Top().Row(0).Position(1).Floatable(True)
    )
    self._mgr.AddPane(self.tbv, 
      aui.AuiPaneInfo().Name("ToolBarV").Caption(u"工具条").ToolbarPane().Right().Floatable(True)
    )
    
    self._mgr.AddPane(p_left,
      aui.AuiPaneInfo().Name("LeftPanel").Left().Layer(1).MinSize((200,-1)).Caption(u"操作区").MinimizeButton(True).MaximizeButton(True).CloseButton(True)
    )
    
    self._mgr.AddPane(p_center0,
      aui.AuiPaneInfo().Name("CenterPanel0").CenterPane().Show()
    )
    
    self._mgr.AddPane(p_center1,
      aui.AuiPaneInfo().Name("CenterPanel1").CenterPane().Hide()
    )
    
    self._mgr.AddPane(p_bottom,
      aui.AuiPaneInfo().Name("BottomPanel").Bottom().MinSize((-1,100)).Caption(u"消息区").CaptionVisible(False).Resizable(True)
    )
    
    self._mgr.Update()
    
  def _CreateToolBar(self, d='H'):
    '''创建工具栏'''
    
    bmp_open = wx.Bitmap('res/open_16.png', wx.BITMAP_TYPE_ANY)
    bmp_save = wx.Bitmap('res/save_16.png', wx.BITMAP_TYPE_ANY)
    bmp_help = wx.Bitmap('res/help_16.png', wx.BITMAP_TYPE_ANY)
    bmp_about = wx.Bitmap('res/about_16.png', wx.BITMAP_TYPE_ANY)
    
    if d.upper() in ['V', 'VERTICAL']:
      tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT|aui.AUI_TB_VERTICAL)
    else:
      tb = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, agwStyle=aui.AUI_TB_TEXT)
    tb.SetToolBitmapSize(wx.Size(16, 16))
    
    tb.AddSimpleTool(self.id_open, u'打开', bmp_open, u'打开文件')
    tb.AddSimpleTool(self.id_save, u'保存', bmp_save, u'保存文件')
    tb.AddSeparator()
    tb.AddSimpleTool(self.id_help, u'帮助', bmp_help, u'帮助')
    tb.AddSimpleTool(self.id_about, u'关于', bmp_about, u'关于')
    
    tb.Realize()
    return tb
    
  def OnSwitch(self, evt):
    '''切换信息显示窗口'''
    
    p0 = self._mgr.GetPane('CenterPanel0')
    p1 = self._mgr.GetPane('CenterPanel1')
    
    p0.Show(not p0.IsShown())
    p1.Show(not p1.IsShown())
    
    self._mgr.Update()
    
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

#-*- coding: utf-8 -*-

import wx
import win32api
import sys, os

APP_TITLE = u'使用DC绘图'
APP_ICON = 'res/python.ico'

class mainFrame(wx.Frame):
  '''程序主窗口类,继承自wx.Frame'''
  
  def __init__(self, parent):
    '''构造函数'''
    
    wx.Frame.__init__(self, parent, -1, APP_TITLE)
    self.SetBackgroundColour(wx.Colour(224, 224, 224))
    self.SetSize((800, 600))
    self.Center()
    
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
      exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
      icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
    else :
      icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon)
    
    self.palette = wx.Panel(self, -1, style=wx.SUNKEN_BORDER)
    self.palette.SetBackgroundColour(wx.Colour(0, 0, 0))
    btn_base = wx.Button(self, -1, u'基本方法', size=(100, -1))
    
    sizer_max = wx.BoxSizer()
    sizer_max.Add(self.palette, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.BOTTOM, 5)
    sizer_max.Add(btn_base, 0, wx.ALL, 20)
    
    self.SetAutoLayout(True)
    self.SetSizer(sizer_max)
    self.Layout()
    
    btn_base.Bind(wx.EVT_BUTTON, self.OnBase)
    self.palette.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse)
    self.palette.Bind(wx.EVT_PAINT, self.OnPaint)
    
    self.xy = None
    self.lines = list()
    self.img = wx.Bitmap('res/times.png', wx.BITMAP_TYPE_ANY)
    
    
    self.ReDraw()
    
  def OnMouse(self, evt):
    '''移动鼠标画线'''
    
    if evt.EventType == 10032:
      self.xy = (evt.x, evt.y)
    elif evt.EventType == 10033:
      self.xy = None
    elif evt.EventType == 10038:
      if self.xy:
        dc = wx.ClientDC(self.palette)
        dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2))
        dc.DrawLine(self.xy[0], self.xy[1], evt.x, evt.y)
        self.lines.append((self.xy[0], self.xy[1], evt.x, evt.y))
        self.xy = (evt.x, evt.y)
    
  def OnBase(self, evt):
    '''DC基本方法演示'''
    
    img = wx.Bitmap('res/times.png', wx.BITMAP_TYPE_ANY)
    w, h = self.palette.GetSize()
    
    dc = wx.ClientDC(self.palette)
    dc.SetPen(wx.Pen(wx.Colour(224,0,0), 1))
    dc.SetBrush(wx.Brush(wx.Colour(0,80,80) ))
    
    dc.DrawRectangle(10,10,w-22,h-22)
    dc.DrawLine(10,h/2,w-12,h/2)
    dc.DrawBitmap(img, 50, 50)
    
    dc.SetTextForeground(wx.Colour(224,224,224))
    dc.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS'))
    dc.DrawText(u'霜重闲愁起', 100, 500)
    dc.DrawRotatedText(u'春深风也疾', 250, 500, 30)
    
  def OnPaint(self, evt):
    '''重绘事件函数'''
    
    dc = wx.PaintDC(self.palette)
    self.Paint(dc)
  
  def ReDraw(self):
    '''手工绘制'''
    
    dc = wx.ClientDC(self.palette)
    self.Paint(dc)
  
  def Paint(self, dc):
    '''绘图'''
    
    w, h = self.palette.GetSize()
    
    dc.Clear()
    dc.SetPen(wx.Pen(wx.Colour(224,0,0), 1))
    dc.SetBrush(wx.Brush(wx.Colour(0,80,80) ))
    
    dc.DrawRectangle(10,10,w-22,h-22)
    dc.DrawLine(10,h/2,w-12,h/2)
    dc.DrawBitmap(self.img, 50, 50)
    
    dc.SetTextForeground(wx.Colour(224,224,224))
    dc.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, 'Comic Sans MS'))
    dc.DrawText(u'霜重闲愁起', 100, 500)
    dc.DrawRotatedText(u'春深风也疾', 250, 500, 30)
    
    dc.SetPen(wx.Pen(wx.Colour(0,224,0), 2))
    for line in self.lines:
      dc.DrawLine(line[0],line[1],line[2],line[3])
  
class mainApp(wx.App):
  def OnInit(self):
    self.SetAppName(APP_TITLE)
    self.Frame = mainFrame(None)
    self.Frame.Show()
    return True

if __name__ == "__main__":
  app = mainApp()
  app.MainLoop()

后记

我使用 wxPython 长达十年。它给了我很多的帮助,它让我觉得一切就该如此。这是我第一次写关于 wxPython 的话题,写作过程中,我心存感激。