以前Python写出来的脚本都是靠命令行执行,输入要是复杂点就得靠加参数,甚至加配置文件来搞定,自己用下还行,给别人用的时候总是不太方便,就一直想找个好用的图形界面库。

有试过Python内置的Tkinter,部署倒是挺简单,不需要额外安装库,但是开发效率还是不够高,而且需要手写不少界面相关的重复代码,用起来不太方便。

最近开始尝试第三方的界面库,几经权衡,最终在PyGTK、wxPython、PyQT中选择了PyQT,试了一下,确实很不错,下面就介绍一下PyQT的使用方法。

1 介绍

PyQT以QT为基础,跨平台(可以运行在Windows、Linux/Unix、MacOS等操作系统上),同时PyQT的界面可以直接用QT Designer做出来,可以很容易移植到其它语言上(C++、C#、Java、Perl等)。

2 例子

为了更好了了解PyQT,先写一段简单的示例代码,例子中通过点击按钮,来改变文本框所显示的内容,具体代码含义直接通过注释说明了。

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

import sys
from PyQt4 import QtCore, QtGui

# 自定义的窗口类
class TestWindow(QtGui.QWidget):
    # 窗口初始化
    def __init__(self, parent = None):
        super(TestWindow, self).__init__(parent)
        self.setWindowTitle(u'胡桃夹子')

        # 创建按钮
        self.pushButton = QtGui.QPushButton(u'测试按钮')

        # 创建文本框
        self.textEdit = QtGui.QTextEdit()

        # 创建垂直布局
        layout = QtGui.QVBoxLayout()

        # 将控件添加到布局中
        layout.addWidget(self.textEdit)
        layout.addWidget(self.pushButton)

        # 设置窗口布局
        self.setLayout(layout)

        # 设置按钮单击动作
        self.pushButton.clicked.connect(self.sayHello)

    # 按钮动作处理
    def sayHello(self):
        self.textEdit.setText('Hello World!')

# 程序主入口
if __name__=='__main__':
    app = QtGui.QApplication(sys.argv)
    mainWindow = TestWindow()
    mainWindow.show()
    sys.exit(app.exec_())

源文件下载:链接

运行效果如下图,点击按钮后,文本框中会显示Hello World!字符串:

Example of PyQT

3 设计界面

了解了PyQT之后,设计界面的工作就可以交给QT设计器来完成,不再需要手写界面代码,最大限度的提高界面开发速度。下面通过QT设计器的方式再完成一次上面例子中的功能。

在QT设计器中,新建一个Dialog类型的窗体,然后向窗体中分别加入Vertical LayoutPush ButtonText Edit三个控件,并界面摆成下图的样子:

Example of QT Designer for PyQT

还可以通过设置各控件的属性来修改界面显示的文字、效果等,此处不过多叙述,下面将设计好的界面保存为qt_designer_example.ui文件,文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="windowModality">
   <enum>Qt::NonModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>280</width>
    <height>240</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QWidget" name="verticalLayoutWidget">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>10</y>
     <width>258</width>
     <height>221</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QTextEdit" name="textEdit"/>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

源文件下载:链接

可以看出,QT设计器所保存的界面是XML文件格式的,事实上,QT设计器所生成的界面可以很自由的移植到不同语言中,甚至不需要太多额外的适配工作,非常方便。

4 生成代码

有了QT界面,下一步就需要用pyuic这个工具来将QT界面转换为Python代码(基于PyQT库),转换命令如下:

pyuic4 -x -o qt_designer_example.py qt_designer_example.ui

其中-o qt_designer_example.py用来标识输出文件名;这里需要特别说明的是-x参数,该参数表示是否生成额外的测试代码来显示窗口,建议加上该参数,这样的话可以方便直接调试运行。

下面是通过pyuic工具生成的Python代码:

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

# Form implementation generated from reading ui file 'qt_designer_example.ui'
#
# Created: Wed Dec 18 22:52:55 2013
#      by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName(_fromUtf8("Dialog"))
        Dialog.setWindowModality(QtCore.Qt.NonModal)
        Dialog.resize(279, 262)
        self.verticalLayoutWidget = QtGui.QWidget(Dialog)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 20, 258, 221))
        self.verticalLayoutWidget.setObjectName(_fromUtf8("verticalLayoutWidget"))
        self.verticalLayout = QtGui.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setMargin(0)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        self.textEdit = QtGui.QTextEdit(self.verticalLayoutWidget)
        self.textEdit.setObjectName(_fromUtf8("textEdit"))
        self.verticalLayout.addWidget(self.textEdit)
        self.pushButton = QtGui.QPushButton(self.verticalLayoutWidget)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.verticalLayout.addWidget(self.pushButton)

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        Dialog.setWindowTitle(_translate("Dialog", "Dialog", None))
        self.pushButton.setText(_translate("Dialog", "PushButton", None))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    Dialog = QtGui.QDialog()
    ui = Ui_Dialog()
    ui.setupUi(Dialog)
    Dialog.show()
    sys.exit(app.exec_())

源文件下载:链接

5 处理事件

QT采用的是Signal(信号)和Slot(槽)的机制来处理事件,Signal负责发送消息来触发事件动作,Slot负责接收消息并进行相应的事件处理(在PyQT中,Signal和Slot可以是任何类型)。

有两种方式可以连接Signal和Slot:

方法一:传统QT的连接方式

self.connect(self.sender, SIGNAL("sndSignal(int)"), self.receiver, SLOT("rcvSlot(int)"))

方法二:适用于Python的连接方式

self.sndSignal.connect(self.receiver.rcvSlot)

本例中我们在Ui_Dialog类的setupUi函数中增加Signal/Slot的连接,然后再增加一个sayHello的事件(Slot)处理函数,先来看下方法一的连接方式:

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        ... ...
        QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL("clicked()"), self.sayHello)

    def sayHello(self):
        self.textEdit.setText('Hello World!')

下面是方法二的连接方式:

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        ... ...
        self.pushButton.clicked.connect(self.sayHello)

    def sayHello(self):
        self.textEdit.setText('Hello World!')

可以看出方法二的连接方式使用更简单,也更符合Python的编程风格。

6 后记

完成上述步骤后,就可以运行Python脚本了,运行后界面功能和开头中的例子基本相同,只不过这个是用QT设计器辅助完成的,相比手写界面代码来说,效率高了不少。


Comments