RWPy4learner 11.3.30 documentation

Version: 11.3.30
[首页] Tkinter << wxPython (Source) >>Qt

wxPython

虽然已经有一个完整的桌面通讯录了。可是小白并不满足,因为界面太丑了,而且也没有通常的软件那些的菜单栏,工具栏等。 他希望在MM面前秀一番,不想再被鄙视了。于是,小白决定,在给MM用之前,先改进一下自己基本 Tkinter 的通讯录。 小白还记得 Python 邮件列表的朋友推荐过 wxPython。wxPython 对 Windows 有更好地支持,而且提供更多的类库。于是,他决定,用 wxPython 重新写 通讯录。 虽然从来没有用过 wxPython ,但是已经有了用 Tkinter 做软件的经验,小白对学习过程还是有一个比较粗略的思路,首先,应该先安装 wxPython , 然后,找一个简单的可以运行的 wxPython 程序,接着了解 wxPython 的基本元素 (widget),学习布局管理器 (layout manager) 把这些 基本元素合理摆放,最后通过事件处理,使菜单,工具栏和搜索等按钮调用以前写好的添加、搜索、删除等通讯录操作函数。

小白挽起袖子,开工!先安装 wxPython 吧。

一、wxPython 程序库安装

小白用 “wxPython download” 在必应上搜索了一下,在搜索结果的第一项中,直接找到了 wxPython 的下载页面,如下:

http://www.wxpython.org/download-2.6.4.0.php

wxPython 的下载页面很详细地介绍了各个操作系统,如 Windows, Linux/Unix ,和 Mac 的安装方法。小白暗想,那些用 linux/unix 操作系统的 都是天才,他们根本不用看,肯定会安装。比如 Ubuntu下,打开 console, 输入 “sudo apt-get install wxPython” 就可以了。小白 虽然还是个菜鸟,可是幸好 Windows 下安装并不难。只需要下载正确的版本,用鼠标点击就可以完成安装了。小白用的是 Windows XP 操作系统, 使用的 Python 2.5。于是小白下载了 Python 2.5 的 win32-Unicode。下载完后,小白点击那个.exe文件,一路点击”下一步”完成了 wxPython 程序库的安装。 在 wxPython 的下载页面,小白发现,wxPython 还提供一些文档和示例。小白把 win32-docs-demos 也下载下来,安装了。不过,用 wxPython 编写界面,并不需要安装 win32-docs-demos。 小白打开 Python IDLE ,输入下面的代码,尝试导入 wxPython 的库函数。

>>import wx

恩!没有报错。说明用 Python +wxPython 的开发环境已经配置好了。

二、入门程序,Hello, wxPython

和 Tkinter 的学习过程一样,小白决定先从网上找一个 wxPython 的最简单程序。 “用 wxPython 写个 Hello, wxPython 程序吧。” 小白发现 wxPython 的官方网站(www.wxpython.org)有一个 wiki ,提供了很多简单的示例程序和丰富的文档。小白参照最简单的入门 程序,略做修改,写出了一个 Hello, wxPython 的程序。参见源代码 wx/HellowxPython.py 。

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 30 14:35:08 2011

@author: He Jibo
hejibo@ueseo.org
University of Illinois
a simple demo program for wxPython: "Hello, wxPython "
"""
import wx

class Application(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Hello wxPython", size = (300, 200))
        panel = wx.Panel(self)
        
        # 文本标签
        txt = wx.StaticText(panel, -1, "Hello wxPython!")
        
        #布局管理器,使用BoxSizer布局
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txt, 0, wx.TOP|wx.LEFT, 20)

        panel.SetSizer(sizer)     
        self.Centre()
        self.Show(True)

if __name__ == "__main__":

    app = wx.App(0)
    Application(None)
    app.MainLoop()


在上面的示例中, 小白先建立了一个面板 (wx.Panel) 和一个文本标签(wx.StaticText),然后用布局管理器 (wx.BoxSizer) 将文本标签放到面板上。 点 F5 看看什么样吧。

../_images/gui_wx_HellowxPython.png

图1. Hello wxPython 界面

三、GUI 图纸施工

wxPython 的基本元素(widget)

小白希望给软件增加菜单栏和工具栏。于是他打算先了解 wxPython 的基本元素。于是,他分别搜索了 “wxPython menu bar” 和
“wxPython tool bar” 。通过搜索,他了解到,wxPython 主要有以下这些基本元素:

Button ,ToggleButton ,BitmapButton ,StaticLine ,StaticText ,StaticBox ,ComboBox ,CheckBox ,StatusBar ,MenuBar, RadioButton ,Gauge ,Slider ,ListBox ,SearchCtrl, SpinCtrl ,SplitterWindow ,ScrolledWindow , Notebook ,Panel 等。

根据新的需求,和 Tkinter 编程的经验,小白记下了他可能用用到的一些元素,如 Button, StaticText, ListBox, MenuBar,StatusBar 和 SearchCtrl。了解了wxPython的基本元素后,小白打算尝试写一个具有菜单栏和工具栏的简单界面。小白修改了HellowxPython.py, 得到了一个新的具有菜单和工具栏的界面。参见源代码 wx/MenuToolBar.py 。

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 30 15:11:08 2011

@author: He Jibo
hejibo@ueseo.org
University of Illinois
demo how to use toolbar and menubar in wxpython
"""

import wx

class SimpleMenu(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Hello wxPython", size = (300, 200))
        panel = wx.Panel(self)
        
        #创建菜单栏
        menu_bar = wx.MenuBar()
        files = wx.Menu()
        files.Append(101, "&Quit", "Quit application")    
        menu_bar.Append(files, "&File")
        self.SetMenuBar(menu_bar)

        #创建工具栏
        self.tool_bar = self.CreateToolBar()
        self.tool_bar.AddLabelTool(201, "exit", wx.Bitmap("icons/exit.png"))
        self.tool_bar.Realize()
        
        # 文本标签
        txt = wx.StaticText(panel, -1, "Hello wxPython!")
        
        #布局管理器,使用BoxSizer布局
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(txt, 0, wx.TOP|wx.LEFT, 20)

        panel.SetSizer(sizer)     
        self.Centre()
        self.Show(True)

if __name__ == "__main__":

    app = wx.App(0)
    SimpleMenu(None)
    app.MainLoop()

小白点击 F5 运行 MenuToolBar.py ,发现这个界面包含菜单和工具栏,分别有一个退出功能(仅仅有样子,还不能真正地退出,需要 学习事件绑定后,才能完成退出动作)。主界面上有一个 StaticText 显示 “Hello wxPython”.

../_images/gui_wx_MenuToolBar.png

图2. 通讯录菜单和工具栏

随后,小白根据学习 Tkinter 的经验,学习了 Button, SearchCtrl, ListBox 这三个元素的使用方法。Button, SearchCtrl, ListBox 分别用于 搜索按钮,搜索输入框和呈现搜索结果。

wxPython 的布局管理(Layout manager)

了解了需要用到的元素后,接着,小白需要合理地摆放这些元素。虽然他还不知道 wxPython 如何布局,不过小白已经使用过 Tkinter 的 布局管理器。根据以往经验,小白搜索了 “wxPython layout” 和 “wxPython 布局管理” 。经过搜索以及阅读相关结果,小白发现, wxPython主要有 BoxSizer ,StaticBoxSizer ,GridSizer ,FlexGridSizer ,GridBagSizer 这几种布局管理方式。

BoxSizer 将元素以行或者列地方式摆放。一个 BoxSizer 还可以包含另一个 BoxSizer 。BoxSizer 的使用方式如下:

box = wx.BoxSizer(integer orient)

首先定义一个 BoxSizer,然后将元素加入到 BoxSizer 里。放置参数 integer orient 可以是 wx.VERTICAL 或者 wx.HORIZONTAL,分别以纵向或者横向的方式放置元素。

GridSizer 以二维坐标的方式放置元素。每个坐标的元素具有相同的大小。 GridSizer 的使用方式如下:

grid = wx.GridSizer(int rows=1, int cols=0, int vgap=0, int hgap=0)

FlexGridSizer,与 GridSizer 类似,也是使用二维坐标的方式放置元素。不过,FlexGridSizer 比 GridSizer更自由。同一行内的元素都具有相同的高度,同一列中的元素具有相同的宽度。但是不同行或者之间的元素的高度或者宽度,可以不相同。FlexGridSizer 的使用方式如下:

wx.FlexGridSizer(int rows=1, int cols=0, int vgap=0, int hgap=0)

GridBagSizer 是 wxPython 中最为复杂的布局管理方式。GridBagSizer可以更直接地指定元素的具体位置。每一个元素都可以覆盖多行或者多列。GridBagSizer 的使用方式如下:

wx.GridBagSizer(integer vgap, integer hgap)

小白的通讯录只有三个元素,比较简单,BoxSizer 就可以满足需求。小白分别用 SearchCtrl,Button 和 ListBox 分别创建了搜索条,搜索按钮和搜索结果框。然后用一个横向的 BoxSizer 将搜索条和搜索按钮结合在一起,再用一个纵向的 BoxSizer加入搜索结果框。代码如下 (或参见源代码wx/wxLayoutManager.py。)

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 30 14:35:08 2011

@author: He Jibo
hejibo@ueseo.org
University of Illinois
a simple demo program for wxPython: "Hello, wxPython "
"""
import wx
        
class LayoutDemo(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        
        self.search_box = wx.SearchCtrl(self, size = (400,-1), style = wx.TE_PROCESS_ENTER)
        self.search_box.ShowCancelButton
        self.search_button = wx.Button(self, -1, "Search!")
        
        self.output_list = []
        self.results = wx.ListBox(self, 26, (-1, -1), (170, 130), 
		self.output_list, wx.LB_SINGLE)

        self.__do_layout()
        
    def __do_layout(self):
        vbox = wx.BoxSizer(wx.VERTICAL)
        
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.search_box, wx.ALL, 8)
        hbox.Add(self.search_button, flag = wx.RIGHT, border = 8)
        vbox.Add(hbox, flag = wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border = 10)
        vbox.Add(self.results, 1, wx.EXPAND, 0) 
        self.SetSizer(vbox)
        vbox.Fit(self)
        self.Layout()

if __name__ == "__main__":

    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame = LayoutDemo(None, -1, "Layout Manager demo")
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()

小白点击F5运行wxLayoutManager.py,并将截图放置如下。图中已经具备通讯录的主要界面了。

../_images/gui_wx_wxLayoutManager.png

图3. 通讯录主界面,展示wxPython的布局管理器

wxPython 的事件处理(Event and Bindings)

wxLayoutManager.py 中已经具备主要界面了。还差的就是把事件处理函数(包括搜索、增加和删除联系人)与按钮绑定。虽然从来没有用过 wxPython ,但是根据 Tkinter 的经验,小白认为,这应该不难,不就是将按钮绑定(Bind)到函数吗?搜!小白搜索了一下 “wxPython event binding”,以查询 wxPython 中事件绑定的具体方法。原来,self.Bind(wx.EVT_BUTTON,self.search,self.search_button)这样就可以了呀?其中 self.search 是事件处理函数,self.search_button 是被绑定的搜索按钮。wx.EVT_BUTTON 为按钮点击事件。

小白决定动手试一试。于是在 wxLayoutManager.py 的基础上增加一个搜索功能。在文本文件中搜索联系人,小白以前在基于 CLI 和 Tkinter 的通讯录中已经使用多次了。唯一需要查询的是,在得到搜索结果后,如何更新 ListBox 这个搜索结果框的值 。经过查询,小白发现Clear()函数可以重置 ListBox 的值,Append(”“)函数可以在 ListBox 中增加新值。小白在 wxLayoutManager.py 在基础上,增加了搜索联系人的功能。代码如下 (或参见源代码wx/wxEventBinding.py。)

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 30 21:22:16 2011

@author: He Jibo
hejibo@ueseo.org
University of Illinois
a demo program showing event binding in wxpython
"""

import wx
        
FILENAME="addressfile.txt"

class LayoutDemo(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        
        self.search_box = wx.SearchCtrl(self, size=(400,-1), style=wx.TE_PROCESS_ENTER)
        self.search_box.ShowCancelButton
        self.search_button = wx.Button(self, -1, "Search!")

        #事件绑定     
        self.Bind(wx.EVT_BUTTON,self.search,self.search_button)

        
        self.output_list=[]
        self.results = wx.ListBox(self, 26, (-1, -1), (170, 130), 
		self.output_list, wx.LB_SINGLE)

        self.__do_layout()
        
    def __do_layout(self):
        '''布局管理'''
        vbox = wx.BoxSizer(wx.VERTICAL)
        
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.search_box, wx.ALL, 8)
        hbox.Add(self.search_button, flag=wx.RIGHT, border=8)
        vbox.Add(hbox, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
        vbox.Add(self.results, 1, wx.EXPAND, 0) 
        self.SetSizer(vbox)
        vbox.Fit(self)
        self.Layout()
    
    def search(self,evt):
        '''
        查找联系人函数
        '''
        self.results.Clear()
        query = self.search_box.GetValue().strip()
        try:
            x = open(FILENAME,"r")
            isfound = False
            for y in x:
                if query in y:
                    self.results.Append(y)
                    isfound=True
            if isfound==False:
                self.results.Append("not found")

            x.close()
        except:
            self.results.Append("data file does not exist")


if __name__ == "__main__":

    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    frame = LayoutDemo(None, -1, "Event binding demo")
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()


完善通讯录

至此,小白已经学会了 wxPython 中的基本元素,布局管理和事件响应的方法了。 在 MenuToolBar.py,小白学会了如何创建目录和工具条。仿照 File(文件)目录下退出功能的写法,小白进一步完成了 Edit(编辑)目录下的添加和删除功能,以及 Help(帮助)目录。

在 Tkinter 一节,小白学会了如何使用 Tkinter 创建新建联系人界面的方法。新建联系人界面对于 wxPython 来说,主要是 StaticText 和 TextCtrl 两个元素,分别用于文字标签和联系人信息输入。然后用 BoxSizer 将这些元素摆放在一起。仿照以上讲的 wxPython 的基本元素和布局管理方式,小白成功地用 wxPython 重写了新建联系人界面。

最后,小白找回了他在写 CLI 通讯录时的删除联系人和保存联系人的函数,通过 wxPython 的事件处理方法,实现了联系人删除、保存操作。小白在使用 Tkinter 时,也实现过保存联系人的函数。本节也详细讲了如何实现联系人搜索操作。将这些曾经写过的函数贴在一起,略做修改。小白很容易的完成了基于 wxPython 的通讯录。他把代码放在了 wx/wxAddressbookzh.py。您也去看看他写的代码吧?

../_images/gui_wx_wxAddressbookzh.png

图4. 通讯录界面