[Python] PyQt5

程式語言:Python
Package:PyQt5
官網
官網文件

功能:建立 GUI 人機介面

範例

簡單視窗
import sys
from PyQt5.QtWidgets import QApplication, QWidget


if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)

    # 建立 QWidget
    w = QWidget()
    # 設定尺寸
    w.resize(250, 150)
    # 設定位置
    w.move(300, 300)
    # 設定標題
    w.setWindowTitle('Simple')
    # 顯示,因圖形元件被創造時都是 hidden 狀態
    w.show()

    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())
簡單按鈕
import sys
from PyQt5.QtWidgets import QApplication, QPushButton


if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)

    # & + 字元,可設定快捷鍵 Alt + E
    # Qt 設定 action focus 的簡便方法
    button = QPushButton("H&ello!")
    # 設定尺寸
    button.resize(200, 75)
    # 設定位置
    button.move(500, 400)
    # 設定視窗標題
    button.setWindowTitle("Hello World")
    # 顯示,因圖形元件被創造時都是 hidden 狀態
    button.show()

    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())
簡單排版
  • 由上到下的概念
    • 通常只需要一個 top-level window,也就是 parent 為 None
    • top-level window 擁有標題列和邊框
  • 每個 widget 可有其所屬的 parent ( 預設參數 parent=None )
    • 當 parent 解構時,child 們也會一同解構
    • 如何成為 parent
      • 當 A 調用 addWidget(w),A 就會成為 w 的 parent
      • 當 A 調用 setLayout(layout) ,A 就會成為 layout 的 parent,並接管 layout 的所有 child
      • 不只這些,不同物件有不同的 function
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout


if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)

    # & + 字元,可設定快捷鍵 Alt + E
    # Qt 設定 action focus 的簡便方法
    ok = QPushButton("&OK")
    cancel = QPushButton("&Cancel")

    # 基本排版元件,有 QHBoxLayout() & QVBoxLayout()
    layout = QHBoxLayout()    
    # 加入 ok button 並設為 child
    layout.addWidget(ok)    
    # 加入彈簧元件,可將兩邊元件推到底,用在視窗放大時的介面調整
    # 數量可設定,增加彈力
    layout.addStretch(1)    
    # 加入 cancel button 並設為 child
    layout.addWidget(cancel)    
    # 加入彈簧元件,可將兩邊元件推到底,用在視窗放大時的介面調整
    # 數量可設定,增加彈力
    layout.addStretch(2)
    
    # 建立 widget 元件,並為 top-level window
    widget = QWidget()    
    # 設定 layout 元件,並設為 child
    widget.setLayout(layout)
    # 顯示,因圖形元件被創造時都是 hidden 狀態
    widget.show()

    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())
簡單 Signals & Slots 範例
  • 多 singals 對 多 slots
  • slots 可為任意 function
  • 參數可為 Python 任意型態
  • signal 與 signal 可互相連接,產生連鎖反應
  • 可 connect,也可 disconnect
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QHBoxLayout, QSpinBox, QSlider, QMessageBox

def helloWorld():
    QMessageBox.information(None, 'Message',  'Hello World', QMessageBox.Ok)

if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)
    
    # & + 字元,可設定快捷鍵 Alt + E
    # Qt 設定 action focus 的簡便方法
    button = QPushButton("H&ello!")
    
    # 建立 QSpinBox
    spinBox = QSpinBox()
    # 設定前罝符號
    spinBox.setPrefix("$")    
    # 設定範圍
    spinBox.setRange(0, 100)
    
    # 建立橫向 QSlider
    slider = QSlider(Qt.Horizontal)
    # 設定範圍
    slider.setRange(0, 100)
    
    # signals 連接 slots
    button.clicked.connect(helloWorld)
    spinBox.valueChanged.connect(slider.setValue)
    slider.valueChanged.connect(spinBox.setValue)

    # 基本排版元件,有 QHBoxLayout() & QVBoxLayout()
    layout = QHBoxLayout()    
    # 加入 ok button 並設為 child
    layout.addWidget(button)    
    # 加入 spinBox 並設為 child
    layout.addWidget(spinBox)    
    # 加入彈簧元件,可將兩邊元件推到底,用在視窗放大時的介面調整
    # 數量可設定,增加彈力
    layout.addStretch(1)    
    # 加入 slider 並設為 child
    layout.addWidget(slider)    
    # 加入彈簧元件,可將兩邊元件推到底,用在視窗放大時的介面調整
    # 數量可設定,增加彈力
    layout.addStretch(2)
    
    # 建立 widget 元件,並為 top-level window
    widget = QWidget()    
    # 設定 layout 元件,並設為 child
    widget.setLayout(layout) 
    # 顯示,因圖形元件被創造時都是 hidden 狀態
    widget.show()

    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())
簡單 OOP 寫法
建議使用 OOP 撰寫 PyQt
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon


class Example(QWidget):
    
    def __init__(self, parent = None):
        # 繼承的 parent 初始化 fucntion
        super().__init__(parent)
        
        self.initUI()
        
        
    def initUI(self):  
        # 設定位置尺寸
        # 同 self.move(300, 300)
        # 同 self.resize(250, 150)        
        self.setGeometry(300, 300, 250, 150)
        # 設定標題
        self.setWindowTitle('Simple')
        # 顯示,因圖形元件被創造時都是 hidden 狀態
        self.show()
        
        
if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)
    # 建立 Exxample instance
    ex = Example()
    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())  
簡單 Main Window 範例
  • QMainWindow
    • 預設內建 menubar、toolbar 跟 status bar
    • QWidget 可擁有這些元件,但必須自行調整
    • 擁有自己的 layout 風格如圖,無法變更
    • central widget 必須被指定,不然顯示會有問題
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QWidget, QDockWidget, QHBoxLayout, QPushButton, QTextEdit, QAction, QApplication, QDesktopWidget
from PyQt5.QtGui import QIcon


class Example(QMainWindow):
    
    def __init__(self, parent = None):
        # 繼承的 parent 初始化 fucntion
        super().__init__(parent)
        
        self.initUI()
        
        
    def initUI(self):          
        # & + 字元,可設定快捷鍵 Alt + 字元
        # Qt 設定 action focus 的簡便方法
        button1 = QPushButton("H&ello1!")
        button2 = QPushButton("&Hello2!")
        
        # 基本排版元件,有 QHBoxLayout() & QVBoxLayout()
        layout = QHBoxLayout()
        # 加入 button 並設為 child
        layout.addWidget(button1)
        layout.addWidget(button2)
        
        # 建立 QWidget 元件
        widget = QWidget()
        # 設定 layout 元件,並設為 child
        widget.setLayout(layout)
        # 設定主要 widget
        self.setCentralWidget(widget)
        
        # 建立 QTextEdit
        textEdit = QTextEdit()
        
        # 建立 QDockWidget 附屬視窗
        dockwidget = QDockWidget()
        # 設定特性 AllDockWidgetFeatures => DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable
        dockwidget.setFeatures(QDockWidget.DockWidgetClosable)        
        # 設定可能的擺放位置
        dockwidget.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.TopDockWidgetArea)
        # 設定 widget 為 textEdit 並設為 child
        dockwidget.setWidget(textEdit)
        # 可由 allowedAreas() 得知可擺放的位置,但不理會也會成功設定
        self.addDockWidget(Qt.RightDockWidgetArea, dockwidget);
        
        
        # 建立 QAction 並設置 Icon,Parent 為 self
        # QAction 可用在 menu 或 toolbar,且可重覆使用
        # exit.jpg 需自行定義
        exitAction = QAction(QIcon('exit.jpg'), 'Exit', self)
        # 設定快捷鍵
        exitAction.setShortcut('Ctrl+Q')
        # 設定 status bar 顯示
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.close)
        
        # 回傳 statusbar,若不存在則建立
        statusbar = self.statusBar()

        # 回傳 menuBar,若不存在則建立
        menubar = self.menuBar()
        # 建立 menu 清單
        fileMenu = menubar.addMenu('&File')
        # 附加 exitAction
        fileMenu.addAction(exitAction)
        
        # 回傳 toolBar,若不存在則建立
        toolbar = self.addToolBar('Exit')
        # 附加 exitAction
        toolbar.addAction(exitAction)
        
        # 設定尺寸
        self.resize(500, 500)
               
        # 設定標題
        self.setWindowTitle('Simple')

        # 顯示,因圖形元件被創造時都是 hidden 狀態
        self.show()
    
    
    # 確定系統已畫,尺寸才會正確
    # 此時 frameGeometry 位置仍是錯的,因尚未把視窗放好
    def showEvent(self, e):
        super().showEvent(e)
        
        # 視窗置中
        self.center() 


    def center(self):     
        # 得到 frameGeometry 的 QRect
        qr = self.frameGeometry()
        
        # QDesktopWidget 可得到和桌面相關的資訊
        # 得到桌面可用的 geometry 的中心點 
        cp = QDesktopWidget().availableGeometry().center()
        
        # 將 qr 的中心點移至此中心點
        qr.moveCenter(cp)
        
        # 將視窗的左上角移至 qr 的左上角座標
        self.move(qr.topLeft())
        
        
if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)
    # 建立 Exxample instance
    ex = Example()
    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())  
簡單 OpenCV 範例
import sys
import cv2
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel, QScrollArea, QDesktopWidget, QSizePolicy
from PyQt5.QtGui import QImage, QPixmap, QPalette


class Example(QMainWindow):
    
    def __init__(self, parent = None):
        # 繼承的 parent 初始化 fucntion
        super().__init__(parent)
        
        self.initUI()
        
        
    def initUI(self):    
        # 建立 QLabel 元件
        imgLabel = QLabel()
        
        # openCV 讀檔
        cvImage = cv2.imread("test.jpg")
        # 得到 大小 與 幾個 byte 組成一個顏色
        height, width, byteValue = cvImage.shape
        # 得到寬有多少 byte
        byteValue = byteValue * width
        
        # openCV 預設顏色排列為 BGR 故需再轉換為 RGB
        temp = cv2.cvtColor(cvImage, cv2.COLOR_BGR2RGB)
        # 設定 imgLabel 圖片
        # 因 setPixmap 輸入需為 QPixmap,所以還得多做一層轉換
        imgLabel.setPixmap(QPixmap().fromImage(QImage(temp, width, height, byteValue, QImage.Format_RGB888)))        
        
        # 建立 QScrollArea for 滾動圖片
        scrollArea = QScrollArea()
        # 設定背景顏色
        scrollArea.setBackgroundRole(QPalette.Dark)
        # 將 imgLabel 設定為 child
        scrollArea.setWidget(imgLabel)
        
        # 設定主要 widget
        self.setCentralWidget(scrollArea)         
        # 設定尺寸
        self.resize(500, 500)
        # 視窗置中
        self.center()        
        # 設定標題
        self.setWindowTitle('Simple')
        # 顯示,因圖形元件被創造時都是 hidden 狀態
        self.show()
    
    def center(self):    
        # 得到 frameGeometry 的 QRect
        qr = self.frameGeometry()
        
        # QDesktopWidget 可得到和桌面相關的資訊
        # 得到桌面可用的 geometry 的中心點 
        cp = QDesktopWidget().availableGeometry().center()
        
        # 將 qr 的中心點移至此中心點
        qr.moveCenter(cp)
        
        # 將視窗的左上角移至 qr 的左上角座標
        self.move(qr.topLeft())
        
        
if __name__ == '__main__':
    # Qt GUI 需要唯一一個 QApplication 負責管理,可傳入 sys.argv 參數
    app = QApplication(sys.argv)
    # 建立 Exxample instance
    ex = Example()
    # app.exec_() 讓 QApplication 進入 event loop
    # exec 是 Python keyword,所以會多出底線
    sys.exit(app.exec_())   

簡單設計方法

使用官方的 designer.exe 設計 GUI 並存檔為 .ui
轉換方法
python -m PyQt5.uic.pyuic xxx.ui > xxx.py
PS. 直接用 pip 安裝並不會有此 designer.exe,需至官網下載安裝後才有

Componets 介紹

  • 範例可使用 python qtdemo.pyw 觀看
    • 位置:...\site-packages\PyQt5\examples\qtdemo
  • QAxContainer
    • 控制 ActiveX controls 跟 COM objects
    • 不支援寫入 ActiveX servers in Python
    • 只限於 Windows
  • QtBluetooth
    • Bluetooth
  • QtCore
    • 主要的核心 classes,像是 event loop 和 Qt’s signal and slot mechanism.
    • 另外還有 abstractions for animations,state machines,threads,mapped files, shared memory,regular expressions 和 user and application settings
  • QtDBus 
    • D-Bus protocol
    • 不支援 Windows.
  • QtDesigner
    • 自定義 Qt Designer 元件
  • QtGui
    • windowing system integration,event handling,2D graphics,basic imaging,fonts 和 text. 
    • OpenGL 和 OpenGL ES bindings
  • QtHelp
    • 建立 Help 文件
  • QtLocation
    • 控制 geocoding,navigation information,位置尋找
  • QtMacExtras
    • 額外的 classes for OS X 和 iOS
  • QtMultimedia
    • multimedia content and APIs to access camera and radio functionality
  • QtMultimediaWidgets
    • 元件可 handle multimedia content
  • QtNetwork
    • UDP and TCP clients and servers
    • HTTP clients and support DNS lookups
  • QtNfc
    • NFC 控制
  • QtOpenGL
    • OpenGL
  • QtPositioning
    • 定位借由 satellite,Wi-Fi,a text file,and so on
  • QtPrintSupport
    • 影印功能,甚至可產生 PostScript 和 PDF
  • QtQml
    • QML 和 JavaScript
  • QtQuick
    • 基本元件 for creating user interfaces with QML.
  • QtQuickWidgets
    • 顯示 QML scene in a traditional widget.
  • QtSensors
    • Sensor 控制,像是 accelerometers,altimeters,ambient light,temperature sensors,gyroscopes 和 magnetometers
    • 未支援 gestures
  • QtSerialPort
    • serial ports
  • QtSql
    • SQL 控制
  • QtSvg
    • SVG 控制
  • QtTest
    • 測試
    • PyQt5 不支援,因 python 本身有更好用的 unittest
  • QtWebChannel
    • 控制 QObject or QML objects from HTML clients.
  • QtWebEngine
    • Web Engine objects created in QML to Python.
  • QtWebEngineCore
    • QtWebEngineWidgets 的核心 classes
  • QtWebEngineWidgets
    • Chromium based implementation of a web browser 
    • 取代 QtWebKit 擁有更好的功能,但消耗更多資源,且無法直接利用 Python APIs 控制 network 跟 HTML
    • Windows 只支援 v3.5+
  • QtWebKit
    • WebKit2 based implementation of a web browser
  • QtWebKitWidgets
    • WebKit1 based implementation of a web browser
  • QtWebSockets
    • WebSocket protocol described in RFC 6455
  • QtWidgets
    • UI elements
  • QtWinExtras
    • 額外的 classes for Window
  • QtX11
    • X11
  • QtXml
    • SAX and DOM interfaces to Qt’s XML parser.
  • QtXmlPatterns
    • XPath, XQuery, XSLT and XML Schema validation.
  • Enginio
    • Qt Cloud Services Managed Application Runtime.
  • Qt
    • 包含以上所有 package
  • uic
    • 控制 .ui files created by Qt Designer
    • 轉換指令:python -m PyQt5.uic.pyuic xxx.ui > xxx.py


參考

PyQt5 tutorial
PyQt 实践教学(一)--First programs in PyQt5
[PyQt 教學] Part 1: Introduction

留言