SoFunction
Updated on 2024-11-15

Python desktop application development practice of the installation and use of PyQt

Introduction: Three major frameworks for desktop application development

Tkinter: Advantage is free installation and relative simplicity, disadvantage is less functionality and no interface design tools.

PyQT: High usage, most powerful, high code maintainability and readability.

WxPython: Between Tkinter and PyQT, equivalent to a compressed version of QT.

In summary: the three frameworks have their own advantages and disadvantages, there is time to learn all of them, it is not complicated. If you choose only one, here is the recommendation to use the most mainstream, the most value-added, the highest ceiling PyQT.

I. Introduction to PyQT

Qt: Qt is a cross-platform C++ GUI application development framework, and has become one of the most powerful and popular cross-platform GUI libraries.Qt not only develops GUI programs, but also develops non-GUI programs, such as console tools and service programs.

PyQt: PyQt is a Python wrapper for Qt, providing an API for Qt classes and functions.

The official website for PyQt6:/news

Chinese manual that you can refer to:/news/?action=onClick

PyQt common modules:

QtWidgets module: contains application classes, window classes, control classes and component classes.

QtGui module: contains gui-related features such as event handling, image handling, font and color classes, etc.

QtCore module: contains core non-gui functionality, such as threads, timers, date and time classes, file classes, etc.

II. Installation

Installation:pip install PyQt6

Check the installed version (go to the python command line):

from  import QT_VERSION_STR, PYQT_VERSION_STR
QT_VERSION_STR, PYQT_VERSION_STR

III. Tutorials

(1) Basic Window

import sys
from  import QApplication, QWidget, QLabel
from  import QIcon, QFont
from  import Qt
# Desktop application window class derived from QWidget class
class MyWindow(QWidget):  #QtWidgets module: contains application classes, window classes, control classes and component classes.
	# Constructor
    def __init__(self): 
        super().__init__() # Call the constructor of the base class
        ('Hello World') # Setting the title
        #(QIcon('res/')) # Can set the icon
        lab = QLabel('Hello World', self) # Instantiation tags
        (320,160) # Set label size
        (QFont('Arial', 32, )) # Set the font size
        () # Text centered within tags
        () # Display window
if __name__ == '__main__':
    app = QApplication() # Create an application that receives a list of parameters from the command line
    win = MyWindow() # Create the window, no QWidget entry is needed for initialization here.
    (()) # After the main loop of the application has finished,invocations()Methods to clean up the site

The result after execution is as follows:

(2) partition layout window (similar to the use of div in html)

import sys
from  import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QHBoxLayout, QVBoxLayout
from  import QIcon
from  import Qt
class MyWindow(QWidget):
    def __init__(self):     
        super().__init__()
        ('Partition Layout')
        (400, 300, 320, 160) # Set window position and size
        lab_acc = QLabel('Account number:') # Instantiation tags
        account = QLineEdit() # Single line text edit box
        () # Text centered within tags
        lab_pw = QLabel('Password:')
        passwd = QLineEdit()
        ()
        () # Do not show password
        btn_ok = QPushButton('OK') #buttons
        btn_cancel = QPushButton('Cancel')
        # Use the Horizontal Layout Manager to layout the lab_acc control and the account control with 10 pixels of left and right white space.
        hbox_acc = QHBoxLayout() # Horizontal Layout Manager
        hbox_acc.addSpacing(10)  # Horizontal layout spacing
        hbox_acc.addWidget(lab_acc) #Put the instantiated tag account into the
        hbox_acc.addWidget(account) #Put #a single line text edit box into the
        hbox_acc.addSpacing(10)
        # Use the Horizontal Layout Manager to layout the lab_pw control and the passwd control with 10 pixels of left and right white space
        hbox_pw = QHBoxLayout() #Vertical Layout Manager
        hbox_pw.addSpacing(10)
        hbox_pw.addWidget(lab_pw)
        hbox_pw.addWidget(passwd)
        hbox_pw.addSpacing(10)
        # Layout the btn_ok control and btn_cancel control using the Horizontal Layout Manager
        hbox_btn = QHBoxLayout() # Horizontal Layout Manager
        hbox_btn.addStretch(5) # Set the left stretch factor
        hbox_btn.addWidget(btn_ok) # Add the btn_ok control
        hbox_btn.addWidget(btn_cancel) # Add the btn_cancel control
        hbox_btn.addStretch(1) # Set the right-hand stretch factor
        # Layout the top 3 horizontal layout managers using the vertical layout manager
        vbox = QVBoxLayout() 
        (10)
        (hbox_acc)
        (5)
        (hbox_pw)
        (1)
        (hbox_btn)
        (10)
        # Apply the Vertical Layout Manager to the window
        (vbox)
        () # Display window
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The result after execution is as follows:

(3) Grid layout window (similar to table in html)

import sys
from  import QApplication, QWidget, QPushButton, QGridLayout
from  import QIcon
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Raster Layout')
        () # Initialize the interface
        () # Display window
    def initUI(self):   #Create a 5*5 array, which will be put into the grid one by one later on
        keys = [
            ['(', ')', 'Back', 'Clear'],
            ['7',  '8',  '9',  '/'], 
            ['4',  '5',  '6',  '*'], 
            ['1',  '2',  '3',  '-'], 
            ['0',  '.',  '=',  '+']
        ]  
        grid = QGridLayout() # Create a grid layout manager
        (grid) # Apply the Grid Layout Manager to the window
        for i in range(5):
            for j in range(4):
                button = QPushButton(keys[i][j])
                (button, i, j) # Place elements into the grid, i is the horizontal coordinate, j is the vertical coordinate
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The result after execution is as follows:

(4) form layout window (similar to form in html)

import sys
from  import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QFormLayout
from  import QIcon
from  import Qt
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Form Layout')
        (400, 300, 320, 200) # Set window position and size
        form = QFormLayout() # Create a form layout manager
        () # Set labels right-aligned (default left-aligned)
        name = QLineEdit() # Single line text edit box
        mobile = QLineEdit()
        passwd = QLineEdit()
        addr = QLineEdit()
        ('Name', name) #Put text into the form
        ('Mobile phone', mobile)
        ('Password', passwd)
        ('Correspondence address', addr)
        ("Please enter your name.") # Set placeholder gray body defaults
        ("Please enter a cell phone number.")
        ("Please enter the password.")
        ("Please enter a mailing address.")
        () #EchoMode is Normal: Display the entered characters. This is the default value
        () #EchoMode is NoEcho: show nothing. This may apply to passwords where even the length of the password should be kept secret.
        () #EchoMode as Password: Displays the platform-associated password mask characters instead of the actual characters typed.
        () #EchoMode is PasswordEchoOnEdit: show the entered characters when editing, otherwise show the same characters as the password.
        btn_ok = QPushButton('OK')
        vbox = QVBoxLayout() #Vertical Layout Manager Layout
        (10)
        (form)
        (1)
        (btn_ok, alignment=)
        (10)
        # Apply the Vertical Layout Manager to the window
        (vbox)
        () # Display window
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The result after execution is as follows:

(5) Event Functions and Event Filters

The commonly used event functions are listed below:
keyPressEvent(): key press event keyReleaseEvent(): key release event
mouseDoubleClickEvent(): mouse double click event
mouseMoveEvent(): mouse move event
mousePressEvent(): mouse press event
mouseReleaseEvent(): mouse release event
timerEvent(): timer event
dragEnterEvent(): drag and drop into the current window event
dragLeaveEvent(): dragLeaveWindow event
dragMoveEvent(): drag and move event
enterEvent(): enter window area event
leaveEvent(): leave window area event
closeEvent(): close window event

Example 1-Event Functions:

import sys
from  import QApplication, QWidget, QLabel, QHBoxLayout
from  import Qt
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Events and event functions')
        (400, 300, 320, 80)
        () # Initialize the interface
        ()
    def initUI(self):
        lab = QLabel('Press Esc to close the window')
        box = QHBoxLayout()
        (1)
        (lab)
        (1)
        (box)
	# Override the event function of the "Keyboard press event" to specify the ESC key as the key that triggers the event (if not, all keys will trigger the event).
    def keyPressEvent(self, evt):
        if () == .Key_Escape.value: #There is a cross-reference table below the corresponding keyboard
            ()
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The result after execution is as follows:

The corresponding keyboard cross-reference is shown below:

Qt::Key_Escape 0x01000000 Esc key
Qt::Key_Tab 0x01000001 Tab key Qt::Key_Backtab 0x01000002 Tab complement key
Qt::Key_Backspace 0x01000003 Backspace Qt::Key_Return 0x01000004 Return Key
Qt::Key_Enter 0x01000005 Enter key Qt::Key_Insert 0x01000006 Insert key
Qt::Key_Delete 0x01000007 Delete key Qt::Key_Pause 0x01000008 Pause key
Qt::Key_Print 0x01000009 Screenshot Key Qt::Key_SysReq 0x0100000a PrtSc
Qt::Key_Clear 0x0100000b Clear key Qt::Key_Home 0x01000010 Home key
Qt::Key_End 0x01000011 End key Qt::Key_Left 0x01000012 ←
Qt::Key_Up 0x01000013 ↑ Qt::Key_Right 0x01000014 →
Qt::Key_Down 0x01000015 ↓ Qt::Key_PageUp 0x01000016 Previous Page
Qt::Key_PageDown 0x01000017 Next Page Qt::Key_Shift 0x01000020 Shift key
Qt::Key_Control 0x01000021 Ctrl key Qt::Key_Alt 0x01000023 Alt key
Qt::Key_AltGr 0x01001103 Right Alt Qt::Key_CapsLock 0x01000024 Caps key
Qt::Key_NumLock 0x01000025 NumLock Qt::Key_ScrollLock 0x01000026 ScrollLock
Qt::Key_F1 0x01000030 F1~F12 Qt::Key_F2 0x01000031
Qt::Key_F3 0x01000032 Qt::Key_F4 0x01000033 Qt::Key_F5 0x01000034
Qt::Key_F6 0x01000035 Qt::Key_F7 0x01000036 Qt::Key_F8 0x01000037
Qt::Key_F9 0x01000038 Qt::Key_F10 0x01000039
Qt::Key_F11 0x0100003a Qt::Key_F12 0x0100003b
Qt::Key_Menu 0x01000055 Menu key Qt::Key_Help 0x01000058 Help key
Qt::Key_Space 0x20 Space key Qt::Key_Exclam 0x21 ! Qt::Key_QuoteDbl 0x22 Quote
Qt::Key_NumberSign 0x23 # Qt::Key_Dollar 0x24 $ Qt::Key_Percent 0x25 %
Qt::Key_Ampersand 0x26 & Qt::Key_Apostrophe 0x27 The participant " ' "
Qt::Key_ParenLeft 0x28 ( Qt::Key_ParenRight 0x29 )
Qt::Key_Asterisk 0x2a * Qt::Key_Plus 0x2b + Qt::Key_Comma 0x2c ,
Qt::Key_Minus 0x2d - Qt::Key_Period 0x2e 。 Qt::Key_Slash 0x2f /
Qt::Key_0 0x30 Number 0~9 Qt::Key_1 0x31 Qt::Key_2 0x32 Qt::Key_3 0x33
Qt::Key_4 0x34 Qt::Key_5 0x35 Qt::Key_6 0x36 Qt::Key_7 0x37
Qt::Key_8 0x38 Qt::Key_9 0x39 Qt::Key_Colon 0x3a :
Qt::Key_Semicolon 0x3b ; Qt::Key_Less 0x3c < Qt::Key_Equal 0x3d equal to
Qt::Key_Greater 0x3e > Qt::Key_Question 0x3f ? Qt::Key_At 0x40 @
Qt::Key_A 0x41 Alphabetic keys Qt::Key_B 0x42 Qt::Key_C 0x43 Qt::Key_D 0x44
Qt::Key_E 0x45 Qt::Key_F 0x46 Qt::Key_G 0x47 Qt::Key_H 0x48
Qt::Key_I 0x49 Qt::Key_J 0x4a Qt::Key_K 0x4b Qt::Key_L 0x4c
Qt::Key_M 0x4d Qt::Key_N 0x4e Qt::Key_O 0x4f Qt::Key_P 0x50
Qt::Key_Q 0x51 Qt::Key_R 0x52 Qt::Key_S 0x53 Qt::Key_T 0x54
Qt::Key_U 0x55 Qt::Key_V 0x56 Qt::Key_W 0x57 Qt::Key_X 0x58
Qt::Key_Y 0x59 Qt::Key_Z 0x5a Qt::Key_BracketLeft 0x5b [
Qt::Key_Backslash 0x5c \ Qt::Key_BracketRight 0x5d ]
Qt::Key_AsciiCircum 0x5e ^ Qt::Key_Underscore 0x5f _
Qt::Key_QuoteLeft 0x60 “ Qt::Key_BraceLeft 0x7b { Qt::Key_Bar 0x7c |
Qt::Key_BraceRight 0x7d } Qt::Key_AsciiTilde 0x7e ~
Qt::Key_nobreakspace 0x0a0 No newline space Qt::Key_exclamdown 0x0a1 !

Example 2-Event Filter:

(Add importing QEvent to what you just did, add the eventFilter method, and execute the installEventFilter method at the end)

import sys
from  import QApplication, QWidget, QLabel, QHBoxLayout
from  import Qt, QEvent
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Events and event functions')
        (400, 300, 320, 80)
        () # Initialize the interface
        ()
    def initUI(self):
        lab = QLabel('Press Esc to close the window')
        box = QHBoxLayout()
        (1)
        (lab)
        (1)
        (box)
	# Override the event function of the "Keyboard press event" to specify the ESC key as the key that triggers the event (if not, all keys will trigger the event).
    def keyPressEvent(self, evt):
        if () == .Key_Escape.value: 
            ()
    # Event Filtering Methods
    def eventFilter(self, objwatched, evt):
        if () == :
            print('Ignore key events')
            return True
        return super().eventFilter(objwatched, evt)
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (win) # Install event filters for this object
    (())

Pressing Esc at this point does not close the window

(6) Signals and slots

Introduction: Signals and slots are the core mechanisms of Qt and communication between objects in PyQt programming. After creating an event loop, communication between objects can be realized by establishing connections between signals and slots. When a signal is emitted (emit), the connected slot function will be executed automatically.

Example 1-Use the built-in signals and slots:

import sys
from  import QPushButton, QMessageBox, QApplication, QWidget
class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ('Button Events')
        (300, 200)
         = QPushButton('Button', self)
        self.init_ui()
    def init_ui(self):
        (100, 30)
        (100, 50)   # Button Location
        (self.btn_hand) # Use connect to bind an event that is triggered when the button is clicked.
        ()
    def btn_hand(self):
        widget = QWidget()
        (widget, 'Pop messgae', 'OK') #Popup session box on triggered events
if __name__ == "__main__":
    app = QApplication()
    window = Window()
    ()
    (())

The results of the run are as follows:

Example 2 - Customized Signal and Slot Function Example:

import sys
from  import pyqtSignal, Qt
from  import *
class WinForm(QWidget):
    # Custom signals without parameters
    button_clicked_signal = pyqtSignal()
    def __init__(self, parent=None):
        super().__init__(parent)
        ('Custom Signal and Slot Function Examples')
        (300, 150)
        # Receive signals to connect to custom slot functions
        self.button_clicked_signal.connect(self.pop_message)
    def pop_message(self):
        (self, 'Pop messgae', 'OK')
    def mousePressEvent(self, event):        # Rewrite the mouse press event
        super().mousePressEvent(event)
        if () == :     # When the left mouse button clicks
            self.button_clicked_signal.emit()     # Transmit signals
if __name__ == '__main__':
    app = QApplication()
    win = WinForm()
    ()
    (())

The results of the run are as follows:

Example 3 - Combining signals and slots for multithreaded operations:

import sys
from  import QApplication,QMainWindow,QWidget,QProgressBar,QVBoxLayout,QPushButton
from  import QThread,QObject,pyqtSignal,Qt
#Thread Functions
class CountSever(QObject):
    _isrun = False Whether #threads run global variables
    countupdate = pyqtSignal()  #Customized signals
    # Constructor
    def __init__(self, interval) -> None:  #-> None Indicates that the type of value returned is unrestricted.
        super().__init__()
         = interval # Execution interval, initialized in the constructor specified, such as self.cs1 = CountSever(100) on behalf of every 100ms execution
    #Determine if a single thread is running
    def isrun(self,value:bool):
        self._isrun = value
    #Thread Run Functions
    def run(self):
        while True:
            pass
            while self._isrun:
                ()  #Fire custom signals when the thread is running
                () #Set the thread execution interval
 #ProgressBar main interface, QMainWindow - top-level window class, heavyweight control.
class MainWindow(QMainWindow):
    countstart = pyqtSignal(bool) # Customized signal objects
    def __init__(self) -> None: #-> None Indicates that the type of value returned is unrestricted.
        super().__init__() 
        self.init_ui()
        () # Call the following centering method
    def init_ui(self):
        ("Thread Refresh Progress Bar")
        (400, 300)
        self.main_widget = QWidget()
        (self.main_widget) #Center the display
        self.main_layout = QVBoxLayout()  ## Vertical Layout Manager Layout
        self.main_layout.setAlignment()
        self.main_widget.setLayout(self.main_layout)
        #Set up four progress bars
        self.progress_1 = QProgressBar(self) The #ProgressBar provides a horizontal or vertical progress bar with minimum and maximum steps specified using setMinimum() and setMaximum.
        self.progress_1.setRange(0,100)
        self.main_layout.addWidget(self.progress_1) # Add a progress bar control to the page layout
        self.progress_2 = QProgressBar(self)
        self.progress_2.setRange(0,100)
        self.main_layout.addWidget(self.progress_2)
        self.main_layout.addStretch(1)
        self.progress_3 = QProgressBar(self)
        self.progress_3.setRange(0, 100)
        self.main_layout.addWidget(self.progress_3)
        self.progress_4 = QProgressBar(self)
        self.progress_4.setRange(0, 100)
        self.main_layout.addWidget(self.progress_4)
        self.btn_start = QPushButton("Start")
        self.btn_start.setFixedSize(120,30)
        # Click the start button to trigger the start method defined below, the start method sends a True/Flase signal to the countstart custom object based on the current button, and then this True/Flase determines the result of isrun, which determines whether the thread runs or not
        self.btn_start.() 
        self.main_layout.addWidget(self.btn_start) #Putting the start button into the layout
        self.main_layout.setAlignment(self.btn_start,)
        # thread
        self.cs1 = CountSever(100) # Executed every 100ms
        # Customize the signal bound to the following event countupdate, the following self.progress_1.setValue(self.progress_1.value() + 1) means that the execution of a progress bar progress_1 value plus one, the range of values is 0-100, added to 100 will stop the thread
        self.() 
        (self.) # Custom signals are bound to the following events: determining whether a thread is running or not
        self.th1 = QThread()
        self.(self.th1) The #moveToThread function: assigns a thread to each of multiple tasks to execute.
        self.(self.) #QThread provides two signals, QThread::started() (fired when the thread starts) and QThread::finished() (fired when the thread has finished running)
        self.() #Threads start executing
        self.cs2 = CountSever(150)
        self.()
        (self.)
        self.th2 = QThread()
        self.(self.th2)
        self.(self.)
        self.()
        self.cs3 = CountSever(1000)
        self.()
        (self.)
        self.th3 = QThread()
        self.(self.th3)
        self.(self.)
        self.()
        self.cs4 = CountSever(1500)
        self.()
        (self.)
        self.th4 = QThread()
        self.(self.th4)
        self.(self.)
        self.()
    def setcenter(self): #Interface centered
        win_rect = ()
        screen_center = ().availableGeometry().center()
        win_rect.moveCenter(screen_center)
        (win_rect.topLeft())
    The #start method sends a True/Flase signal to the countstart custom object based on the current button, and then this True/Flase determines the result of isrun, which determines whether the thread runs or not
    def start(self):
        if self.btn_start.text() == "Start":
            (True)
            self.btn_start.setText("Pause")
        elif self.btn_start.text() == "Pause":
            (False)
            self.btn_start.setText("Start")
    def countupdate(self):
        if () == self.cs1:
            self.progress_1.setValue(self.progress_1.value() + 1)  # indicates that the execution of a progress bar progress_1 value plus one, the range of values is 0-100, add to 100 will stop the thread
        elif () == self.cs2:
            self.progress_2.setValue(self.progress_2.value() + 1)
        elif () == self.cs3:
            self.progress_3.setValue(self.progress_3.value() + 1)
        elif () == self.cs4:
            self.progress_4.setValue(self.progress_4.value() + 1)
if __name__ == "__main__":
    """ Main program run """
    app = QApplication()
    main_window = MainWindow()
    main_window.show()
    (())

The results of the run are as follows:

IV. Practical Examples

(1) Status bar (tracking mouse movements)

import sys
from  import QApplication, QMainWindow, QWidget, QLabel, QHBoxLayout
from  import QIcon, QPixmap
from  import Qt
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ('Status bar')
        () # Initialize the interface
        () # Window centered on the screen
        () # Display window
    def center(self):
        win_rect = () # Window rectangles (QRect class)
        scr_center = ().availableGeometry().center() # Screen Center
        win_rect.moveCenter(scr_center) # Center of window rectangle moved to center of screen
        (win_rect.topLeft()) # Moving window and window rectangle overlap
    def initUI(self):
        (400, 300)
        lab = QLabel()
        box = QHBoxLayout()
        (lab)
        self.main_widget = QWidget()
        self.main_widget.setLayout(box)
        (self.main_widget)
        # Enable mouse movement track sensing for widgets that need to handle mouse movement events.
        (True)
        self.main_widget.setMouseTracking(True)
        (True)
         = () # Return to the window status bar
        self.mouth_info = QLabel() # Label showing mouse coordinates
        (self.mouth_info) # Add tabs showing mouse coordinates to status bar
        (QLabel('All rights reserved ')) # Add a copyright notice to the status bar
        ('Ready to go', 3000) # Display message, disappears after 3 seconds
    def enterEvent(self, evt):
        """Responding to mouse-in-window events"""
        #(str, msecs=0): show str message lasts msecs milliseconds. If msecs is 0 (default), the message will be displayed until clearMessage() is called or showMessage is shown again to change the message.
        ('Mouse over', 3000) 
    def leaveEvent(self, evt):
        """Responds to mouse-out-of-window events."""
        ('Mouse over', 3000)
        self.mouth_info.setText('')
    def mouseMoveEvent(self, evt):
        """Responding to mouseover events"""
        pos = ()
        self.mouth_info.setText('x=%d, y=%d '%((), ()))
if __name__ == '__main__':
    app = QApplication() 
    win = MainWindow()
    (())

The results of the run are as follows:

(2) Menu bar

import sys
from  import QApplication, QMainWindow
from  import QIcon, QAction
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ('Menu Bar')
        (250, 150)
        () # Initialize the interface
        () # Window centered on the screen
        () # Display window
    def center(self):
        win_rect = () # Window rectangles (QRect class)
        scr_center = ().availableGeometry().center() # Screen Center
        win_rect.moveCenter(scr_center) # Center of window rectangle moved to center of screen
        (win_rect.topLeft()) # Moving window and window rectangle overlap
    def initUI(self):
         = () # Return to the window status bar
        #openAction = QAction(QIcon('res/open_mso.png'), '& open', self) You can set the image in the first parameter
        openAction = QAction('& open', self)
        ('Ctrl+O')  #Setup shortcuts
        ('Open file') #Set status alert text
        (lambda : print('This pops up a dialog box to open the file'))
        saveAction = QAction('& Save', self)
        ('Ctrl+S')
        ('Save file')
        (lambda : print('This pops up a dialog box to save the file'))
        quitAction = QAction('& Exit', self)
        ('Ctrl+Q')
        ('Exit the program')
        ()
        singleAction = QAction('& Personal Privileges', self)
        ('Personal Privileges')
        (lambda : print('This responds to a personal permission action'))
        groupAction = QAction('& Group Permissions', self)
        ('Group Permissions')
        (lambda : print('Respond to group permission action here'))
        mb = ()
        fm = ('& Documentation')
        (openAction)
        (saveAction)
        ()
        subMenu = ('Permissions')
        (singleAction)
        (groupAction)
        ()
        (quitAction)
if __name__ == '__main__':
    app = QApplication() 
    win = MainWindow()
    (())

The results of the run are as follows:

(3) Toolbar

import sys
from  import QApplication, QMainWindow, QToolBar
from  import QIcon, QAction
from  import Qt
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ('Toolbar')
        (400, 300)
        () # Initialize the interface
        () # Window centered on the screen
        () # Display window
    def center(self):
        win_rect = () # Window rectangles (QRect class)
        scr_center = ().availableGeometry().center() # Screen Center
        win_rect.moveCenter(scr_center) # Center of window rectangle moved to center of screen
        (win_rect.topLeft()) # Moving window and window rectangle overlap
    def initUI(self):
         = () # Return to the window status bar
        import sys
from  import QApplication, QMainWindow, QToolBar
from  import QIcon, QAction
from  import Qt
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ('Toolbar')
        (400, 300)
        () # Initialize the interface
        () # Window centered on the screen
        () # Display window
    def center(self):
        win_rect = () # Window rectangles (QRect class)
        scr_center = ().availableGeometry().center() # Screen Center
        win_rect.moveCenter(scr_center) # Center of window rectangle moved to center of screen
        (win_rect.topLeft()) # Moving window and window rectangle overlap
    def initUI(self):
         = () # Return to the window status bar
        #openAction = QAction(QIcon('res/open_mso.png'), '& open', self) can add icon images
        openAction = QAction('& open', self)
        ('Ctrl+O')
        ('Open file')
        (lambda : print('This pops up a dialog box to open the file'))
        saveAction = QAction('& Save', self)
        ('Ctrl+S')
        ('Save file')
        (lambda : print('This pops up a dialog box to save the file'))
        quitAction = QAction('& Exit', self)
        ('Ctrl+Q')
        ('Exit the program')
        ()
        singleAction = QAction('& Personal Privileges', self)
        ('Personal Privileges')
        (lambda : print('This responds to a personal permission action'))
        groupAction = QAction('& Group Permissions', self)
        ('Group Permissions')
        (lambda : print('Respond to group permission action here'))
        mb = ()
        fm = ('& Documentation')
        (openAction)
        (saveAction)
        () #add a dividing line
        subMenu = ('Permissions')
        (singleAction)
        (groupAction)
        ()
        (quitAction)
        tb = ('Documentation')
        (openAction)
        (saveAction)
        tb = ('Exit')
        (quitAction)
        tb = QToolBar(self)
        ('Permissions')
        (, tb)
        (singleAction)
        (groupAction)
if __name__ == '__main__':
    app = QApplication() 
    win = MainWindow()
    (())
        openAction = QAction('& open', self)
        ('Ctrl+O')
        ('Open file')
        (lambda : print('This pops up a dialog box to open the file'))
        saveAction = QAction('& Save', self)
        ('Ctrl+S')
        ('Save file')
        (lambda : print('This pops up a dialog box to save the file'))
        quitAction = QAction('& Exit', self)
        ('Ctrl+Q')
        ('Exit the program')
        ()
        singleAction = QAction('& Personal Privileges', self)
        ('Personal Privileges')
        (lambda : print('This responds to a personal permission action'))
        groupAction = QAction('& Group Permissions', self)
        ('Group Permissions')
        (lambda : print('Respond to group permission action here'))
        mb = ()
        fm = ('& Documentation')
        (openAction)
        (saveAction)
        () #add a dividing line
        subMenu = ('Permissions')
        (singleAction)
        (groupAction)
        ()
        (quitAction)
        tb = ('Documentation')
        (openAction)
        (saveAction)
        tb = ('Exit')
        (quitAction)
        tb = QToolBar(self)
        ('Permissions')
        (, tb)
        (singleAction)
        (groupAction)
if __name__ == '__main__':
    app = QApplication() 
    win = MainWindow()
    (())

The results of the run are as follows:

(4) Dialog box

import sys, os
from  import QApplication, QMainWindow, QWidget, QPushButton, QGridLayout, QMessageBox, QInputDialog, QColorDialog, QFileDialog
from  import QIcon
from  import Qt
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        ('Dialog box')
        ()
        ()
    def initUI(self):
        grid = QGridLayout()
        btns = [
            ['Notification messages', 'Warning message', 'Error message', 'Confirmation message', 'About'],
            ['Color Selection', 'Save file', 'Open file', 'Select Path', 'Input']
        ]
        for i in (0, 1):
            for j in range(5):
                btn = QPushButton(btns[i][j])
                (self.on_button)
                (btn, i, j)
        self.main_widget = QWidget()
        self.main_widget.setLayout(grid)
        (self.main_widget)
         = ()
        ('Ready', 5000)
    def on_button(self):
        """Responding to keystrokes."""
        key = ().text()  #Calling this method in a slot function returns the emitter of the signal.
        (key, 1000)
        The #/warning/critical/question/about methods have different icons.
        #methodologiesAPI--int QMessageBox::information(QWidget *parent, const QString &title, const QString &text, button0|button1|button2)
        #parent is a pointer to the parent component, title is the title, text is the text text, and buttons 0, 1, and 2 are the three buttons, each separated by |.
        if key == 'Notification messages':
            reply = (self, 'Hints', ''The opponent admits defeat, the match is over.'', 
                           
            )
            (, 2000)
        elif key == 'Warning message':
            reply = (self, 'Warning', 'You can't mention peace continuously!', 
                
            )
            (, 2000)
        elif key == 'Error message':
            reply = (self, 'Error', 'Wrong move!', 
                |||
            )
            (, 2000)
        elif key == 'Confirmation message':
            reply = (self, 'Please select', 'To handheld and, accept?', 
                 |  | 
            )
            (, 2000)  # Display the result of the button selection to the message display bar in the bottom left corner
        elif key == 'About':
            (self, 'About', 'Genting Game S9 Spam Version')
        elif key == 'Color Selection':
            color = ()
            if ():
                ((), 2000)
        elif key == 'Save file':
            dase_dir = ()
            file_type = 'Python Files (*.py);;Text Files (*.txt);;All Files (*)'  #Select the file format you want to save: python or txt.
            fname, fext = (self, 'Save file', directory=dase_dir, filter=file_type)
            if fname:
                (fname, 2000)
        elif key == 'Open file':
            dase_dir = ()
            file_type = 'Python Files (*.py);;Text Files (*.txt);;All Files (*)'  #Select the file format you want to save: python or txt.
            fname = (self, 'Select file', directory=dase_dir, filter=file_type)
            if fname[0]:
                (fname[0], 2000)
        elif key == 'Select Path':
            dase_dir = ()
            folder = (self, 'Select folder', directory=dase_dir)
            (folder, 2000)
        elif key == 'Input':
            text, ok = (self, 'Input dialog', 'Please enter your room number:')
            if ok:                    
                (text, 2000)
if __name__ == '__main__':
    app = QApplication() 
    win = MainWindow()
    (())

The results of the run are as follows:

(5) Photo Albums

import sys
from  import QApplication, QWidget, QLabel, QPushButton, QHBoxLayout, QVBoxLayout
from  import QIcon, QPixmap
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Albums')
        ()
        ()
        ()
    def center(self):
        """The window is centered on the screen."""
        win_rect = ()
        scr_center = ().availableGeometry().center()
        win_rect.moveCenter(scr_center)
        (win_rect.topLeft())
    def initUI(self):
         = 0
        #Note that here you have to select three images and put them in the res directory of the folder where the current pyhon script is located, where I put the previous screenshot.
         = ('D:/tools/python/srcipts2023/res/', 'D:/tools/python/srcipts2023/res/', 'D:/tools/python/srcipts2023/res/')
         = QLabel()
        (QPixmap([])) # Use label tags to display images, default from the first image in the list, followed by methods to switch images based on buttons
        btn_prev = QPushButton('<')
        btn_next = QPushButton('>')
        btn_prev.(self.on_btn)
        btn_next.(self.on_btn)
        #Set the border
        hbox = QHBoxLayout()
        (1)
        (btn_prev)
        (50)
        (btn_next)
        (1)
        vbox = QVBoxLayout()
        ()
        (hbox)
        (vbox)
    def on_btn(self):
        """Responding to keystrokes."""
        key = ().text()
        if key == '<':
            # When clicking the '<' button, take the last picture, generally by definition (-1) on the line, the role of the total number of residuals here is to be able to maintain the cycle, for example, the first one does not have a picture in front of it, it will jump to the last one
            #For example, at the first one = 0, (-1)%len() = -1%3 = 2, at this point jump to the last one
             = (-1)%len() 
        else:
             = (+1)%len()
        (QPixmap([]))  
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The results of the run are as follows:

(6) Calculator

import sys
from  import QApplication, QWidget, QLabel, QPushButton, QGridLayout, QVBoxLayout
from  import QIcon, QFont
from  import Qt
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Calculator')
        ()
        ()
        ()
    def center(self):
        """The window is centered on the screen."""
        win_rect = ()
        scr_center = ().availableGeometry().center()
        win_rect.moveCenter(scr_center)
        (win_rect.topLeft())
    def initUI(self):
        """Initialization Interface"""
        #lcd:Liquid Crystal Display (QLCDNumber)
         = QLabel('0')  #Default Display
        (QFont('Arial', 24, )) #Set the font
        () # Right-aligned
        ('background-color:#000030; color:#30ff30') #Set the color
        keys = [
            ['(', ')', 'Back', 'Clear'],
            ['7',  '8',  '9',  '/'], 
            ['4',  '5',  '6',  '*'], 
            ['1',  '2',  '3',  '-'], 
            ['0',  '.',  '=',  '+']
        ]
        grid = QGridLayout()
        box = QVBoxLayout()
        for i in range(5):
            for j in range(4):
                btn = QPushButton(keys[i][j])
                (self.on_btn)
                #Partition set color back, clear and op character color settings are different from others
                if i == 0 and j in (2, 3):
                    ('background-color:#f0e0d0')
                elif i > 0 and j == 3:
                    ('background-color:#a0f0e0')
                elif i == 4 and j == 2:
                    ('background-color:#f0e0a0')
                else:
                    ('background-color:#d9e4f1')
                (btn, i, j)
        ()
        (10) # Setting Interval
        (grid)
        (box)
    def on_btn(self):
        """Responding to keystrokes."""
        if () == 'Error':
            ('')
        key = ().text()
        if key == 'Clear':
            ('')
        elif key == 'Back':
            (()[:-1]) # Back to the previous step
        elif key == '=':
            try:
                result = str(eval(()))
            except:
                result = 'Error'
            (result)
        else:
            (() + key) # Splicing numbers directly to the end except for the calculation symbol and back/clear/=
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The results of the run are as follows:

(7) Stopwatch

import sys, time
from  import QApplication, QWidget, QLCDNumber, QPushButton, QVBoxLayout
from  import QIcon
from  import QObject, pyqtSignal, QThread
class ClockServer(QObject):
    """Time service"""
    update_time = pyqtSignal(str) # Update time signals
    isRun = False
    def run(self):
        """Initiate services."""
        while True:
            t0 = ()
            while :
                # Output the time difference when the signal is emitted (execute update_time method), because t0 is initialized only once, this time difference is the stopwatch timing, '%.2f'%+number means keep two decimal places.
                self.update_time.emit('%.2f'%(()-t0))  
                (0.01)    # Executed every 0.01s
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Stopwatch')
        (400, 300, 240, 140)
        ()
        ()
    def initUI(self):
         = QLCDNumber()  #QLCDNumber: Display LCD style numbers
        (6)  #Set the limiting digit to 6
        ('0:00') # Initial value
        #Setup button
         = QPushButton('Start')
        ('background-color:#f0d090')
        (self.on_btn)
        #Setup Styles
        vbox = QVBoxLayout()
        ()
        (5)
        ()
        (vbox)
         = ClockServer() # Create a timekeeping service, ts is a service object of your own name.
        .update_time.connect() # Connect update_time signal to LCD display function
         = QThread() # Create threads
        () # Add timekeeping services to the thread
        () # Set the startup method of the timing service to a threaded function
        () # Starting threads
    def on_btn(self):
        """Responding to keystrokes."""
        if () == 'Start':
             = True
            ('Stop')
        else:
             = False
            ('Start')
if __name__ == '__main__':
    app = QApplication()
    win = MyWindow()
    (())

The results of the run are as follows:

(8) Embedded browser or other windows applications

Here using the QAxWidget plugin to achieve, we have to learn how to find the component through the registry uuid, in order to open the component through the uuid. First of all, open the registry editor, find HKEY_CLASSES_ROOT this column.

Then scroll down to find /.1/.2 and you can see that there are three browsers here, just pick one and copy its uuid:

The sample code for the final call to the embedded browser is as follows:

import sys
from  import QApplication, QWidget, QVBoxLayout
from  import QAxWidget
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        ('Embedded Browser')
        (960, 640)
        widget  = QAxWidget(self)  #The QAxWidget class is a QWidget that wraps an ActiveX control and can be used as a window class.
        # If setControl("E:/"); directly will load the word application to open the doc document.
        # Here {8856F961-340A-11D0-A96B-00C04FD705A2} is the UUID of the Microsoft Web Browser control, which stands for Open Browser.
        #UUID from registry HKEY_CLASSES_ROOT\.1\CLSID
        ("{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}")
        ('Navigate(const QString&)', '') #"Navigate(const QString&)" is fixed, followed by the URL of the entry
        box = QVBoxLayout(self)
        (widget )
        ()
        ()
    def center(self):
        """The window is centered on the screen."""
        win_rect = ()
        scr_center = ().availableGeometry().center()
        win_rect.moveCenter(scr_center)
        (win_rect.topLeft())
if __name__ == '__main__':
    app = QApplication() 
    win = MyWindow()
    (())

The results of the run are as follows:

5. Graphical interface Qt Designer

Why put this to the end? There are two reasons: ① is still recommended to write their own interface, more flexible; ② first learn the basics of drawing, and then look at the graphical code converted by the tool is easier to understand, but also easier to do on the basis of the transformation!

(1) Installation

pip install pyqt6-tools
pip install pyside6

pyside6 uses a domestic mirror installation source:pip install pyside6 -i /simple/

Just open it directly after installation. pyside6 path is: your python installation directory\Lib\site-packages\PySide6\

Official Handbook:/qt-5/

(2) Basic Controls

I believe that through the previous study, there are a number of controls you have been more familiar with

①Layouts:

  • Vertical Layout:Vertical Layout
  • Horizontal Layout: Horizontal Layout
  • Gird Layout:Grid Layout
  • FormLayout: Form Layout

②Spacers:

  • Horizontal Spacer: Horizontal Spring
  • Vertical Spacer: Vertical Spring

③Buttons:

  • Push Button:Common Buttons
  • Tool Button:Tool Button
  • Radio Button: radio box (button)
  • Check Box:Multi-Check Box
  • Command Link Button:Command Link Button
  • Dialog Button Box:Button box

④Item View(Model-Based):

  • List View:List View
  • Tree View:Tree View
  • Table View:Table View
  • Column View:Column View

⑤Item Widgets(Item-Based):

  • List Widget:List Widget
  • Tree Widget:Tree Widgets
  • Table Widget:Table Widget

⑥Containers:

  • Group Box:Group Box with Title
  • Scroll Area:Auto Scroll Area
  • Tool Box: drawer control (toolbox)
  • Tab Widget:Tabs
  • Stacked Widget: control stack. The control stack displays only the controls at the top of the stack, and the developer can use the raiseWidget() function to move any other control in the stack to the top of the stack, thus enabling switching between controls.
  • Frame: frame. QFrame frame component is used to hold other controls, can also be used for decoration, generally used as the basis for more complex containers, can also be used in the form as an occupancy control.
  • Widget: The QWidget class is the base class for all user interface objects. QWidget components are invisible when they are created and can contain child controls, which are deleted when the widget is deleted.
  • MDI Area:MDI window display area. Multi-document interface, mainly applicable to the completion of a job that requires the use of multiple documents, mainly applicable to all work without too many documents involved.
  • Dock Widget:Dock window. Can be used as a top-level window floating on the desktop, mainly as a secondary form in the interface, you can see docked windows in many IDEs.
  • QAxWidget: is a QWidget that wraps an ActiveX control.

⑦Input Widgets:

  • Line Edit:Single Line Text Edit Box
  • Text Edit: Multi-line text editing box
  • Plain Text Edit: Multi-line text editor for displaying and editing multi-line simple text (same style as above)
  • Spin Box: Integer Adjustment Button

⑧Double Spin Box:

  • Time Edit: Provides a widget for editing the time.
  • Data Edit: provides a widget for editing dates
  • Date/Time Edit: A widget provided by the QDateTime class for editing dates and times.
  • Dial:Rotary Dashboard. Used for temperature and sound control of universal remotes and other industrial instrument panels.
  • Horizontal Scroll Bar: Horizontal scroll bar, similar to QSlider, can also be used for scrolling area.
  • Vertical Scroll Bar: Vertical scroll bar, similar to QSlider, can also be used for scrolling area.
  • Horizontal Slider: Horizontal slider, set the value by sliding, can be used for numeric input.
  • Vertical Slider: Vertical slider, set the value by sliding, can be used for numeric input.
  • Key Sequence Edit: Text input control. Used to capture shortcut keys. Combined with its internal API, it can be used to set customized shortcuts.

⑨Display Widgets:

  • Label: Commonly used control for displaying text, displaying html hypertext, placing clickable hyperlinks, displaying images, animations and so on.
  • Text Browser: Rich Text Browser with hypertext navigation , you can set its readOnly attribute to false can be edited , the use of ->document()->toPlainText() you can get the contents of the inside out .
  • Graphics Widget: is an extended base item that provides additional functionality on top of QGraphicsItem. It's similar to QWidget in many ways, and I think it's a more flexible version of the big drawing board.
  • LCD Number: Used to display an LCD number.
  • Progress Bar: A horizontal or vertical progress bar.
  • Horizontal Line:Horizontal Line
  • Vertical Line: Vertical line.
  • OpenGL Widget: A widget that renders OpenGL graphics.
  • QQuickWidget: used to load qml file for displaying interface.

(3) Using the tutorial

①Click on the left side of the window control to see all the window controls

② Create a MainWindow

③ Drag in a control such as FormLayout.

④ Double-click the form to bring up the form layout line. ⑤Fill in the label text, and then OK to get a one-line form, I added three lines of form here.

⑥Save the current layout: Ctrl+S, can save the layout file

(7) Open the file and find that it is a standard xml file.

⑧Use the generated .ui file - use the

import sys
from PyQt6 import QtWidgets, uic
app = ()
window = ("D:/tools/python/srcipts2023/")
()
()

⑨ file conversion, enter cmd, go to the directory where the ui file is located and execute: pyuic6 -o {output filename} {input the .ui suffix interface file designed by the designer} e.g.: pyuic6 -o

The ⑩pyuic tool appends Ui_ to the end of the name of the object you defined, which is the object you want to import, e.g., you created MainWindow The generated object is Ui_MainWindow by default.

It is also possible to look directly at the .py file generated using and find that the name of the class object inside is Ui_MainWindow.

The call is implemented through the custom object MyUi inheriting the Ui_MainWindow class (in order to implement the initialization method), and the final code implementation is shown below:

import sys 
from PyQt6 import QtWidgets, uic
from test0628 import Ui_MainWindow
class MyUi(, Ui_MainWindow):
    def __init__(self, *args, obj=None, **kwargs):
        super(MyUi, self).__init__(*args, **kwargs)
        (self)
app = ()
window = MyUi()
()
()

summarize

This article on the Python desktop application development practices of PyQt installation and use of the article is introduced to this, more related Python desktop application PyQt content please search my previous posts or continue to browse the following related articles I hope you will support me in the future!