跳到内容

Python 编程/PyQt4

来自维基教科书,开放的书籍,开放的世界


警告:本页面上的示例混合了 PyQt3 和 PyQt4 - 请谨慎使用!

本教程旨在提供一个动手指南,教你学习用 Python 构建小型 Qt4 应用程序的基础知识。

要学习本教程,你需要具备基本的 Python 知识。然而,不需要 Qt4 方面的知识。我在这些示例中使用的是 Linux,并假设你已经安装了 Python 和 PyQt4。为了测试这一点,打开一个 Python shell(在控制台中输入“Python”以启动交互式解释器)并输入

>>> import PyQt4

如果没有出现错误消息,说明你已经准备就绪。

本教程中的示例尽可能简单,展示了编写和构建程序的实用方法。阅读示例文件中的源代码非常重要,大部分解释都在代码中。熟悉 PyQt 的最佳方法是玩示例并尝试更改它们。

你好,世界!

[编辑 | 编辑源代码]

让我们从一个简单的例子开始:弹出一个窗口并显示一些内容。以下小程序将弹出一个显示“你好,世界!”的窗口。

 #!/usr/bin/env python
 
 import sys
 from PyQt4 import Qt
 
 # We instantiate a QApplication passing the arguments of the script to it:
 a = Qt.QApplication(sys.argv)
 
 # Add a basic widget to this application:
 # The first argument is the text we want this QWidget to show, the second
 # one is the parent widget. Since Our "hello" is the only thing we use (the 
 # so-called "MainWidget", it does not have a parent.
 hello = Qt.QLabel("Hello, World")
 
 # ... and that it should be shown.
 hello.show()
 
 # Now we can start it.
 a.exec_()

大约 7 行代码,这就是最简单的实现方式。

一个按钮

[编辑 | 编辑源代码]

让我们添加一些交互性!我们将用一个按钮替换显示“你好,世界!”的标签,并为其分配一个操作。这个分配是通过将一个信号(按钮被按下时发送的事件)连接到一个(一个操作,通常是一个在事件发生时运行的函数)来实现的。

#!/usr/bin/env python

import sys
from PyQt4 import Qt

a = Qt.QApplication(sys.argv)

# Our function to call when the button is clicked
def sayHello():
    print ("Hello, World!")

# Instantiate the button
hellobutton = Qt.QPushButton("Say 'Hello world!'")

# And connect the action "sayHello" to the event "button has been clicked"
hellobutton.clicked.connect(sayHello)

# The rest is known already...
#a.setMainWidget(hellobutton)
hellobutton.show()
a.exec_()

你可以想象,用这种方式编码不可扩展,也不是你想要继续使用的方式。因此,让我们使它更 Pythonic,添加结构并实际使用面向对象编程。我们创建自己的应用程序类,继承自 QApplication,并将应用程序的自定义内容放到它的方法中:一个方法来构建小部件,一个槽,它包含在接收到信号时执行的代码。

#!/usr/bin/env python

import sys
from PyQt4 import Qt

class HelloApplication(Qt.QApplication):
    def __init__(self, args):
        """ In the constructor we're doing everything to get our application
            started, which is basically constructing a basic QApplication by 
            its __init__ method, then adding our widgets and finally starting 
            the exec_loop."""
        Qt.QApplication.__init__(self, args)
        self.addWidgets()

    def addWidgets(self):
        """ In this method, we're adding widgets and connecting signals from 
            these widgets to methods of our class, the so-called "slots" 
        """
        self.hellobutton = Qt.QPushButton("Say 'Hello world!'")
        self.hellobutton.clicked.connect(self.slotSayHello)
        self.hellobutton.show()

    def slotSayHello(self):
        """ This is an example slot, a method that gets called when a signal is 
            emitted """
        print ("Hello, World!")


# Only actually do something if this script is run standalone, so we can test our 
# application, but we're also able to import this program without actually running
# any code.
if __name__ == "__main__":
    app = HelloApplication(sys.argv)
    app.exec_()

GUI 编码

[编辑 | 编辑源代码]

… 所以我们想要使用 Qt3 Designer 来创建我们的 GUI。在图片中,你可以看到一个简单的 GUI,其中用绿色字母标注了小部件的名称。我们要做的是:我们将 Qt Designer 中的 .ui 文件编译成一个 Python 类,我们继承该类并将其用作我们的 mainWidget。这样,我们就能从 Qt Designer 中更改用户界面,而不用担心代码中的更改。

pyuic4 testapp_ui.ui -o testapp_ui.py

生成一个我们可以使用的 Python 文件。

程序的工作方式可以描述如下:我们填写 lineedit,点击添加按钮将连接到一个方法,该方法从 lineedit 中读取文本,用它创建一个 listviewitem 并将其添加到 listview。点击删除按钮将从 listview 中删除当前选定的项目。以下是包含大量注释的代码(仅在 PyQt3 中有效)

#!/usr/bin/env python
 
from testapp_ui import TestAppUI
from qt import *
import sys
 
class HelloApplication(QApplication):
    def __init__(self, args):
        """ In the constructor we're doing everything to get our application
            started, which is basically constructing a basic QApplication by 
            its __init__ method, then adding our widgets and finally starting 
            the exec_loop."""
        QApplication.__init__(self, args)
        
        # We pass None since it's the top-level widget, we could in fact leave 
        # that one out, but this way it's easier to add more dialogs or widgets.
        self.maindialog = TestApp(None)
        
        self.setMainWidget(self.maindialog)
        self.maindialog.show()
        self.exec_loop()     


class TestApp(TestAppUI):
    def __init__(self, parent):
        # Run the parent constructor and connect the slots to methods.
        TestAppUI.__init__(self, parent)
        self._connectSlots()

        # The listview is initially empty, so the deletebutton will have no effect,
        # we grey it out.
        self.deletebutton.setEnabled(False)

    def _connectSlots(self):
        # Connect our two methods to SIGNALS the GUI emits.
        self.addbutton.clicked.connect(self._slotAddClicked)
        self.deletebutton.clicked.connect(self._slotDeleteClicked)

    def _slotAddClicked(self):
        # Read the text from the lineedit,
        text = self.lineedit.text()
        # if the lineedit is not empty,
        if len(text):
            # insert a new listviewitem ...
            lvi = QListViewItem(self.listview)
            # with the text from the lineedit and ...
            lvi.setText(0,text)
            # clear the lineedit.
            self.lineedit.clear()

            # The deletebutton might be disabled, since we're sure that there's now
            # at least one item in it, we enable it.
            self.deletebutton.setEnabled(True)

    def _slotDeleteClicked(self):
        # Remove the currently selected item from the listview.
        self.listview.takeItem(self.listview.currentItem())

        # Check if the list is empty - if yes, disable the deletebutton.
        if self.listview.childCount() == 0:
            self.deletebutton.setEnabled(False)


if __name__ == "__main__":
    app = HelloApplication(sys.argv)

这段代码也非常有用,它可以在 PyQt4 上运行,并且有很多有用的选项

#!/usr/bin/env python
# Copyright (c) 2008-10 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 2 of the License, or
# version 3 of the License, or (at your option) any later version. It is
# provided for educational purposes and is distributed in the hope that
# it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
#
#
#   Versions
#
# 1.0.1 Fixed bug reported by Brian Downing where paths that contained
#       spaces were not handled correctly.
# 1.0.2 Fixed bug reported by Ben Thompson that if the UIC program
#       fails, no problem was reported; I try to report one now.
# 1.1.0 Added Remember path option; if checked the program starts with
#       the last used path, otherwise with the current directory, unless
#       overridden on the command line
# 1.1.1 Changed default path on Windows to match PyQt 4.4
# 1.2.1 Changed import style + bug fixes
# 1.2.2 Added stderr to error message output as per Michael Jackson's
#       suggestion
# 1.2.3 Tried to make the paths work on Mac OS X
# 1.2.4 Added more options
# 1.2.5 Use "new-style" connections (src.signal.connect(target.slot) instead of
#       src.connect(src, SIGNAL("signal()"), target.slot)) and improve PEP-8
#       compliance

from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from future_builtins import *

import os
import platform
import stat
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

__version__ = "1.2.5"

Windows = sys.platform.lower().startswith(("win", "microsoft"))

class OptionsForm(QDialog):
    def __init__(self, parent=None):
        super(OptionsForm, self).__init__(parent)

        settings = QSettings()
        if sys.platform.startswith("darwin"):
            pyuic4Label = QLabel("pyuic4 (pyuic.py)")
        else:
            pyuic4Label = QLabel("pyuic4")
        self.pyuic4Label = QLabel(settings.value("pyuic4",
                QVariant(PYUIC4)).toString())
        self.pyuic4Label.setFrameStyle(QFrame.StyledPanel|
                                       QFrame.Sunken)
        pyuic4Button = QPushButton("py&uic4...")
        pyrcc4Label = QLabel("pyrcc4")
        self.pyrcc4Label = QLabel(settings.value("pyrcc4",
                QVariant(PYRCC4)).toString())
        self.pyrcc4Label.setFrameStyle(QFrame.StyledPanel|
                                       QFrame.Sunken)
        pyrcc4Button = QPushButton("p&yrcc4...")
        pylupdate4Label = QLabel("pylupdate4")
        self.pylupdate4Label = QLabel(settings.value("pylupdate4",
                QVariant(PYLUPDATE4)).toString())
        self.pylupdate4Label.setFrameStyle(QFrame.StyledPanel|
                                           QFrame.Sunken)
        pylupdate4Button = QPushButton("&pylupdate4...")
        lreleaseLabel = QLabel("lrelease")
        self.lreleaseLabel = QLabel(settings.value("lrelease",
                QVariant("lrelease")).toString())
        self.lreleaseLabel.setFrameStyle(QFrame.StyledPanel|
                                         QFrame.Sunken)
        lreleaseButton = QPushButton("&lrelease...")
        toolPathGroupBox = QGroupBox("Tool Paths")

        pathsLayout = QGridLayout()
        pathsLayout.addWidget(pyuic4Label, 0, 0)
        pathsLayout.addWidget(self.pyuic4Label, 0, 1)
        pathsLayout.addWidget(pyuic4Button, 0, 2)
        pathsLayout.addWidget(pyrcc4Label, 1, 0)
        pathsLayout.addWidget(self.pyrcc4Label, 1, 1)
        pathsLayout.addWidget(pyrcc4Button, 1, 2)
        pathsLayout.addWidget(pylupdate4Label, 2, 0)
        pathsLayout.addWidget(self.pylupdate4Label, 2, 1)
        pathsLayout.addWidget(pylupdate4Button, 2, 2)
        pathsLayout.addWidget(lreleaseLabel, 3, 0)
        pathsLayout.addWidget(self.lreleaseLabel, 3, 1)
        pathsLayout.addWidget(lreleaseButton, 3, 2)
        toolPathGroupBox.setLayout(pathsLayout)

        resourceModuleNamesGroupBox = QGroupBox(
                "Resource Module Names")
        qrcFiles = bool(int(settings.value("qrc_resources", "1").toString()))
        self.qrcRadioButton = QRadioButton("&qrc_file.py")
        self.qrcRadioButton.setChecked(qrcFiles)
        self.rcRadioButton = QRadioButton("file_&rc.py")
        self.rcRadioButton.setChecked(not qrcFiles)

        radioLayout = QHBoxLayout()
        radioLayout.addWidget(self.qrcRadioButton)
        radioLayout.addWidget(self.rcRadioButton)
        resourceModuleNamesGroupBox.setLayout(radioLayout)

        self.pyuic4xCheckBox = QCheckBox("Run pyuic4 with -&x "
                " to make forms stand-alone runable")
        x = bool(int(settings.value("pyuic4x", "0").toString()))
        self.pyuic4xCheckBox.setChecked(x)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|
                                     QDialogButtonBox.Cancel)

        layout = QVBoxLayout()
        layout.addWidget(toolPathGroupBox)
        layout.addWidget(resourceModuleNamesGroupBox)
        layout.addWidget(self.pyuic4xCheckBox)
        layout.addWidget(buttonBox)
        self.setLayout(layout)

        pyuic4Button.clicked.connect(lambda: self.setPath("pyuic4"))
        pyrcc4Button.clicked.connect(lambda: self.setPath("pyrcc4"))
        pylupdate4Button.clicked.connect(lambda: self.setPath("pylupdate4"))
        lreleaseButton.clicked.connect(lambda: self.setPath("lrelease"))
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

        self.setWindowTitle("Make PyQt - Options")

    def accept(self):
        settings = QSettings()
        settings.setValue("pyuic4", QVariant(self.pyuic4Label.text()))
        settings.setValue("pyrcc4", QVariant(self.pyrcc4Label.text()))
        settings.setValue("pylupdate4",
                QVariant(self.pylupdate4Label.text()))
        settings.setValue("lrelease", QVariant(self.lreleaseLabel.text()))
        settings.setValue("qrc_resources",
                "1" if self.qrcRadioButton.isChecked() else "0")
        settings.setValue("pyuic4x",
                "1" if self.pyuic4xCheckBox.isChecked() else "0")
        QDialog.accept(self)

    def setPath(self, tool):
        if tool == "pyuic4":
            label = self.pyuic4Label
        elif tool == "pyrcc4":
            label = self.pyrcc4Label
        elif tool == "pylupdate4":
            label = self.pylupdate4Label
        elif tool == "lrelease":
            label = self.lreleaseLabel
        path = QFileDialog.getOpenFileName(self,
                "Make PyQt - Set Tool Path", label.text())
        if path:
            label.setText(QDir.toNativeSeparators(path))


class Form(QMainWindow):
    def __init__(self):
        super(Form, self).__init__()

        pathLabel = QLabel("Path:")
        settings = QSettings()
        rememberPath = settings.value("rememberpath",
                QVariant(True if Windows else False)).toBool()
        if rememberPath:
            path = (unicode(settings.value("path").toString()) or
                    os.getcwd())
        else:
            path = (sys.argv[1] if len(sys.argv) > 1 and
                    QFile.exists(sys.argv[1]) else os.getcwd())
        self.pathLabel = QLabel(path)
        self.pathLabel.setFrameStyle(QFrame.StyledPanel|
                                     QFrame.Sunken)
        self.pathLabel.setToolTip("The relative path; all actions will "
                "take place here,<br>and in this path's subdirectories "
                "if the Recurse checkbox is checked")
        self.pathButton = QPushButton("&Path...")
        self.pathButton.setToolTip(self.pathLabel.toolTip().replace(
                "The", "Sets the"))
        self.recurseCheckBox = QCheckBox("&Recurse")
        self.recurseCheckBox.setToolTip("Clean or build all the files "
                "in the path directory,<br>and all its subdirectories, "
                "as deep as they go.")
        self.transCheckBox = QCheckBox("&Translate")
        self.transCheckBox.setToolTip("Runs <b>pylupdate4</b> on all "
                "<tt>.py</tt> and <tt>.pyw</tt> files in conjunction "
                "with each <tt>.ts</tt> file.<br>Then runs "
                "<b>lrelease</b> on all <tt>.ts</tt> files to produce "
                "corresponding <tt>.qm</tt> files.<br>The "
                "<tt>.ts</tt> files must have been created initially by "
                "running <b>pylupdate4</b><br>directly on a <tt>.py</tt> "
                "or <tt>.pyw</tt> file using the <tt>-ts</tt> option.")
        self.debugCheckBox = QCheckBox("&Dry Run")
        self.debugCheckBox.setToolTip("Shows the actions that would "
                "take place but does not do them.")
        self.logBrowser = QTextBrowser()
        self.logBrowser.setLineWrapMode(QTextEdit.NoWrap)
        self.buttonBox = QDialogButtonBox()
        menu = QMenu(self)
        optionsAction = menu.addAction("&Options...")
        self.rememberPathAction = menu.addAction("&Remember path")
        self.rememberPathAction.setCheckable(True)
        self.rememberPathAction.setChecked(rememberPath)
        aboutAction = menu.addAction("&About")
        moreButton = self.buttonBox.addButton("&More",
                QDialogButtonBox.ActionRole)
        moreButton.setMenu(menu)
        moreButton.setToolTip("Use <b>More-&gt;Tool paths</b> to set the "
                "paths to the tools if they are not found by default")
        self.buildButton = self.buttonBox.addButton("&Build",
                QDialogButtonBox.ActionRole)
        self.buildButton.setToolTip("Runs <b>pyuic4</b> on all "
                "<tt>.ui</tt> "
                "files and <b>pyrcc4</b> on all <tt>.qrc</tt> files "
                "that are out-of-date.<br>Also runs <b>pylupdate4</b> "
                "and <b>lrelease</b> if the Translate checkbox is "
                "checked.")
        self.cleanButton = self.buttonBox.addButton("&Clean",
                QDialogButtonBox.ActionRole)
        self.cleanButton.setToolTip("Deletes all <tt>.py</tt> files that "
                "were generated from <tt>.ui</tt> and <tt>.qrc</tt> "
                "files,<br>i.e., all files matching <tt>qrc_*.py</tt>, "
                "<tt>*_rc.py</tt> and <tt>ui_*.py.")
        quitButton = self.buttonBox.addButton("&Quit",
                QDialogButtonBox.RejectRole)

        topLayout = QHBoxLayout()
        topLayout.addWidget(pathLabel)
        topLayout.addWidget(self.pathLabel, 1)
        topLayout.addWidget(self.pathButton)
        bottomLayout = QHBoxLayout()
        bottomLayout.addWidget(self.recurseCheckBox)
        bottomLayout.addWidget(self.transCheckBox)
        bottomLayout.addWidget(self.debugCheckBox)
        bottomLayout.addStretch()
        bottomLayout.addWidget(self.buttonBox)
        layout = QVBoxLayout()
        layout.addLayout(topLayout)
        layout.addWidget(self.logBrowser)
        layout.addLayout(bottomLayout)
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        aboutAction.triggered.connect(self.about)
        optionsAction.triggered.connect(self.setOptions)
        self.pathButton.clicked.connect(self.setPath)
        self.buildButton.clicked.connect(self.build)
        self.cleanButton.clicked.connect(self.clean)
        quitButton.clicked.connect(self.close)

        self.setWindowTitle("Make PyQt")

    def closeEvent(self, event):
        settings = QSettings()
        settings.setValue("rememberpath",
                QVariant(self.rememberPathAction.isChecked()))
        settings.setValue("path", QVariant(self.pathLabel.text()))
        event.accept()

    def about(self):
        QMessageBox.about(self, "About Make PyQt",
                """<b>Make PyQt</b> v {0}
                <p>Copyright &copy; 2007-10 Qtrac Ltd. 
                All rights reserved.
                <p>This application can be used to build PyQt
                applications.
                It runs pyuic4, pyrcc4, pylupdate4, and lrelease as
                required, although pylupdate4 must be run directly to
                create the initial .ts files.
                <p>Python {1} - Qt {2} - PyQt {3} on {4}""".format(
                __version__, platform.python_version(),
                QT_VERSION_STR, PYQT_VERSION_STR,
                platform.system()))

    def setPath(self):
        path = QFileDialog.getExistingDirectory(self,
                "Make PyQt - Set Path", self.pathLabel.text())
        if path:
            self.pathLabel.setText(QDir.toNativeSeparators(path))

    def setOptions(self):
        dlg = OptionsForm(self)
        dlg.exec_()

    def build(self):
        self.updateUi(False)
        self.logBrowser.clear()
        recurse = self.recurseCheckBox.isChecked()
        path = unicode(self.pathLabel.text())
        self._apply(recurse, self._build, path)
        if self.transCheckBox.isChecked():
            self._apply(recurse, self._translate, path)
        self.updateUi(True)

    def clean(self):
        self.updateUi(False)
        self.logBrowser.clear()
        recurse = self.recurseCheckBox.isChecked()
        path = unicode(self.pathLabel.text())
        self._apply(recurse, self._clean, path)
        self.updateUi(True)

    def updateUi(self, enable):
        for widget in (self.buildButton, self.cleanButton,
                self.pathButton, self.recurseCheckBox,
                self.transCheckBox, self.debugCheckBox):
            widget.setEnabled(enable)
        if not enable:
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        else:
            QApplication.restoreOverrideCursor()
            self.buildButton.setFocus()

    def _apply(self, recurse, function, path):
        if not recurse:
            function(path)
        else:
            for root, dirs, files in os.walk(path):
                for dir in sorted(dirs):
                    function(os.path.join(root, dir))

    def _make_error_message(self, command, process):
        err = ""
        ba = process.readAllStandardError()
        if not ba.isEmpty():
            err = ": " + str(QString(ba))
        return "<span style="color:red;">FAILED: %s%s</span>" % (command, err)

    def _build(self, path):
        settings = QSettings()
        pyuic4 = unicode(settings.value("pyuic4",
                                        QVariant(PYUIC4)).toString())
        pyrcc4 = unicode(settings.value("pyrcc4",
                                        QVariant(PYRCC4)).toString())
        prefix = unicode(self.pathLabel.text())
        pyuic4x = bool(int(settings.value("pyuic4x", "0").toString()))
        if not prefix.endswith(os.sep):
            prefix += os.sep
        failed = 0
        process = QProcess()
        for name in os.listdir(path):
            source = os.path.join(path, name)
            target = None
            if source.endswith(".ui"):
                target = os.path.join(path,
                                    "ui_" + name.replace(".ui", ".py"))
                command = pyuic4
            elif source.endswith(".qrc"):
                if bool(int(settings.value("qrc_resources", "1").toString())):
                    target = os.path.join(path,
                                        "qrc_" + name.replace(".qrc", ".py"))
                else:
                    target = os.path.join(path, name.replace(".qrc", "_rc.py"))
                command = pyrcc4
            if target is not None:
                if not os.access(target, os.F_OK) or (
                   os.stat(source)[stat.ST_MTIME] >
                   os.stat(target)[stat.ST_MTIME]):
                    args = ["-o", target, source]
                    if command == PYUIC4 and pyuic4x:
                        args.insert(0, "-x")
                    if (sys.platform.startswith("darwin") and
                        command == PYUIC4):
                        command = sys.executable
                        args = [PYUIC4] + args
                    msg = ("converted <span style="color:darkblue;">" + source +
                           "</span> to <span style="color:blue;">" + target +
                           "</span>")
                    if self.debugCheckBox.isChecked():
                        msg = "<span style="color:green;"># " + msg + "</span>"
                    else:
                        process.start(command, args)
                        if (not process.waitForFinished(2 * 60 * 1000) or
                            not QFile.exists(target)):
                            msg = self._make_error_message(command,
                                                           process)
                            failed += 1
                    self.logBrowser.append(msg.replace(prefix, ""))
                else:
                    self.logBrowser.append("<span style="color:green;">"
                            "# {0} is up-to-date</span>".format(
                            source.replace(prefix, "")))
                QApplication.processEvents()
        if failed:
            QMessageBox.information(self, "Make PyQt - Failures",
                    "Try manually setting the paths to the tools "
                    "using <b>More-&gt;Options</b>")

    def _clean(self, path):
        prefix = unicode(self.pathLabel.text())
        if not prefix.endswith(os.sep):
            prefix += os.sep
        deletelist = []
        for name in os.listdir(path):
            target = os.path.join(path, name)
            source = None
            if (target.endswith(".py") or target.endswith(".pyc") or
                target.endswith(".pyo")):
                if name.startswith("ui_") and not name[-1] in "oc":
                    source = os.path.join(path, name[3:-3] + ".ui")
                elif name.startswith("qrc_"):
                    if target[-1] in "oc":
                        source = os.path.join(path, name[4:-4] + ".qrc")
                    else:
                        source = os.path.join(path, name[4:-3] + ".qrc")
                elif name.endswith(("_rc.py", "_rc.pyo", "_rc.pyc")):
                    if target[-1] in "oc":
                        source = os.path.join(path, name[:-7] + ".qrc")
                    else:
                        source = os.path.join(path, name[:-6] + ".qrc")
                elif target[-1] in "oc":
                    source = target[:-1]
                if source is not None:
                    if os.access(source, os.F_OK):
                        if self.debugCheckBox.isChecked():
                            self.logBrowser.append("<span style="color:green;">"
                                    "# delete {0}</span>".format(
                                    target.replace(prefix, "")))
                        else:
                            deletelist.append(target)
                    else:
                        self.logBrowser.append("<span style="color:darkred;">"
                                "will not remove "
                                "'{0}' since `{1}' not found</span>"
                                .format(target.replace(prefix, ""),
                                source.replace(prefix, "")))
        if not self.debugCheckBox.isChecked():
            for target in deletelist:
                self.logBrowser.append("deleted "
                        "<span style="color:red;">{0}</span>".format(
                        target.replace(prefix, "")))
                os.remove(target)
                QApplication.processEvents()

    def _translate(self, path):
        prefix = unicode(self.pathLabel.text())
        if not prefix.endswith(os.sep):
            prefix += os.sep
        files = []
        tsfiles = []
        for name in os.listdir(path):
            if name.endswith((".py", ".pyw")):
                files.append(os.path.join(path, name))
            elif name.endswith(".ts"):
                tsfiles.append(os.path.join(path, name))
        if not tsfiles:
            return
        settings = QSettings()
        pylupdate4 = unicode(settings.value("pylupdate4",
                             QVariant(PYLUPDATE4)).toString())
        lrelease = unicode(settings.value("lrelease",
                           QVariant(LRELEASE)).toString())
        process = QProcess()
        failed = 0
        for ts in tsfiles:
            qm = ts[:-3] + ".qm"
            command1 = pylupdate4
            args1 = files + ["-ts", ts]
            command2 = lrelease
            args2 = ["-silent", ts, "-qm", qm]
            msg = "updated <span style="color:blue;">{0}</span>".format(
                    ts.replace(prefix, ""))
            if self.debugCheckBox.isChecked():
                msg = "<span style="color:green;"># {0}</span>".format(msg)
            else:
                process.start(command1, args1)
                if not process.waitForFinished(2 * 60 * 1000):
                    msg = self._make_error_message(command1, process)
                    failed += 1
            self.logBrowser.append(msg)
            msg = "generated <span style="color:blue;">{0}</span>".format(
                    qm.replace(prefix, ""))
            if self.debugCheckBox.isChecked():
                msg = "<span style="color:green;"># {0}</span>".format(msg)
            else:
                process.start(command2, args2)
                if not process.waitForFinished(2 * 60 * 1000):
                    msg = self._make_error_message(command2, process)
                    failed += 1
            self.logBrowser.append(msg)
            QApplication.processEvents()
        if failed:
            QMessageBox.information(self, "Make PyQt - Failures",
                    "Try manually setting the paths to the tools "
                    "using <b>More-&gt;Options</b>")


app = QApplication(sys.argv)
PATH = unicode(app.applicationDirPath())
if Windows:
    PATH = os.path.join(os.path.dirname(sys.executable),
                        "Lib/site-packages/PyQt4")
    if os.access(os.path.join(PATH, "bin"), os.R_OK):
        PATH = os.path.join(PATH, "bin")
if sys.platform.startswith("darwin"):
    i = PATH.find("Resources")
    if i > -1:
        PATH = PATH[:i] + "bin"
PYUIC4 = os.path.join(PATH, "pyuic4")
if sys.platform.startswith("darwin"):
    PYUIC4 = os.path.dirname(sys.executable)
    i = PYUIC4.find("Resources")
    if i > -1:
        PYUIC4 = PYUIC4[:i] + "Lib/python2.6/site-packages/PyQt4/uic/pyuic.py"
PYRCC4 = os.path.join(PATH, "pyrcc4")
PYLUPDATE4 = os.path.join(PATH, "pylupdate4")
LRELEASE = "lrelease"
if Windows:
    PYUIC4 = PYUIC4.replace("/", "\\") + ".bat"
    PYRCC4 = PYRCC4.replace("/", "\\") + ".exe"
    PYLUPDATE4 = PYLUPDATE4.replace("/", "\\") + ".exe"
app.setOrganizationName("Qtrac Ltd.")
app.setOrganizationDomain("qtrac.eu")
app.setApplicationName("Make PyQt")
if len(sys.argv) > 1 and sys.argv[1] == "-c":
    settings = QSettings()
    settings.setValue("pyuic4", QVariant(PYUIC4))
    settings.setValue("pyrcc4", QVariant(PYRCC4))
    settings.setValue("pylupdate4", QVariant(PYLUPDATE4))
    settings.setValue("lrelease", QVariant(LRELEASE))
form = Form()
form.show()
app.exec_()

有用的知识

[编辑 | 编辑源代码]

在 Qt Designer 中创建 GUI 不仅使创建 GUI 更容易,而且也是一个很好的学习工具。你可以测试小部件的外观,查看 Qt 中有哪些可用的小部件,以及查看你可能想要使用的属性。

C++ API 文档也是在使用 PyQt 时非常有用(必须使用)的工具。API 翻译非常直接,使用一段时间后,你会发现开发者 API 文档是你真正需要的工具之一。在 KDE 环境中,konqueror 的默认快捷键是 qt:[widgetname],因此使用 [alt]+[F2],输入“qt:qbutton”会直接带你到正确的 API 文档页面。Trolltech 的文档部分还有更多文档,你可能想看看。

本教程中的前 3 个示例是使用 PyQt4 创建的,最后一个示例使用了仅在 PyQt3 中有效的语法。

注意:本页面的先前版本(适用于 pyqt3)在 http://vizzzion.org/?id=pyqt 中可用。

本文档是在 GNU 自由文档许可证 下发布的。

华夏公益教科书