PyQt 中如何结合 Qt 设计师进行开发

Qt 设计师是 Qt 的所见即所得的界面设计工具,通过拖拉方式设计界面,但它并不能产生任何代码。

Qt 设计师使用 .ui 后缀的 XML 文件来存储界面内容。通过 pyuic4 命令可以编译成 .py 文件,.py 文件的内容就和我们手写界面的类似。

当然直接使用 .ui 文件也是可以的,但是有两个问题,一个是效率不高,需要在运行时做动态转换创建界面;二是不方便打包发布。这里只讲最佳实践,不讲这种方式,感兴趣的可以自己研究。我后面会写文章讲解。

下面通过一个例子,讲述如何创建界面,并编译,以及如何使用信号和槽。该例子上有一个 QLabel 和一个 QPushButton 。点击按钮,修改标签的文字为 “Hello PyQt4” 。

环境:

创建界面

打开 Qt 设计师,在 templates/forms 中选择 Widget,点击【创建】。进入设计状态,从 窗口部件盒 中拖出两个 Vertical Spacer 、一个 Label ,一个 Push Button 。如图所示:

在空白处点击右键,选择垂直布局。

双击按钮修改文字为 “Say Hello”,选中对象在属性编辑器中修改属性:

完成后的界面如下:

打开 widget.ui ,你会发现是个 XML 文件。

转换界面文件为python代码

通过 pyuic4 命令转换代码,把 widget.ui 转换成 ui_widget.py 文件。

$ pyuic4 -o ui_widget.py widget.ui

转换后代码如下:

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName(_fromUtf8("Form"))
        Form.resize(400, 300)
        self.verticalLayout = QtGui.QVBoxLayout(Form)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
        spacerItem = QtGui.QSpacerItem(20, 98, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.lHello = QtGui.QLabel(Form)
        self.lHello.setObjectName(_fromUtf8("lHello"))
        self.verticalLayout.addWidget(self.lHello)
        self.pbHello = QtGui.QPushButton(Form)
        self.pbHello.setObjectName(_fromUtf8("pbHello"))
        self.verticalLayout.addWidget(self.pbHello)
        spacerItem1 = QtGui.QSpacerItem(20, 98, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem1)

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

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
        self.lHello.setText(QtGui.QApplication.translate("Form", "TextLabel", None, QtGui.QApplication.UnicodeUTF8))
        self.pbHello.setText(QtGui.QApplication.translate("Form", "Say Hello", None, QtGui.QApplication.UnicodeUTF8))

注意到代码中 self.lHelloself.pbHello 变量,他们的名字是和我们前面界面设计的 objectName 是一一对应的。

注意: 不要把业务代码写在 ui_widget.py 文件中,下次使用 pyuic4 命令的时候将会覆盖该文件,你所做的一切工作将白费。

使用转换后的界面代码

这里只介绍使用 Python 的多重继承方式。这种方式有个好处是代码编写方便,而且可以通过 self. + objectName 的方式来调用界面组件。

# -*- coding: utf-8 -*-
from PyQt4 import QtGui
from ui_widget import Ui_Form

class Widget(QtGui.QWidget, Ui_Form):
    """QtGui.QWidget和界面设计时选择的类型一致"""
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setupUi(self) # Ui_Form.setupUi


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    widget = Widget()
    widget.show()
    sys.exit(app.exec_())

处理按钮点击事件

PyQt支持使用 QtCore.pyqtSignature() 装饰器来连接信号和槽,不需要通过手工连接。具体方法名为 on_objectName_信号 ,代码如下:

# -*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
from ui_widget import Ui_Form

class Widget(QtGui.QWidget, Ui_Form):
    """QtGui.QWidget和界面设计时选择的类型一致"""
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setupUi(self) # Ui_Form.setupUi

    @QtCore.pyqtSignature("")
    def on_pbHello_clicked(self):
        """pbHello和界面设计时的objectName一致"""
        self.lHello.setText('Hello PyQt4') # lHello和界面设计的objectName一致

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    widget = Widget()
    widget.show()
    sys.exit(app.exec_())

完整代码下载:hellopyqt.zip