第三篇 -- 界面与逻辑分离的设计方法(多继承方法和单继承方法)
一、多继承方法
1. 画图Form.ui,生成Form.py
UI:
Form.py
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'Form.ui' # # Created by: PyQt5 UI code generator 5.13.0 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(400, 300) self.label = QtWidgets.QLabel(Form) self.label.setGeometry(QtCore.QRect(110, 100, 181, 31)) font = QtGui.QFont() font.setPointSize(12) font.setBold(True) font.setWeight(75) self.label.setFont(font) self.label.setObjectName("label") self.btnClose = QtWidgets.QPushButton(Form) self.btnClose.setGeometry(QtCore.QRect(160, 180, 75, 23)) self.btnClose.setObjectName("btnClose") self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Demo2_2")) self.label.setText(_translate("Form", "Hello, by UI Designer")) self.btnClose.setText(_translate("Form", "关闭"))
2. 多继承方法导入界面Form.py
appMain2.py
# # appMain2.py 多继承方法 """ 多继承方式优缺点: 1. 界面上的组件都成为窗体业务逻辑类QmyWidget的公共属性,外界可以直接访问。优点是访问方便,缺点是过于开放, 不符合面向对象严格封装的设计思想 2. 界面上的组件与QmyWidget类里新定义的属性混合在一起了,不便于区分。当窗体上的界面组件较多, 且窗体业务逻辑类里定义的属性也很多时,就难以区分哪个属性是界面上的组件,哪个属性是在业务逻辑类里新定义的, 这样不利于界面与业务逻辑分离。 """ import sys from PyQt5.QtWidgets import QWidget, QApplication from Form 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.label.setText(self.Lab) if __name__ == "__main__": app = QApplication(sys.argv) # 创建app myWidget = QmyWidget() myWidget.show() myWidget.btnClose.setText("不关闭了") sys.exit(app.exec_())
3. 多继承解析:
a. 采用多继承的方式定义了一个类QmyWidget,称这个类为窗体的业务逻辑类,它的父类是QWidget和Ui_Form。
b. 在这个类的构造函数中,首先用函数super()获取父类,并执行父类的构造函数,代码是super().__init__(parent),在多继承时,使用super()得到的是第一个基类,在这里就是QWidget。所以,执行这条语句后,self就是一个QWidget对象。
c. 调用setUi()函数创建UI窗体,即self.setupUi(self)。因为QmyWidget的基类包括Ui_Form类,所以可以调用Ui_Form类的setupUi()函数。同时,经过前面调用父类的构造函数,self是一个QWidget对象,可以作为参数传递给setupUi()函数,正好作为各组件的窗体容器。
d. 通过这样的多继承,Ui_Form类中定义的窗体上的所有界面组件对象就变成了新定义的类QmyWidget的公共属性,可以直接访问这些界面组件。
二、单继承与界面独立封装方法
1. 同样写一个界面,同多继承一样Form.py
2. 单继承方法导入界面Form.py
appMain.py
# # appMain.py 单继承方法,能更好地进行界面与逻辑的分离 import sys from PyQt5.QtWidgets import QWidget, QApplication from Form 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.label.setText(self.Lab) def set_btn_text(self, a_text): self.__ui.btnClose.setText(a_text) if __name__ == "__main__": app = QApplication(sys.argv) # 创建app,用QApplication类 myWidget = QmyWidget() myWidget.show() myWidget.set_btn_text("间接设置") sys.exit(app.exec_())
3. 单继承解析
a. 新定义的窗体业务逻辑类QmyWidget只有一个基类QWidget。
b. 在QmyWidget的构造函数中,首先调用父类(也就是QWidget)的构造函数,这样self就是一个QWidget对象
c. 显式地创建了一个Ui_Form类的私有属性self.__ui,即self.__ui = Ui_Form。私有属性self.__ui包含了可视化设计的UI窗体上的所有组件,所以,只有通过self.__ui才可以访问窗体上的组件,包括调用其创建界面组件的setupUi()函数
注:Python语言的类定义通过命名规则来限定元素对外的可见性,属性或方法名称前有两个下划线表示是私有的,一个下划线表示模块内可见,没有下划线的就是公共的。
d. 由于self.__ui是QmyWidget类的私有属性,因此在应用程序中创建的QmyWidget对象myWidget不能直接访问myWidget.__ui,也就无法直接访问窗体上的界面组件。为了访问窗体上的组件,可以在QWidget类里定义接口函数,例如set_btn_text()用于设置窗体上按钮的文字。在应用程序里创建QmyWidget对象的实例myWidget,通过调用set_btn_text()函数间接修改界面上按钮的文字,即myWidget.set_btn_text("间接设置")
4. 单继承特点
a. 可视化设计的窗体对象被定义为QmyWidget类的一个私有属性self.__ui,在QmyWidget类的内部对窗体上的组件的访问都通过这个属性实现,而外部无法直接访问窗体上的对象,这更符合面向对象封装隔离的设计思想。
b. 窗体上的组件不会与QmyWidget里定义的属性混淆,例如self.Lab和self.__ui.label,有利于界面与业务逻辑的分离。
c. 当然,也可以定义界面对象为公共属性,即创建界面对象时用下面的语句:self.ui = Ui_Form(),这里的ui就是个公共属性,在类的外部也可以通过属性ui直接访问界面上的组件
总结:对比多继承方法和单继承方法,可以发现单继承方法更有利于界面与业务逻辑分离。