093.PyQt5_QTDesigner_具体使用

  

我 的 个 人 主 页: 失心疯的个人主页
入 门 教 程 推 荐 : Python零基础入门教程合集
虚 拟 环 境 搭 建 : Python项目虚拟环境(超详细讲解)
PyQt5 系 列 教 程: Python GUI(PyQt5)文章合集
Oracle数据库教程: Oracle数据库文章合集
优 质 资 源 下 载 : 资源下载合集

QTDesigner 具体使用

  • QtDesigner界面
    • 工具界面包含
    • 1、菜单栏
    • 2、工具栏
    • 3、控件盒子
    • 4、控件列表
    • 5、属性面板
    • 6、信号与槽、动作、资源面板
    • 093.PyQt5_QTDesigner_具体使用_第1张图片

  • 控件操作
    • 添加控件:直接从左侧控件盒子中拖到窗体中
    • 控件是按功能或者布局分组
      Layoutts                        # 布局控件
      Spacers                         # 弹簧控件
      Buttons                         # 按钮控件
      Item Views(Model-Based)         # 
      Item Widgets(Item-Based)        # 
      Containers                      # 容器控件
      Input Widgets                   # 输入控件
      Display Widgets                 # 展示控件
      
    • 控件属性在右侧属性面板,可以直接搜索
    • 常用属性,在鼠标右键菜单中
    • 右侧属性面板搜索栏后面的“+”,可以给控件添加动态属性, setProperty(属性名,属性值)
  • 样式表和资源使用
    • 在控件属性面板直接搜索stylesheet —— 点击属性后面的...
    • 在控件上鼠标右键 —— Change styleSheet...(改变样式表)
    • 编辑样式表,可以直接添加资源、渐变色、颜色(前景、背景、边框)、字体等
    • 界面如下
      • 093.PyQt5_QTDesigner_具体使用_第2张图片

    • 添加资源一般都是操作图片资源(背景图片、边框图片、前景图片)
    • ui文件并不能直接通过文件路径使用本地文件,必须通过资源管理文件的名称列表和资源路径列表进行关联使用
      • 093.PyQt5_QTDesigner_具体使用_第3张图片

    • 后期,我们将资源管理文件(.qrc)转换成py文件,py文件就包含了qrc文件代码和资源文件(资源文件以二进制的方式保存在py文件中)
  • 控件类型提升(自定义类使用)
  • 首先我们写一个自定义类Btn继承自QPushbutton
    # 创建一个MyBtn.py文件,代码如下
    
    from PyQt5.Qt import *
    
    
    class Btn(QPushButton):
        def __init__(self, *args, **kwargs):
            super(Btn, self).__init__(*args, **kwargs)
            print('自定义按钮类')
    
  • 在QtDesigner中使用这个自定义类
    • 1、在窗体中添加一个该自定义类的父类控件
    • 2、鼠标右击该控件 —— Promote to...(提升为...)
      • 093.PyQt5_QTDesigner_具体使用_第4张图片

    • 3、填写好要提升为那种类,该类所在的模块名(py文件名,这里注意不要写文件后缀),点击Add(添加)
    • 4、选中添加的 提升的类,点击 Promote(提升)
      • 093.PyQt5_QTDesigner_具体使用_第5张图片

    • 5、提升完了之后,他们直接Ctrl+R运行,并没有看到任何效果
    • 6、此时,我们需要将ui文件转换为py文件,再直接运行py文件就能看到效果了(转换后的py文件如下)
      # -*- coding: utf-8 -*-
      
      # Form implementation generated from reading ui file 'test.ui'
      #
      # Created by: PyQt5 UI code generator 5.15.9
      #
      # WARNING: Any manual changes made to this file will be lost when pyuic5 is
      # run again.  Do not edit this file unless you know what you are doing.
      
      
      from PyQt5 import QtCore, QtGui, QtWidgets
      
      
      class Ui_Form(object):
          def setupUi(self, Form):
              Form.setObjectName("Form")
              Form.resize(400, 300)
              Form.setStyleSheet("")
              self.pushButton = Btn(Form)
              self.pushButton.setGeometry(QtCore.QRect(100, 70, 121, 91))
              self.pushButton.setObjectName("pushButton")
      
              self.retranslateUi(Form)
              QtCore.QMetaObject.connectSlotsByName(Form)
      
          def retranslateUi(self, Form):
              _translate = QtCore.QCoreApplication.translate
              Form.setWindowTitle(_translate("Form", "Form"))
              self.pushButton.setText(_translate("Form", "PushButton"))
      from MyBtn import Btn
      
      
      if __name__ == "__main__":
          import sys
          app = QtWidgets.QApplication(sys.argv)
          Form = QtWidgets.QWidget()
          ui = Ui_Form()
          ui.setupUi(Form)
          Form.show()
          sys.exit(app.exec_())
      
      
    • 其中有两句代码说明了这个按钮已经提升为我们自定义的类了
      • 093.PyQt5_QTDesigner_具体使用_第6张图片

    • 7、自定义类添加信号与槽函数:回到QtDesigner工具,切换到信号与槽编辑模式
      • 093.PyQt5_QTDesigner_具体使用_第7张图片

    • 8、 信号与槽添加完成之后,我们需要重新将ui文件转换为py文件,然后在py文件中编写具体的槽函数内容
      # -*- coding: utf-8 -*-
      
      # Form implementation generated from reading ui file 'test.ui'
      #
      # Created by: PyQt5 UI code generator 5.15.9
      #
      # WARNING: Any manual changes made to this file will be lost when pyuic5 is
      # run again.  Do not edit this file unless you know what you are doing.
      
      
      from PyQt5 import QtCore, QtGui, QtWidgets
      
      
      class Ui_Form(object):
          def setupUi(self, Form):
              Form.setObjectName("Form")
              Form.resize(412, 300)
              Form.setStyleSheet("")
              self.pushButton = Btn(Form)
              self.pushButton.setGeometry(QtCore.QRect(100, 70, 121, 91))
              self.pushButton.setObjectName("pushButton")
      
              self.retranslateUi(Form)
              self.pushButton.clicked.connect(Form.btn_click) # type: ignore
              QtCore.QMetaObject.connectSlotsByName(Form)
      
          def retranslateUi(self, Form):
              _translate = QtCore.QCoreApplication.translate
              Form.setWindowTitle(_translate("Form", "Form"))
              self.pushButton.setText(_translate("Form", "PushButton"))
      from MyBtn import Btn
      
      
      if __name__ == "__main__":
          import sys
          app = QtWidgets.QApplication(sys.argv)
          Form = QtWidgets.QWidget()
          Form.btn_click = lambda :print('自定义按钮被点击了')
          ui = Ui_Form()
          ui.setupUi(Form)
          Form.show()
          sys.exit(app.exec_())
      
      
  • 布局管理器使用
    • 窗体中有控件未设置布局时,右侧对象查看器中窗体对象会显示打破布局标志
      • 093.PyQt5_QTDesigner_具体使用_第8张图片

    • 窗体中控件全部设置布局后,右侧对象查看器中窗体对象打破布局标志会消失
      • 093.PyQt5_QTDesigner_具体使用_第9张图片

    • 为整个窗体设置布局
      • 1、在窗体界面选中窗体或者点击右侧对象查看器中的窗体
      • 2、点击工具栏上相应的布局按钮或者鼠标右键 —— 布局(lay out) —— 选择相应布局
    • 为多个控件设置布局
      • 1、在窗体界面框选选中多个控件或者右侧对象查看器中通过shift或ctrl键选中需要布局的多个控件
      • 2、点击工具栏上相应的布局按钮或者鼠标右键 —— 布局(lay out) —— 选择相应布局
    • 取消布局
      • 1、在窗体界面选中已布局的控件或者右侧对象查看器中选中已布局的控件
      • 2、点击工具栏上打破布局按钮或者鼠标右键 —— 布局(lay out) —— 打破布局
    • sizePolicy属性设置策略和拉伸系数
      Horizontal Policy       # 垂直策略
      Vertical Policy         # 水平策略
      # 策略参数
          Fixed               # 固定尺寸
          Minimum             # 最小尺寸
          Maximum             # 最大尺寸
          Preferred           # 优先分配空间
          MinimumEXpanding    # 
          EXpanding           # 尽可能占据更多空间
          Lgnored
          
      Horizontal Stretch      # 垂直拉伸系数
      Vertical Stretch        # 水平拉伸系数
      
    • 通过minimumSize和MaximunSize设置为相同值来固定控件的尺寸(相当于fixedSize)
  • ui文件使用
    • 方法一:直接调用ui文件
      • 直接调用ui文件需要导入模块
        from PyQt5.uic import loadUi
        
      • 加载ui文件语法
        loadUi(ui文件名, self)
        
      • 示例(创建一个test_login.py文件)
        from PyQt5.Qt import *
        import sys
        
        
        class Window(QWidget):
            def __init__(self):
                super().__init__()
                self.setWindowTitle('ui文件使用')
                self.resize(500, 300)
                self.addWidget()
        
            def addWidget(self):
                from PyQt5.uic import loadUi
                loadUi('login.ui', self)
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
            window = Window()
            window.show()
        
            sys.exit(app.exec_())
            
        
      • 通过直接加载ui文件的方式可以直接运行,但是经常会出现一些奇奇怪怪的问题,比如资源图片加载不成功,信号与槽函数连接报错等
      • 我们可以通过ui文件转换后的py文件来分析一下
        • 093.PyQt5_QTDesigner_具体使用_第10张图片

      • 图中可以看出来,其实ui文件就是自定义了一个类Ui_Form,继承自object
      • 在该自定义类中,通过setupUi方法将所有的控件都赋值给了这个类的各个属性
      • setupUi方法有一个形参Form,并且在创建所有控件的时候,将Form设置为了所有控件的父对象
      • 那么,通过loadUi方法读取ui文件时,将self作为实参传递进去(这里的self,是test_login.py文件中的window对象)
      • 相当于ui文件中的所有Form即为window对象(也就是说window是所有子控件的父对象)
      • 我们可以在test_login.py文件中,通过self.children()来看一下window对象有哪些子控件
      • 也可以通过dir(self)来获取window对象内部有哪些属性可以使用(可以通过这些属性来获取到子控件对象)
        • 093.PyQt5_QTDesigner_具体使用_第11张图片

      • 这里dir函数实际是获取到如图中红色框选部分的属性,然后再通过这些属性获取到子控件对象
      • 拿到了子控件对象,那就可以对子控件进行一系列的操作了,比如连接信号与槽,获取子控件内容等...
        from PyQt5.Qt import *
        import sys
        
        
        class Window(QWidget):
            def __init__(self):
                super().__init__()
                self.setWindowTitle('ui文件使用')
                self.resize(500, 300)
                self.addWidget()
        
            def addWidget(self):
                from PyQt5.uic import loadUi
                loadUi('login.ui', self)
                # print(self.children())
                # print(dir(self))
        
                self.btn_login.clicked.connect(self.btn_click)
        
            def btn_click(self):
                account = self.le_username.text()
                password = self.le_password.text()
        
                print(account, password)
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
            window = Window()
            window.show()
        
            sys.exit(app.exec_())
        
        
      • 方法一在后期很少使用,所以了解即可。后期主要还是使用方法二
    • 方法二:将ui文件转换成py文件后再加载
      • 1、通过pyuic5工具将ui文件转换成py文件
      • 2、根据生成的py文件,创建另外一个py文件进行加载
      • 3、对ui文件中使用的资源进行处理,通过pyrcc5工具将qrc文件转换成py文件
      • 4、补全槽函数,给ui文件中使用到的槽函数写具体的执行过程
      • 注意:后期我们不要修改界面文件(ui文件转换的py文件),而是通过新建一个py文件去加载这个界面文件
      • 因为我们的界面需要改变的时候,修改ui文件之后,再次转换成py文件会讲之前的py文件全部覆盖,手动修改的内容将全部丢失
      • 我们的设计要做到界面与逻辑分离
      • A、创建py文件,以包的模式加载界面文件,通过包里面的类创建对象
        • ui文件转py文件(login.py)
        # login.py 文件
        
        # -*- coding: utf-8 -*-
        
        # Form implementation generated from reading ui file 'login.ui'
        #
        # Created by: PyQt5 UI code generator 5.15.9
        #
        # WARNING: Any manual changes made to this file will be lost when pyuic5 is
        # run again.  Do not edit this file unless you know what you are doing.
        
        
        from PyQt5 import QtCore, QtGui, QtWidgets
        
        
        class Ui_Form(object):
            def setupUi(self, Form):
                Form.setObjectName("Form")
                Form.resize(395, 186)
                Form.setStyleSheet("")
                self.verticalLayout = QtWidgets.QVBoxLayout(Form)
                self.verticalLayout.setContentsMargins(0, 0, 0, 8)
                self.verticalLayout.setSpacing(6)
                self.verticalLayout.setObjectName("verticalLayout")
                self.widget = QtWidgets.QWidget(Form)
                sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
                sizePolicy.setHorizontalStretch(0)
                sizePolicy.setVerticalStretch(0)
                sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
                self.widget.setSizePolicy(sizePolicy)
                self.widget.setStyleSheet("background-image: url(:/back/images/3.jpg);")
                self.widget.setObjectName("widget")
                self.verticalLayout.addWidget(self.widget)
                self.horizontalLayout = QtWidgets.QHBoxLayout()
                self.horizontalLayout.setObjectName("horizontalLayout")
                spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
                self.horizontalLayout.addItem(spacerItem)
                self.label_1 = QtWidgets.QLabel(Form)
                self.label_1.setObjectName("label_1")
                self.horizontalLayout.addWidget(self.label_1)
                self.le_username = QtWidgets.QLineEdit(Form)
                self.le_username.setObjectName("le_username")
                self.horizontalLayout.addWidget(self.le_username)
                spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
                self.horizontalLayout.addItem(spacerItem1)
                self.verticalLayout.addLayout(self.horizontalLayout)
                self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
                self.horizontalLayout_2.setObjectName("horizontalLayout_2")
                spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
                self.horizontalLayout_2.addItem(spacerItem2)
                self.label_2 = QtWidgets.QLabel(Form)
                self.label_2.setObjectName("label_2")
                self.horizontalLayout_2.addWidget(self.label_2)
                self.le_password = QtWidgets.QLineEdit(Form)
                self.le_password.setObjectName("le_password")
                self.horizontalLayout_2.addWidget(self.le_password)
                spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
                self.horizontalLayout_2.addItem(spacerItem3)
                self.verticalLayout.addLayout(self.horizontalLayout_2)
                self.btn_login = QtWidgets.QPushButton(Form)
                self.btn_login.setMinimumSize(QtCore.QSize(270, 30))
                self.btn_login.setMaximumSize(QtCore.QSize(270, 30))
                self.btn_login.setStyleSheet("")
                self.btn_login.setObjectName("btn_login")
                self.verticalLayout.addWidget(self.btn_login, 0, QtCore.Qt.AlignHCenter)
                self.widget.raise_()
                self.btn_login.raise_()
                self.label_1.setBuddy(self.le_username)
                self.label_2.setBuddy(self.le_password)
        
                self.retranslateUi(Form)
                QtCore.QMetaObject.connectSlotsByName(Form)
        
            def retranslateUi(self, Form):
                _translate = QtCore.QCoreApplication.translate
                Form.setWindowTitle(_translate("Form", "Form"))
                self.label_1.setText(_translate("Form", "账号(&N):"))
                self.label_2.setText(_translate("Form", "密码(&P):"))
                self.btn_login.setText(_translate("Form", "登录"))
        import backgroud_rc
        
        
        if __name__ == "__main__":
            import sys
            app = QtWidgets.QApplication(sys.argv)
            Form = QtWidgets.QWidget()
            ui = Ui_Form()
            ui.setupUi(Form)
            Form.show()
            sys.exit(app.exec_())
        
        
        • qrc文件转py文件(background_rc.py)
        # 二进制文件太长,此处省略
        
        • 创建py文件加载界面文件(load_login.py)
        from PyQt5.Qt import *
        import sys
        
        
        class Window(QWidget):
            def __init__(self):
                super().__init__()
                self.setWindowTitle('界面文件加载')
                self.resize(500, 300)
                self.addWidget()
        
            def addWidget(self):
                from login import Ui_Form       # 导入模块
                ui = Ui_Form()                  # 通过模块内的类创建对象
                ui.setupUi(self)                # 调用对象的setupUi方法,并将self传递进去
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
            window = Window()
            window.show()
        
            sys.exit(app.exec_())
        
      • B、利用python的多继承特性,来继承界面文件里面的自定义类
      • 创建py文件(load_login_2.py)
        from PyQt5.Qt import *
        import sys
        from login import Ui_UserLogin
        
        
        class Window(QWidget, Ui_UserLogin):
            def __init__(self):
                super().__init__()
                self.resize(500, 300)
                self.setupUi(self)      # 调用父类的setuoUi方法
                self.addWidget()
                self.setWindowTitle('用户登录')
                icon = QIcon('icon/1.ico')
                self.setWindowIcon(icon)
        
            def addWidget(self):
                self.btn_login.clicked.connect(self.btn_click)
        
            def btn_click(self):
                account = self.le_username.text()
                password = self.le_password.text()
                print(account, password)
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
            window = Window()
            window.show()
        
            sys.exit(app.exec_())
        
      • 运行结果
      • 093.PyQt5_QTDesigner_具体使用_第12张图片


原理讲解

  • 这里我们做一个很简单的界面(一个窗体上面放了一个标签),来解读一下文件代码
    • 093.PyQt5_QTDesigner_具体使用_第13张图片
  • 将ui_FormHello.ui文件转成ui_FormHello.py文件,代码如下:
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class Ui_Form(object):
        def setupUi(self, Form):
            Form.setObjectName("Form")
            Form.resize(400, 200)
            self.label = QtWidgets.QLabel(Form)
            self.label.setGeometry(QtCore.QRect(100, 70, 72, 15))
            self.label.setObjectName("label")
    
            self.retranslateUi(Form)
            QtCore.QMetaObject.connectSlotsByName(Form)
    
        def retranslateUi(self, Form):
            _translate = QtCore.QCoreApplication.translate
            Form.setWindowTitle(_translate("Form", "MainWindow窗体"))
            self.label.setText(_translate("Form", "标签"))
    
    
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        Form = QtWidgets.QWidget()
        ui = Ui_Form()
        ui.setupUi(Form)
        Form.show()
        sys.exit(app.exec_())
    
  • UI文件原理解析
    • 分析这个文件的代码,可以发现这个文件实际上定义了一个类Ui_Form,仔细分析一下这段代码,可以发现其原理和功能
    • (1)Ui_Form类的父类是object,而不是QWidget。
    • (2)Ui_Form类定义了一个函数setupUi(),其接口定义为
      def setupUi(self, Form):
      
    • 其传入的参数有两个,其中self是函数自己,Python中的self类似于C++语言中的this;Form是一个传入的参数,而不是在Ui_Form类里定义的一个变量
    • setupUi()函数的前两行语句是:
      Form.setObjectName("Form")
      Form.resize(400, 200)
      
    • 所以,Form是窗体,是一个QWidget对象,其名称就是在UI Designer里设计的窗体的objectName。但是这个Form不是在类Ui_Form里创建的,而是作为一个参数传入的。
    • (3)创建了一个QLabel类型的对象labe,创建的语句是
      self.label = QtWidgets.QLabel(Form)
      
    • label定义为Ui_Form类的一个公共属性(类似于C++的公共变量),它的父容器是Form,所以label在窗体Form上显示。后面的语句又设置了label的显示位置、大小
    • 注意:在Python语言中,类的接口包括属性(attribute)和方法(method),属性又分为类属性和类的实例属性
    • (4)setupUi()函数的倒数第二行调用了Ui_Form类里定义的另外一个函数retranslateUi(),这个函数设置了窗体的标题、标签labe的文字。实际上,retranslateUi()函数集中设置了窗体上所有的字符串,利于实现软件的多语言界面
    • (5)setupUi()函数的最后一行语句用于窗体上各组件的信号与槽函数的自动连接
    • 特点说明
    • 经过pyuic5编译后,ui_FormHello.ui文件转换为一个对应的Python的类定义文件ui_FormHello.py,类的名称是Ui_Form。有如下的特点和功能。
      • (1)Ui_FormHello.py文件里的类名称Ui_Form与Ui_FormHello.ui文件里窗体的objectName有关,是在窗体的objectName名称前面加“Ui_”自动生成的。
      • (2)Ui_Form类的函数setupUi()用于窗体的初始化,它创建了窗体上的所有组件并设置其属性。
      • (3)Ui_Form类并不创建窗体Form,窗体Form是由外部传入的,作为所有界面组件的父容器
  • 多继承方法原理解析
    • 将窗体UI文件Ui_FormHello.ui编译转换为Python的类定义文件ui_FormHello.py后,就可以使用其中的类Ui_FormHello创建GUI应用程序。
    • Python的面向对象编程支持使用多继承,编写一个程序appMain2.py,代码如下:
      # appMain2.py 多继承方法
      import sys
      from PyQt5.QtWidgets import QWidget, QApplication
      from ui_FormHello import Ui_Form
      
      
      class QmyWidget(QWidget,Ui_Form):
          def __init__(self, parent=None):
              super().__init__(parent)        # 调用父类构造函数,创建QWidget窗体
              self.Lab="多重继承的QmyWidget"  # 新定义的一个变量
              self.setupUi(self)              # self是QWidget窗体,可作为参数传给setupUi()
              self.LabHello.setText(self.Lab)
              
      if __name__ == "__main__":
          app = QApplication(sys.argv)        #创建app
          myWidget=QmyWidget()
          myWidget.show()
          myWidget.btnClose.setText("不关闭了")
          sys.exit(app.exec_())
      
    • (1)采用多继承的方式定义了一个类QmyWidget,称这个类为窗体的业务逻辑类,它的父类是QWidget和Ui_Form。(2)在这个类的构造函数中,首先用函数super()获取父类,并执行父类的构造函数,代码是:
      super().__init__(parent)
      
    • 在多继承时,使用super()得到的是第一个基类,在这里就是QWidget。所以,执行这条语句后,self就是一个QWidget对象。
    • (3)调用setupUi()函数创建UI窗体,即
      self.setupUi(self)
      
    • 因为QmyWidget的基类包括Ui_Form类,所以可以调用Ui_Form类的setupUi()函数。同时,经过前面调用父类的构造函数,self是一个QWidget对象,可以作为参数传递给setupUi()函数,正好作为各组件的窗体容器
    • 通过这样的多继承,Ui_Form类中定义的窗体上的所有界面组件对象就变成了新定义的类QmyWidget的公共属性,可以直接访问这些界面组件。例如:
      self.Lab="多重继承的QmyWidget"      # 新定义的一个属性
      self.LabHello.setText(self.Lab)
      
    • 在应用程序创建QmyWidget类的实例对象myWidget后,通过下面的语句设置了界面上按钮的显示文字
      myWidget.btnClose.setText("不关闭了")
      
    • 这种多继承方式有其优点,也有其缺点,表现为以下两方面
      • (1)界面上的组件都成为窗体业务逻辑类QmyWidget的公共属性,外界可以直接访问。优点是访问方便,缺点是过于开放,不符合面向对象严格封装的设计思想。
      • (2)界面上的组件与QmyWidget类里新定义的属性混合在一起了,不便于区分。例如,在构造函数中有这样一条语句
        self.LabHello.setText(self.Lab)
        
      • 其中,self.LabHello是窗体上的标签对象,而self.Lab是QmyWidget类里新定义的一个属性。如果没有明确的加以区分的命名规则,当窗体上的界面组件较多,且窗体业务逻辑类里定义的属性也很多时,就难以区分哪个属性是界面上的组件,哪个属性是在业务逻辑类里新定义的,这样是不利于界面与业务逻辑分离的。
  • 单继承与界面独立封装方法原理解析
    • 针对多继承存在的一些问题,改用单继承的方法,编写另一个程序appMain.py,其代码如下:
      # appMain.py 单继承方法,能更好地进行界面与逻辑的分离
      import sys
      from PyQt5.QtWidgets import QWidget, QApplication
      from ui_FormHello import Ui_Form
      
      
      class QmyWidget(QWidget):
          def __init__(self, parent=None):
              super().__init__(parent)        # 调用父类构造函数,创建QWidget窗体
              self.__ui=Ui_Form()        # 创建UI对象
              self.__ui.setupUi(self)         # 构造UI
              self.Lab="单继承的QmyWidget"
              self.__ui.LabHello.setText(self.Lab)
          
          def setBtnText(self, aText):
              self.__ui.btnClose.setText(aText)
              
      if __name__ == "__main__":
          app = QApplication(sys.argv)        # 创建app,用QApplication类
          myWidget=QmyWidget()
          myWidget.show()
          myWidget.setBtnText("间接设置")
          sys.exit(app.exec_())
      
    • (1)新定义的窗体业务逻辑类QmyWidget只有一个基类QWidget
    • (2)在QmyWidget的构造函数中,首先调用父类(也就是QWidget)的构造函数,这样self就是一个QWidget对象。
    • (3)显式地创建了一个Ui_Form类的私有属性self.__ui,即
      self.__ui=Ui_Form()     # 创建UI对象
      
    • 私有属性self.__ui包含了可视化设计的UI窗体上的所有组件,所以,只有通过self.__ui才可以访问窗体上的组件,包括调用其创建界面组件的setupUi()函数。
    • 提示:Python语言的类定义通过命名规则来限定元素对外的可见性,属性或方法名称前有两个下划线表示是私有的,一个下划线表示模块内可见,没有下划线的就是公共的
    • (4)由于self.__ui是QmyWidget类的私有属性,因此在应用程序中创建的QmyWidget对象myWidget不能直接访问myWidget.__ui,也就无法直接访问窗体上的界面组件
    • 为了访问窗体上的组件,可以在QmyWidget类里定义接口函数,例如函数setBtnText()用于设置窗体上按钮的文字。在应用程序里创建QmyWidget对象的实例myWidget,通过调用setBtnText()函数间接修改界面上按钮的文字,即
      myWidget.setBtnText("间接设置")
      
    • 这种单继承的方式,有如下特点:
      • (1)可视化设计的窗体对象被定义为QmyWidget类的一个私有属性self.__ui,在QmyWidget类的内部对窗体上的组件的访问都通过这个属性实现,而外部无法直接访问窗体上的对象,这更符合面向对象封装隔离的设计思想。
      • (2)窗体上的组件不会与QmyWidget里定义的属性混淆。例如,下面的语句:
        self.__ui.LabHello.setText(self.Lab)
        
      • self.__ui.LabHello表示窗体上的标签对象LabHello,它是self.__ui的一个属性;
      • self.Lab是QmyWidget类里定义的一个属性。
      • 这样,窗体上的对象和QmyWidget类里新定义的属性不会混淆,有利于界面与业务逻辑的分离。
      • (3)当然,也可以定义界面对象为公共属性,即创建界面对象时用下面的语句:
        self.ui=Ui_FormHello()
        
      • 这里的ui就是个公共属性,在类的外部也可以通过属性ui直接访问界面上的组件
  • 总结
    • 对比多继承方法和单继承方法,可以发现单继承方法更有利于界面与业务逻辑分离

你可能感兴趣的:(PyQt5,pyqt5,python,gui,python,pyqt,qt)