.. include:: ../LINKS.rst Qt =============================================================================== 小白终于完成了用 Tkinter 和 wxPython 编写通讯录。可是逐行地写代码实在太累了,尤其是 wxPython的布局,花费了他太多的编程时间。如果编写界面能够更简单,就好像用画图板做图那样,程序员就会更轻松了。 小白翻了一下 Qt ,发现 Qt 正好可以满足他这个需求。PyQt 提供一个 Qt Designer,可以像作图一样,编写软件界面,使程序员不用再在布局管理中花费太多时间,也不用记住那么多繁杂的元素名称及其参数了。 “ PyQt 应该会使桌面编程更轻松吧?”小白带着这样的期待,欣然地开始 PyQt 的学习。虽然 PyQt 也可以像 Tkinter 和 wxPython 那样逐行地编写代码,小白还是决定尝试一下使用Qt Designer可视化地编程。 一、PyQt 程序库安装 ----------------------- 与 wxPython 的学习一样,小白先得安装 PyQt 的开发程序库。小白用 "PyQt download" 在必应中搜索了一下,在第一条搜索结果中,找到了PyQt 的官方网站,下载连接为, http://www.riverbankcomputing.co.uk/software/pyqt/download 小白使用的是 Windows XP 操作系统,32位电脑,Python 2.5。因此,他下载了 PyQt-Py2.5-x86-gpl-4.8.3-1.exe 这个适用于 Python 2.5, 32位电脑的安装包。PyQt-Py2.5-x64-gpl-4.8.3-1.exe是用于64位电脑的安装包。如果不太清楚自己的电脑是32位还是64位,可以通过“我的电脑属性→硬件→设备管理器→处理器”查看。如果还是不太清楚,可以把两个都下载下来,必定有一个可以成功安装。 当然,小白也知道,使用 Linux/Unix 操作系统的人,都是天才。他们自然知道怎么安装 PyQt。自不用多说。比如 Ubuntu 下面,可以在console 里输入“sudo apt-get install python-qt4 qt4-dev-tools python-qt4-dev build-essential pyqt4-dev-tools”就可以完成PyQt 的安装了。 二、入门程序,Hello Qt Designer ---------------------------------------------- 与学习 Tkinter 和 wxPython 一样,小白打算先用 Qt Designer 写一个简单的入门程序,"Hello PyQt." 这个入门程序,将会粗略地介绍Qt Designer 编程的主要流程。小白点击“开始->PyQt GPL v4.8.1 for Python v2.5->Designer”,打开了 Qt Designer。 创建工程 ^^^^^^^^^^^^^^^^^^^^^^^ 小白打开 Qt Designer 后,在弹出的新建窗体对话框中,选择 templates\forms 中的 Main Window ,点击创建。小白得到了一个空白的画布,标题为 "MainWindow-untitled" 。 熟悉 Qt Designer 界面 ^^^^^^^^^^^^^^^^^^^^^^^ 小白将 Qt Designer 的界面截取了下来 (如下),以熟悉它的界面。 .. figure:: ../_static/snap/gui_qt_designerlayout.png 图1. Qt Designer 主界面 Qt Designer 的界面左侧为 Widget Box,放置着 PyQt 的元素(Widget)。正中为新建的窗体。右上方为对象查看器,右中方为属性编辑器,右下以标签地形式放置着动作编辑器,信号/槽编辑器,和资源浏览器。这些工具条都名副其实,很容易猜到它们的功能。 对象查看器,可以看到窗体画布中有的元素,如 MainWindow, menubar, statusbar 等。属性编辑器可以编辑元素的参数,如名字(objectName), 尺寸(geometry)、字体 (font) 等众多信息。动作编辑器可以编辑动作。信号/槽编辑器是建立信号 (signal) 和槽 (slot) 之间的联系,资源浏览器则可以编辑软件用到的图标资源。 设计简单的程序界面 ^^^^^^^^^^^^^^^^^^^^^^^ 小白打算写的入门程序很简单。仅仅需要一个 Button,上面写着 "Hello Qt Designer" 即可。小白从左侧的 Widget Box 中找到了Push Button,将它拖到中间的画布中。小白双击新的 Button,写入了 "Hello Qt Designer",以更改 Button 的名字。小白也看了一下属性编辑器,发现这里可以设置 Button 更多的参数,如 geometry, font 等。小白尝试用属性编辑器修改了一下 Button 的大小。 这就是小白需要的最简单的入门界面了。于是,他把设计保存为 HelloQtDesigner.ui,放在了 Qt 文件夹里(如下图)。 .. figure:: ../_static/snap/gui_qt_HelloQtDesigner.png 图2.Hello Qt Deisgner界面 完成入门程序 ^^^^^^^^^^^^^^^^^^^^^^^ Qt Designer 生成的是.ui 文件。小白需要将.ui 转化为.py 文件,才能使用。小白搜索了一下 “python qt designer ui”,又翻了《Rapid GUI Programing with PyQt》这本书,终于找到了编译.ui 为.py 的方法。:: pyuic4 -o ui_HelloQtDesigner.py HelloQtDesigner.ui 最后一步,就是调用这个界面文件,完成程序了。 小白找了一个PyQt的最简单程序,导入刚才生成的界面文件(import ui_HelloQtDesigner),并且让 Hello 类继续界面文件中的类(class Hello(QMainWindow,ui_HelloQtDesigner.Ui_MainWindow))。终于完成了"Hello Qt Designer"。它把下面的代码保存为了HelloQtDesignerGUI.py,放在了qt/HelloQtDesignerGUI.py下面。您也运行一下,看看小白的成就吧。 .. literalinclude:: ../../src/3_guimala/qt/HelloQtDesignerGUI.py 至此,小白了解了用 Qt Designer 编写软件界面的基本流程。对于一些较为复杂的程序,程序员还需要撰写自定义的事件处理函数。比如,我会正在写的通讯录,还需要自定义联系人搜索、新建和删除等函数。我们将会在下面讲述。 三、制作通讯录 ----------------------- 小白要写的通讯录比上面的入门程序更复杂一些。小白需要一个通讯录主界面和新建联系人对话框。通讯录的主界面包含菜单栏、工具栏、联系人搜索条和搜索按钮,搜索结果栏等几个元素。新建联系人对话框主要是文本标签和输入框,以及确认按钮。下面,我们以通讯录的主界面为例,看看小白怎么使用 Qt Designer 设计界面的。 与上一节一样,小白先从templates\forms中建立了一个主窗体(Main Window)。当然,也可以打开原来的 HelloQtDesigner.ui,直接在上面设计界面。 目录栏 ^^^^^^^^^^^^^^^^^^^^^^^ 在新建的窗体左上方,软件目录所在位置,有 “在这里输入”(Type here)的引导文字 (如下图上方所示)。小白双击“在这里输入”的引导文字,输入了“文件”目录。Qt Designer 随后出现了两个“在这里输入”的引导文字(如下图下方所示)。小白随着横向的引导文字,随后输入了“编辑”和“帮助”两个目录。然后在“文件”目录下增加了“退出”动作(Action),在“编辑”目录下增加了“新建”和“删除”动作,在“帮助”目录下增加了“关于作者”动作。小白在建立目录和动作时,不小心输入错误了。他发现,点击鼠标右键,就可以选择移除目录或者动作,然后重新输入。 编辑完目录栏后,小白发现,Qt Designer 右上方的对象查看器中有了许多新的对象,包括刚才建立的目录和动作。不过目录和动作对象的名称都很容易混淆。小白双击对象名称,为所有的目录和动作取了名副其实的对象名。 .. figure:: ../_static/snap/gui_qt_menubar.png 图3. 设计目录栏 工具栏 ^^^^^^^^^^^^^^^^^^^^^^^ 小白在窗体中空白处点击右键,发现了“添加工具栏” (Add Tool Bar)的选项。他点击“添加工具栏”,于是窗体中出现了一个工具栏。小白尝试着把目录中的动作为移动到工具栏中,可是将目录下的动作移到工具栏后,目录下的动作就被删除了。这并不是小白想要的。 小白需要“退出”,“新建”,“删除”这些动作在目录栏和工具栏中同时出现。 应该怎么才能在目录栏和工具栏中同时有同一个动作呢?小白不知道,不过搜索引擎应该知道。小白搜索了“qt designer toolbar”,找到了 Nokia 的 Nokia Qt Designer Manual (Qt的官方教程)中 Adding and Removing Toolbar Buttons‎(添加和删除工具栏按钮)一节。 .. figure:: ../_static/snap/gui_qt_addtoolbar.png 图4. 设计工具栏 资源浏览器 ^^^^^^^^^^^^^^^^^^^^^^^ 小白已经成功地完成了菜单栏和工具栏。不过美中不足的是,小白的菜单和工具栏的按钮都是文字,而不是通常软件使用的图片,比较难看。怎么才能使工具栏呈现图标呢?小白想到了资源浏览器。顾名思义,资源浏览器应该是配置软件使用的图标等资源的工具。小白在Qt Designer主界面(如图一所示)的右下角找到了资源浏览器。(如果您的界面中没有资源浏览器,请点击“视图”菜单,并勾选“资源浏览器”)。 创建和编辑资源文件(Resources file) """""""""""""""""""""""""""""""""""""""""""""" 右下角的资源浏览器如图5所示。资源浏览器中有两个工具按钮,编辑资源文件 .. figure:: ../_static/snap/gui_qt_editiresourceicon.png 和重新载入资源文件 .. figure:: ../_static/snap/gui_qt_reloadresourceicon.png .. figure:: ../_static/snap/gui_qt_resourcebrowser.png 图5. 资源浏览器 小白点击.. figure:: ../_static/snap/gui_qt_editiresourceicon.png ,打开了资源文件编辑窗口(如图6所示)开始编辑资源文件。小白先点击图6左下角“新建资源文件”图片,创建了一个资源文件,并取名为 resources.qrc。然后小白在右侧方框的底部,点击“添加前缀”图标,为所有的图片设计了一个前缀,再点击“添加文件”图片,导入了所有需要的图标(包括help.png, delete.png, exit.gif和new.gif,分别用于帮助 、添加、退出和新建动作。)至此,小白已经完成了创建和编辑资源文件。 .. figure:: ../_static/snap/gui_qt_editresource.png 图6. 资源文件编辑窗口 使用图片资源 """"""""""""""""""""""" 完成编辑资源文件后,小白需要给菜单栏和工具栏中所有的动作增加图标,使它们更美观。小白首先尝试给工具栏中的“新建”按钮增加图标。 他点击选中“新建”按钮,然后在属性编辑器中找到“新建”动作的图标属性(icon) ,如图7所示。小白点击图标属性对应的小拉按钮,点击“选择文件”,然后在弹出的“选择资源“对话框中选择了新建动作的图标(new.gif)。小白点击”确认“后,工具栏中的“新建”动作就自动替换成图标呈现了。 随后,采用同样的方法,小白为所有的动作都设置了图标。 .. figure:: ../_static/snap/gui_qt_useresource.png 图7. 使用资源文件 编译资源文件 """"""""""""""""""""""" 最后,为了让软件能够使用资源文件,小白还需要将资源文件 resources.qrc 编译成 .py 文件。这应该和编译.ui文件类似,不过应该怎么做呢?小白经过搜索,查找 Nokia 和 Riverbank 的在线文档,最终找到了编译资源文件 .qrc 为 .py 的方法 :: pyrcc4 -o qrc_resources.py resources.qrc 其中qrc_resources.py为目标文件,resources.qrc为需要编译的资源文件。 小白在命令行里运行上面的代码后,得到了qrc_resources.py文件。 放置 widgets,完成布局 ^^^^^^^^^^^^^^^^^^^^^^^ 根据 Tkinter 和 wxPython 的学习经验,小白知道他需要一个输入框,一个搜索按钮和一个用于呈现搜索结果的列表框。小白在 Qt Designer 左侧的 Widget Box 找到了形式和名字都恰似他需要的需要的三个元素,分别是 Line Edit, Push Button 和List Widget。小白分别把这三个 widgets 拖放到了中央的设计窗口,然后通过属性编辑器为这三个 widgets 设置了有意义的名字,以便后面调用这些 widgets。小白把这三个 widgets 分别取名为 InputQueryBox,pushButton 和SearchResults。当然您也可以取其它的名字,只要能准备地描述这些 widgets 的用途。 为了软件能够自适应界面大小,小白还增加了两个 Horizontal Spacer。下面就是布局啦。小白将两个 Horizontal Spacer,InputQueryBox (搜索框)和 pushButton(搜索按钮)邻近地水平放置。然后用鼠标同时选取这四个 widgets,然后点击 Qt Designer 工具栏中的水平布局按钮,将四个 widgets 水平放置。(当然,您也可以采用点击Qt Designer菜单栏中的“窗体”->"水平布局"实现)。 最后,小白选取界面中的所有widgets,点击工具栏中的垂直布局按钮,将搜索结果(SearchResults)和搜索条垂直地放置在一起。至此,小白完成了主界面的设计工作。小白把最后的设计保存为AddressBook.ui,并把图放在了图8。您瞅瞅看? .. figure:: ../_static/snap/gui_qt_layoutdesign.png 图8. 设计完成后的软件界面 通讯录除了主界面外,还需要一个新建联系人的对话框。这个对话框比主界面简单多了,不需要菜单和工具栏。只需要几个Label,几个Line Edit, 和两个按钮,分别用于确认和取消。 小白很快地完成了对话框的设计,并把设计保存为CreateNewContact.ui。 编译.ui文件 ^^^^^^^^^^^^^^^^^^^^^^^ 最后,小白需要将两个设计文件,AddressBook.ui 和 CreateNewContact.ui 编译成 .py 文件。这个很简单。小白在入门程序中已经做过了小白在命令行中输入下面的命令,将 AddressBook.ui 编译生成了 ui_AddressBook.py 。 :: pyuic4 -o ui_AddressBook.py AddressBook.ui 采用同样地方法,小白将 CreateNewContact.ui 编译成了 ui_CreateNewContact.py。 导入界面设计,运行一下! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 小白,“终于设计完界面了,运行一下,看看什么样吧?”。运行设计的界面并不难,除了导入的界面设计方案不同,和前面入门程序的代码基本一样。下面是小白更新后的代码,它放在了 qt/LoadUI.py。其中导入的 ui_AddressBook 是通过 pyuic4 生成的。在QtAddressbook类中,小白调用了 self.setupUi(self) ,这个语句会把Qt Designer中设计的界面搬到我们的通讯录中来。此外,这个语句还会调用QtCore.QmetaObject.connectSlotsByName()方法,自动地创建信号 (Signal) 和槽 (slot) 之间的联系,当软件激发某个信号时,就调用PyQt自带的或者用户自定义的事件处理函数。您也运行一下下面的代码吧? .. literalinclude:: ../../src/3_guimala/qt/LoadUI.py 哇!运行后的界面和 Qt Designer 里设计的界面基本上一样。除了看不到 spacer 和 layout manager 那些标记外,其它都完全一样。 下面,我们来实现软件的功能吧。 自定义函数,实现软件功能 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 软件的界面已经完全具备了,小白只需要定义事件处理函数了,包括查询、新增和删除联系人。我们来看看小白怎么实现查询联系人的功能吧。 前面,小白知道了调用 self.setupUi(self) 语句后,会自动地创建信号-槽的联系(Signal-slots connection)。因此,要调用自定义函数,小白只需要以 on_widgetName_signalName 的格式命名自定义函数,就可以成功地建立 widgetName 和 signalName 之间的联系了。以联系人搜索功能为例,小白需要将搜索按钮和他以前在 CLI 通讯录、Tkinter 和 wxPython 都用到过的联系人搜索函数之间建立联系。在用Qt Designer设计界面时,小白将搜索按钮的名字设为pushButton。当用户点击一个按钮时,会激发clicked信号。因此,小白只需要将自定义的搜索函数取名为 on_pushButton_clicked ,就可以成功地建立与搜索按钮(pushButton)之间的联系了。当用户点击搜索按钮后,软件就会自动地调用 on_pushButton_clicked 这个函数了。 小白从前面写的 wxPython 通讯录中找到了联系人搜索函数,将函数名改为 on_pushButton_clicked,然后贴到了上面写的 LoadUI.py 这个程序里。小白注意到,PyQt 和 wxPython 对于列表框(PyQt 中的 QListWidget 和 wxPython 中的 ListBox )的类函数处理是不一样的。他还需要弄明白 PyQt 中怎么重置和增加 QListWidget 的值。小白分别搜索了 “QListWidget clear item” 和 “QListWidget add item” 这个关键词。小白在 Nokia 和 Riverbank 的文档中都找到了self.SearchResults.clear()可以重置 SearchResults 这个 QListWidget 的值。self.SearchResults.addItem()可以为 QListWidget 增加值。就这样,小白在 LoadUI.py 的基础上稍做修改,实现了联系人搜索功能。他把代码放在了qt/QtSignalSlot_SearchContact.py。您也试试看吧? .. literalinclude:: ../../src/3_guimala/qt/QtSignalSlot_SearchContact.py 根据类似的方法,小白找到以前写的自定义函数,略做修改,实现了删除和新建联系人的功能。 至此,小白成功地用 PyQt/Qt Designer ,可视化地方式设计了软件界面,完成了通讯录。小白把主程序放在了qt/QtAddressbookzh.py。 “终于完成通讯录了!”小白欢呼着,“去给MM秀一下!” “纸上得来终觉浅,绝知此事须躬行“。请读者朋友都试运行一下本书附带的源代码吧。另外,PyQt 也可以像 Tkinter 和 wxPython 那样,逐行地代码实现通讯录。 为了更好的学习用 Python 写程序,期望读者可以根据 Tkinter 和 wxPython 的学习过程那样,逐行代码地用 PyQt 构建通讯录。 小白:”我躬行了,用 Python 写软件的确挺简单的!“ ”完!“