PyQt

PyQt是Python語言的GUI編程解決方案之一。可以用來代替Python內置的Tkinter。其它替代者還有PyGTK、wxPython等。與Qt一樣，PyQt是一個自由軟體。PyQt是PyKDE的基礎。

PyQt的開發者是英國的「Riverbank Computing」公司。與4.5版本之前的Qt一樣，它提供了GPL與商業協議兩種授權方式，因此它可以免費地用於自由軟體的開發。不過目前尚不提供LGPL授權方式。PyQt可以運行於Microsoft Windows、Mac OS X、Linux以及Unix的多數變種上。

2009年8月，Qt的開發公司諾基亞發布PySide，提供與PyQt類似的功能，但提供了LGPL授權。主要原因是「Riverbank Computing」不願以LGPL授權發布PyQt。

自4.5版本以後，PyQt同時支持Python 2.x與Python 3.x。但是在API方面有所區別，最主要的是運行在Python 3.x下的PyQt不使用`QString`，而是`str`。另外，之前為了避開Python 2.x關鍵詞限制的`exec_()`、`print_()`兩個函數現在重新命名為`exec()`，`print()`。

## PyQt組件

PyQt包含了大約440個類型、超過6000個的函數和方法。

- 「QtCore」模塊主要包含了一些非GUI的基礎功能，包含事件循環與Qt的信號機制。此外，還提供了跨平台的Unicode、線程、記憶體映射文件、共享內存、正則表達式和使用者設置。
- 「QtGui」模塊包含了大多數的GUI類型。包含按鈕、文本框、列表等常見控制項，還包含了基於MVC設計模式的列表、表格、樹型控制項。同時還提供了一個能夠容納成千上萬個元素的畫布控制項，其中可以放置各種控制項和圖形。此外，QtGui還支持界面動畫與界面狀態機編程。
- 「QtNetwork」模塊可以用於編寫非阻塞式的UDP、TCP程序。還包含了DNS、HTTP與FTP的客戶端。
- 「QtOpenGL」模塊允許Qt程序使用OpenGL渲染3D圖形，而且不必大量更改代碼。
- 「QtSql」模塊支持多種SQL資料庫。包括SQLite、ODBC、MySQL、PostgreSQL、Oracle。還提供了一個基於MVC模式的數據模型，與QtGui的的表格控制項配合使用。
- 「QtXml」包含一個XML解釋器，同時支持SAX和DOM兩種編程方式。
- 「QtWebkit」與「QtScript」兩個子模塊支持WebKit與ECMAScript腳本語言
- 「Phonon」子模塊支持高級的多媒體編程。包含音訊播放器、影片播放器與聲效處理。
- 「uic」子模塊能夠將Qt的窗體文件轉換為Python代碼，能夠即時讀入窗體文件並且顯示出來。它依賴於QtXml模塊。「QScintilla」子模塊包含一個基於Scintilla的文本編輯器控制項，Eric IDE使用它作為代碼編輯器。「QtMultimedia」提供了底層的多媒體支持，現在多數開發者改用Phonon模塊。「QtSvg」支持SVG 1.2 Tiny的靜態標準，用於顯示與保存SVG格式的圖形。

## 簡單例子

下面一段代碼演示了一個簡單的PyQt程序，它的功能是在窗體內顯示一個按鈕，當按下按鈕時，要求使用者輸入名字。根據使用者是否輸入了名字，會分別顯示兩種問候語。

```python
# -*- coding: utf-8 -*-
#该程序适合Python 2.x

import sys
from PyQt4.QtGui import *

class TestWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self, windowTitle=u"A Simple Example for PyQt.")
        self.outputArea=QTextBrowser(self)
        self.helloButton=QPushButton(self.trUtf8("问候(&S)"), self)
        self.setLayout(QVBoxLayout())
        self.layout().addWidget(self.outputArea)
        self.layout().addWidget(self.helloButton)

        self.helloButton.clicked.connect(self.sayHello)

    def sayHello(self):
        yourName, okay=QInputDialog.getText(self, self.trUtf8("请问你的名字是?"), self.trUtf8(b"名字"))
        if not okay or yourName==u"": #用户没有输入名字，或者是点了取消
            self.outputArea.append(self.trUtf8("你好，陌生人！"))
        else:
            self.outputArea.append(self.trUtf8("你好，<b>%1</b>。").arg(yourName))

app=QApplication(sys.argv)
testWidget=TestWidget()
testWidget.show()
sys.exit(app.exec_())
```

## signal和slot

Qt採用了signal和slot的概念來處理GUI程序中的使用者事件。PyQt同樣支持這種方法，還進而針對Python的特點增強了某些功能。任何Python類型都可以定義signal和slot，並與GUI控制項的signal和slot相連接。PyQt支持old-style與new-style兩種連接方式。不過，目前一般推薦使用new-style connection。因為它還支持連接到Python函數，而且看起來也比較pythonic。

```Python
#old-style connection。
self.connect(self, SIGNAL("mySignal(int)"), self, SLOT("mySlot(int)"))
#new-style connection
self.mySignal.connect(self.mySlot)
#连接到函数
self.mySignal.connect(lambda value:sys.stdout.write(str(value)))
```

`QMetaObject.connectSlotsByName(obj)`函數可以幫助程式設計師自動連接signal和slot。使用它可以免去很多代碼。

## Qt設計器

Qt設計器是Qt所包含的可視化UI設計器。在安裝PyQt時，可以選擇安裝Qt設計器。它使用拖拉操作來設計圖形界面。在設計的同時，還能夠直接預覽最終的窗體效體。當窗體很複雜或者整個程序需要大量的窗體時，Qt設計器可以節省大量的代碼。不過稍有改變的是，設計好窗體後需要運行pyuic4這個腳本，將窗體文件轉換成Python代碼。仍以「簡單例子」中的小程序為例，首先使用Qt設計器設計出窗體。假定保存為"h:\pyqt_example.ui"。窗體文件內容是：

```xml
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>TestWidget</class>
 <widget class="QWidget" name="TestWidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>361</width>
    <height>271</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>A Simple Example for PyQt.</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <item>
    <widget class="QTextBrowser" name="outputArea"/>
   </item>
   <item>
    <widget class="QPushButton" name="helloButton">
     <property name="text">
      <string>问候(&amp;S)</string>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>
```

使用如下命令將窗體文件轉換成Python代碼（適用於Windows系統）：

```dos
H:\> C:\Python26\pyuic4.bat -o ui_pyqt_example.py pyqt_example.ui
```

新的示例程序是（假定保存為h:\pyqt_example.py）：

```python
# -*- coding: utf-8 -*-
#该程序适合Python 2.x版本。

import sys
from PyQt4.QtGui import *
#差异1：从转换后的代码里面导入窗体
from ui_pyqt_example import Ui_TestWidget

#差异2：需要继承Ui_TestWidget
class TestWidget(QWidget, Ui_TestWidget):
    def __init__(self):
        QWidget.__init__(self)
        #差异3：原来长篇累牍的创建控件的代码不再需要了，取而代之的是一行简单的setupUi()
        self.setupUi(self)
        self.helloButton.clicked.connect(self.sayHello)

    def sayHello(self):
        yourName, okay=QInputDialog.getText(self, self.trUtf8("请问你的名字是?"), self.trUtf8(b"名字"))
        if not okay or yourName==u"": #用户没有输入名字，而是点了取消
            self.outputArea.append(self.trUtf8("你好，陌生人！"))
        else:
            self.outputArea.append(self.trUtf8("你好，<b>%1</b>。").arg(yourName))

app=QApplication(sys.argv)
testWidget=TestWidget()
testWidget.show()
sys.exit(app.exec_())
```

## PyQt的優劣

### 優勢

- PyQt的API與Qt類似，Qt的文檔通常仍然可以應用於PyQt。因此，PyQt的文檔比PyGTK、wxPython、Tkinter等GUI編程庫的文檔豐富得多。
- 如果程式設計師具備使用Qt的經驗，一般很快就可以過渡到PyQt上。而使用PyQt的程式設計師，如果同時精通C++的話，也可以很快地過渡到Qt平台上。
- 利用SIP，大多數為Qt開發的控制項可以方便地port到PyQt。——然而，SIP也需要一些學習成本。
- 有方便的周邊工具支持PyQt。如QtDesigner，可以使用拖拉式的方法來設計界面，簡單易用。Eric6，一個使用PyQt設計的Python IDE，對PyQt有特殊的支持。

### 劣勢

- 由於PyQt同時使用Qt以及Python的兩種記憶體管理方法，所以在使用PyQt的過程中要注意避免記憶體洩露以及懸掛指針。
- 運行時龐大，在Windows平台，只使用PyQt.QtCore與PyQt.QtGui兩個子模塊時，壓縮後至少需要4.09M
- 需要學習一些C++知識，主要是C++類型、記憶體管理兩個方面，以便於閱讀Qt文檔和理解PyQt的行為。

## SIP

SIP是一個自動為C和C++庫生成Python擴展模塊的工具。為了方便開發PyQt，SIP於1998被「Riverbank Computing」公司創造出來。不過，SIP不專用於PyQt，而是適用於所有的C和C++庫。

使用SIP時，程式設計師首先要編寫一個特殊的".sip"文件，使用類似於C++的語法在其中描述擴展模塊所提供的類型與函數。然後用SIP將這個文件轉化為C++代碼。最終編譯，與C、C++庫連結後就成為Python擴展模塊。".sip"文件類似於C、C++的頭文件。根據需要，需要程式設計師用SIP定義的語法添加一些C++代碼中沒有的資訊。因為SIP不支持完整的C++語法，所以不能直接使用C++的頭文件作為".sip"文件。

## 使用PyQt的著名應用程式

- Eric Python IDE
- Anki, a spaced repetition flashcard program
- QtiPlot, a computer program to analyze and visualize scientific data
- qt-recordMyDesktop：recordMyDesktop的Qt4介面
- Kodos, Python Regular Expression Debugger

## 基本資料

- 開發者：Riverbank Computing
- 發行日期：1998
- 軟體版本週期：6.9.1
- 軟體版本週期：2025年6月6日，2025年6月6日
- 作業系統：跨平台
- 程式語言：C++ / Python
- 許可協議：GNU GPL和商業授權
- 網站： riverbankcomputing.com