PyQt advanced interface control and operation

   through the description in the previous sections, I believe that the little partner has a more detailed understanding of the application of PyQt. It is estimated that there is no problem in completing a simple application now. Is that enough? Of course not, leaving aside the complex layout, because this part is implemented by the Qt Dedigner plug-in explained later. We also need to understand PyQt advanced interface controls

1, Table and tree structure

  the problem solved by tables and trees is how to regularly present more data in a control. PyQt provides two control classes to solve this problem, one of which is the control class of table structure; The other is the control class of tree structure.

1. Forms

  under normal circumstances, an application needs to interact with a batch of data (such as arrays and lists), and then output this information in the form of tables. At this time, qtableview class is used. In qtableview, the user-defined data model can be used to display the content, and the data source can be bound through setModel.

1. Code example

import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class Table(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('QTableView Example of table view')
        self.resize(500,300)
        self.model=QStandardItemModel(4,4)
        self.model.setHorizontalHeaderLabels(['Title 1','Title 2','Title 3','Title 4'])
        for row in range(4):
            for column in range(4):
                item=QStandardItem('row %s,column %s'%(row,column))
                self.model.setItem(row,column,item)

        self.tableView=QTableView()
        self.tableView.setModel(self.model)

        layout=QVBoxLayout()
        layout.addWidget(self.tableView)
        self.setLayout(layout)

if __name__ == '__main__':

    app=QApplication(sys.argv)
    table=Table()
    table.show()
    app.exec_()

2. Code analysis:

self.model=QStandardItemModel(4,4)

  set data hierarchy, 4 rows and 4 columns

self.model.setHorizontalHeaderLabels(['Title 1','Title 2','Title 3','Title 4'])

  set the text content of four headers in the horizontal direction

item=QStandardItem('row %s,column %s'%(row,column))
self.model.setItem(row,column,item)

  set the text value of each position

self.tableView=QTableView()
self.tableView.setModel(self.model)

   instantiate the table view and set the model as a user-defined model

3. Supplementary description of attribute parameters

Attribute methodexplain
setHorizontalHeaderLabels()Sets the horizontal label of the QTableWidget table control
setVerticalHeaderLabels()Sets the vertical label of the QTableWidget table control
setItem(int ,int ,QTableWidgetItem)Add values (row number, column number, value) to each cell control of the QTableWidget table control
setEditTriggers(EditTriggers triggers)Set whether the table can be edited. Parameter: qabstractitemview NoEditTriggers; QAbstractItemView.CurrentChanged; QAbstractItemView.DoubleClicked; QAbstractItemView.SelectedClicked; QAbstractItemView.EditKeyPressed; QAbstractItemView.AnyKeyPressed; QAbstractItemView.AllEditTriggers
setSelectionBehaviorSets the selection behavior of the table. parameter
setShowGrid()Set the display of the grid. The default is True
setSpan(int row,int column,int rowSpanCount,int columnSpanCount)To merge cells, change the row and column of the cell, and the number of rows and columns of rowSpancount to be merged

   the above properties are of the QTableView component, and its child control QTableWidget has more properties and more detailed settings.

2. Tree structure

1. User defined tree structure

   QTreeWidget class implements a tree structure, which is mainly used for the display of similar directories.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon, QBrush, QColor
from PyQt5.QtCore import Qt

class TreeWidgetDemo(QMainWindow):
    def __init__(self, parent=None):
        super(TreeWidgetDemo, self).__init__(parent)
        self.setWindowTitle('TreeWidget example')

        self.tree=QTreeWidget()
        #Set the number of columns
        self.tree.setColumnCount(2)
        #Sets the title of the tree control header
        self.tree.setHeaderLabels(['Key','Value'])

        #Set root node
        root=QTreeWidgetItem(self.tree)
        root.setText(0,'Root')# The first parameter specifies the column and the second parameter specifies the value

        # Sets the background color of the root node
        brush_red=QBrush(Qt.red)
        root.setBackground(0,brush_red)
        brush_blue=QBrush(Qt.blue)
        root.setBackground(1,brush_blue)

        #Sets the width of the column of the tree control
        self.tree.setColumnWidth(0,150)

        #Set child node 1
        child1=QTreeWidgetItem()
        child1.setText(0,'child1')
        child1.setText(1,'ios')

        #Optimization 1 sets the state of the node
        child1.setCheckState(0,Qt.Checked)
        root.addChild(child1)

        #Set child node 2
        child2=QTreeWidgetItem(root)
        child2.setText(0,'child2')
        child2.setText(1,'')

        #Set child node 3
        child3=QTreeWidgetItem(child2)
        child3.setText(0,'child3')
        child3.setText(1,'android')

        #Load all properties and child controls of the root node
        self.tree.addTopLevelItem(root)
        # Add response event to node
        self.tree.clicked.connect(self.onClicked)
        #Expand all nodes
        self.tree.expandAll()
        self.setCentralWidget(self.tree)

    def onClicked(self,qmodeLindex):
        item=self.tree.currentItem()
        print('Key=%s,value=%s'%(item.text(0),item.text(1)))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    tree = TreeWidgetDemo()
    tree.show()
    app.exec_()

2. System customization mode

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

if __name__ == '__main__':
    app=QApplication(sys.argv)
#     Mode provided by window system
    model=QDirModel()
#     Create a QTreeView control
    tree=QTreeView()
    #Add mode to control
    tree.setModel(model)
    tree.setWindowTitle('QTreeView example')
    tree.resize(640,480)
    tree.show()
    app.exec_()
tree.setRootIndex(model.index(r'C:\Users\admin\Desktop'))

   the path can be specified through the above code. If not specified, it is the whole system path of running the code.

2, QStackedWidget component

   QStackedWidget is a stack window control, which can fill some widgets, but only one widget can be displayed at the same time. QStackedWidget uses QStackedLayout layout.

import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class StackedExample(QWidget):
    def __init__(self):
        super(StackedExample, self).__init__()
        #Set the initial position and size of the window
        self.setGeometry(300,50,10,10)
        self.setWindowTitle('StackedWidget example')

        self.leftlist=QListWidget()
        self.leftlist.insertItem(0,'contact information')
        self.leftlist.insertItem(1,'personal information')
        self.leftlist.insertItem(2,'Educational level')
        
        #Create three gizmos and QStackedWidget objects, and bind the three controls to QStackedWidget objects
        self.stack1=QWidget()
        self.stack2=QWidget()
        self.stack3=QWidget()
        self.stack=QStackedWidget(self)
        
        self.stack.addWidget(self.stack1)
        self.stack.addWidget(self.stack2)
        self.stack.addWidget(self.stack3)

		#Horizontal layout, adding parts to layout
        HBox=QHBoxLayout()
        HBox.addWidget(self.leftlist)
        HBox.addWidget(self.stack)

        self.setLayout(HBox)

        self.leftlist.currentRowChanged.connect(self.display)
        

        self.stack1UI()
        self.stack2UI()
        self.stack3UI()

    def stack1UI(self):
        layout=QFormLayout()
        layout.addRow('full name',QLineEdit())
        layout.addRow('address',QLineEdit())
        self.stack1.setLayout(layout)

    def stack2UI(self):
        # ABCD form layout, sub horizontal layout
        layout = QFormLayout()
        sex = QHBoxLayout()

        # Add radio button to horizontal layout
        sex.addWidget(QRadioButton('male'))
        sex.addWidget(QRadioButton('female'))

        # Add controls to form layout
        layout.addRow(QLabel('Gender'), sex)
        layout.addRow('birthday', QLineEdit())

        self.stack2.setLayout(layout)

    def stack3UI(self):
        # Horizontal layout
        layout = QHBoxLayout()

        # Add control to layout
        layout.addWidget(QLabel('subject'))
        layout.addWidget(QCheckBox('Physics'))
        layout.addWidget(QCheckBox('High number'))

        self.stack3.setLayout(layout)
    def display(self,i):
        #Sets the index of the currently visible tab
        self.stack.setCurrentIndex(i)
if __name__ == '__main__':
    app=QApplication(sys.argv)
    demo=StackedExample()
    demo.show()
    sys.exit(app.exec_())
        self.leftlist=QListWidget()
        self.leftlist.insertItem(0,'contact information')
        self.leftlist.insertItem(1,'personal information')
        self.leftlist.insertItem(2,'Educational level')

  create a list window and add entries.

   def display(self,i):
        #Sets the index of the currently visible tab
        self.stack.setCurrentIndex(i)

  associate the currentRowChanged signal of QListWidget with the display() slot function to change the view of stacked controls.

3, QTabWidget

The QTabWidget control provides a tab and a page area. The page of the first tab is displayed by default. You can view the corresponding page by clicking each tab. If there are many input fields displayed in one window, these fields can be split and placed in tabs on different pages. QTabWidget blank is similar to QStackedWidget, which can effectively display the controls in the window.

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class TabDemo(QTabWidget):
    def __init__(self,parent=None):
        super(TabDemo, self).__init__(parent)

        #Create 3 tabbed gizmo windows
        self.tab1=QWidget()
        self.tab2=QWidget()
        self.tab3=QWidget()

        #Add three tabs to the top-level window
        self.addTab(self.tab1, "Tab 1")
        self.addTab(self.tab2, "Tab 2")
        self.addTab(self.tab3, "Tab 3")
        self.setGeometry(100,100,400,300)

        #Customized content for each tab
        self.tab1UI()
        self.tab2UI()
        self.tab3UI()

    def tab1UI(self):
        #Table Layout 
        layout=QFormLayout()
        #Single line text input box for adding name and address
        layout.addRow('full name',QLineEdit())
        layout.addRow('address',QLineEdit())
        #Set the subtitle and layout of the tab
        self.setTabText(0,'contact information')
        self.tab1.setLayout(layout)

    def tab2UI(self):
        #ABCD form layout, sub horizontal layout
        layout=QFormLayout()
        sex=QHBoxLayout()

        #Add radio button to horizontal layout
        sex.addWidget(QRadioButton('male'))
        sex.addWidget(QRadioButton('female'))

        #Add controls to form layout
        layout.addRow(QLabel('Gender'),sex)
        layout.addRow('birthday',QLineEdit())

        #Set title and layout
        self.setTabText(1,'Personal details')
        self.tab2.setLayout(layout)

    def tab3UI(self):
        #Horizontal layout
        layout=QHBoxLayout()

        #Add control to layout
        layout.addWidget(QLabel('subject'))
        layout.addWidget(QCheckBox('Physics'))
        layout.addWidget(QCheckBox('High number'))

        #Set subtitles and layout
        self.setTabText(2,'Educational level')
        self.tab3.setLayout(layout)
        
        
if __name__ == '__main__':
    app=QApplication(sys.argv)
    demo=TabDemo()
    demo.show()
    sys.exit(app.exec_())

4, Multi window mode QMidArea

  a typical GUI application may have multiple windows. Tab controls and stack window controls allow one of them to be used at a time. However, many times this method is not very useful because the views of other windows are hidden. So there is QMdiArea.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class MainWindow(QMainWindow):
    count=0
    def __init__(self,parent=None):
        super(MainWindow, self).__init__(parent)
        #Instantiate the Qmidarea area
        self.mdi=QMdiArea()
        #Set as intermediate control
        self.setCentralWidget(self.mdi)

        #Instantiate menu bar
        bar=self.menuBar()
        #Add main menu
        file=bar.addMenu('File')
        #Add submenu
        file.addAction('New')
        file.addAction('cascade')
        file.addAction('Tiled')

        #Click QAction to bind the customized slot function (pass the value [QAction])
        file.triggered[QAction].connect(self.windowaction)
        #Set the title of the main window
        self.setWindowTitle("MDI demo")
    def windowaction(self,q):
        if q.text()=='New':
            #Add a sub window
            MainWindow.count=MainWindow.count+1

            #Instantiate multi document interface object
            sub=QMdiSubWindow()
            #Add internal controls to sub
            sub.setWidget(QTextEdit())
            #Set the title of the new sub window
            sub.setWindowTitle('subWindow'+str(MainWindow.count))

            #Add sub window to Mdi area
            self.mdi.addSubWindow(sub)
            #Sub window display
            sub.show()
        if q.text()=='cascade':
            #cascadeSubWindows(): arrange sub windows to be cascaded in Mdi area
            self.mdi.cascadeSubWindows()
        if q.text()=='Tiled':
            #tileSubWindow(): arrange the sub windows to be tiled in the Mdi area
            self.mdi.tileSubWindow()
if __name__ == '__main__':
    app=QApplication(sys.argv)
    demo=MainWindow()
    demo.show()
    sys.exit(app.exec_())

Its more attributes are as follows:

5, Window docking QDockWidget

  QDockWidget is a window control that can be docked in QMainWindow. It can be kept floating or attached to the main window as a child window at a specified position. The main window object of QMainWindow class retains an area for docking the window, which is around the center of the control.

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class DockDemo(QMainWindow):
    def __init__(self,parent=None):
        super(DockDemo, self).__init__(parent)
        #Set horizontal layout
        layout=QHBoxLayout()
        #Instantiate menu bar
        bar=self.menuBar()
        #Create the main menu file and add submenus to it
        file=bar.addMenu('File')
        file.addAction('New')
        file.addAction('Save')
        file.addAction('quit')

        #Create QDockWidget window (title, own window)
        self.items=QDockWidget('Dockable',self)

        #Instantiate the list window and add several entries
        self.listWidget=QListWidget()
        self.listWidget.addItem('Item1')
        self.listWidget.addItem('Item2')
        self.listWidget.addItem('Item3')
        self.listWidget.addItem('Item4')

        #Set QWidget in the window area and add list control
        self.items.setWidget(self.listWidget)

        #Set whether the dock window can float, True, run floating outside, automatically separate from the main interface, False, float in the main window by default, and can be separated manually
        self.items.setFloating(False)

        #Set QTextEdit as the central gizmo
        self.setCentralWidget(QTextEdit())
        #Place the window to the right of the central gizmo
        self.addDockWidget(Qt.RightDockWidgetArea,self.items)

        self.setLayout(layout)
        self.setWindowTitle('Dock example')
if __name__ == '__main__':
    app=QApplication(sys.argv)
    demo=DockDemo()
    demo.show()
    sys.exit(app.exec_())

6, Call other application QAxWidget

import sys
from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QPushButton, QFileDialog
from PyQt5.QtCore import Qt
 

class Window(QWidget):
    
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        self.setFixedSize(900, 600)
        layout = QVBoxLayout(self)
        self.axWidget = QAxWidget("ObjectName", self)
        layout.addWidget(self.axWidget)
        layout.addWidget(QPushButton('open word', self, clicked=self.onOpenWord))
        self.axWidget.setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}")
#         self.axWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
        self.axWidget.setProperty("DisplayAlerts", False)
        self.axWidget.setProperty("DisplayScrollBars", True)
        self.axWidget.dynamicCall("Navigate(const QString&)", "https://map.baidu.com/@13523265.31,3641114.64,12z")

    def onOpenWord(self):
        path, _ = QFileDialog.getOpenFileName(self, 'Please select Word file', '', 'word(*.docx *.doc)')
        if not path:
            return
        # Do not show form
#         self.axWidget.resetControl()
        self.axWidget.setControl(path)
        self.axWidget.dynamicCall('SetVisible (bool Visible)', 'false')
#         self.axWidget.setProperty('DisplayAlerts', False)
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Window()
    w.show()
    app.exec()

7, QWebEngineView

   PyQt5 uses the QWebEngineView control to display HTML pages. The old version of QWebView class is no longer maintained, because QWebEngineView uses the CHromium kernel to bring users a better experience.
   the QWebEngineView control uses the load() function to load a web page. In fact, it uses the HTTP Get method to load a web page. This control can load local web pages or external web pages. Its core code is as follows

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.webEngineView = QtWebEngineWidgets.QWebEngineView(self.centralwidget)
        self.webEngineView.setUrl(QtCore.QUrl("https://www.baidu.com/"))
        self.webEngineView.setZoomFactor(1.0)
        self.webEngineView.setObjectName("webEngineView")
        self.horizontalLayout.addWidget(self.webEngineView)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

from PyQt5 import QtWebEngineWidgets


class Example(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)



if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    win = Example()
    win.show()
    app.exit(app.exec_())

   there is a problem with the above code, that is, it is impossible to click in the loaded web page. Therefore, it is said on the Internet that QtWebEngineWidgets should be reconstructed. The code is as follows:

import sys
import os
import datetime
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtWebEngineWidgets import QWebEngineView,QWebEngineSettings
################################################
#######Create main window
################################################
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle('My Browser')
        self.showMaximized() # maximize window
#         self.setWindowFlags(Qt.FramelessWindowHint) # Window structure format. There is no close button above here

        #####Create tabwidget
        self.tabWidget = QTabWidget()
        self.tabWidget.setTabShape(0)# Set the style of the label. 0 represents a circular label and 1 represents a triangle
        self.tabWidget.setDocumentMode(True) # In document mode, pictures will be displayed normally when opened
        self.tabWidget.setMovable(True)# tab window movable 
        self.tabWidget.setTabsClosable(True)# Open tab window close button
        self.tabWidget.tabCloseRequested.connect(self.close_Tab)
        self.setCentralWidget(self.tabWidget)

        ####First tab
        self.webview = WebEngineView(self)   #self is required, which is to pass the main window to the browser as a parameter
        self.webview.load(QUrl("http://www.baidu.com"))
        self.create_tab(self.webview)


    #Create tab
    def create_tab(self,webview):
        self.tab = QWidget()
        self.tabWidget.addTab(self.tab, "New tab")
        self.tabWidget.setCurrentWidget(self.tab)
        #####
        self.Layout = QHBoxLayout(self.tab)
        self.Layout.setContentsMargins(0, 0, 0, 0)
        self.Layout.addWidget(webview)



    #Close tab
    def close_Tab(self,index):
        if self.tabWidget.count()>1:
            self.tabWidget.removeTab(index)
        else:
            self.close()   # When there is only one tab, close the main window





################################################
#######Create browser
################################################
class WebEngineView(QWebEngineView):

    def __init__(self,mainwindow,parent=None):
        super(WebEngineView, self).__init__(parent)
        self.mainwindow = mainwindow
        ##############
        self.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)      #Support video playback
        self.page().windowCloseRequested.connect(self.on_windowCloseRequested)     #Page close request
        self.page().profile().downloadRequested.connect(self.on_downloadRequested) #Page download request



    #  Support page close request
    def on_windowCloseRequested(self):
        the_index = self.mainwindow.tabWidget.currentIndex()
        self.mainwindow.tabWidget.removeTab(the_index)


    #  Support page download button
    def on_downloadRequested(self,downloadItem):
        if  downloadItem.isFinished()==False and downloadItem.state()==0:
            ###Generate file storage address
            the_filename = downloadItem.url().fileName()
            if len(the_filename) == 0 or "." not in the_filename:
                cur_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
                the_filename = "Download File" + cur_time + ".xls"
            the_sourceFile = os.path.join(os.getcwd(), the_filename)

            ###Download File
            # downloadItem.setSavePageFormat(QWebEngineDownloadItem.CompleteHtmlSaveFormat)
            downloadItem.setPath(the_sourceFile)
            downloadItem.accept()
            downloadItem.finished.connect(self.on_downloadfinished)


    #  Download end trigger function
    def on_downloadfinished(self):
        js_string = '''
        alert("Download succeeded. Please go to the same directory of the software to find the downloaded file!"); 
        '''
        self.page().runJavaScript(js_string)




    # Override createwindow()
    def createWindow(self, QWebEnginePage_WebWindowType):
        new_webview = WebEngineView(self.mainwindow)

        self.mainwindow.create_tab(new_webview)

        return new_webview


################################################
#######Program introduction
################################################
if __name__ == "__main__":
    app = QApplication(sys.argv)
    QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)   #This sentence solves the error warning: ERROR:gl_context_wgl.cc(78)] Could not share GL contexts.
    the_mainwindow = MainWindow()
    the_mainwindow.show()
    app.exec_()

Now that I've explained it, I'm estimated to have an understanding of the more advanced application of PyQt. It's being updated continuously

👊🙈 ...

Keywords: UI PyQt5

Added by viko20 on Thu, 27 Jan 2022 06:24:03 +0200