前言:watchpoints版的双向绑定会拖慢程序的运行效率。这里将采用装饰器的方式来实现。
我们这里示例:增强pyside6的原生按钮。
目录
一、定义实体类
import os
import platform
from PySide6 import QtWidgets
from PySide6.QtCore import Signal, QObject, Qt
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QVBoxLayout, QPushButton, QWidget, QApplication
class ButtonData(QObject):
"""
Data Model
"""
text_changed: Signal = Signal(str)
styleSheet_changed: Signal = Signal(str)
extend_changed: Signal = Signal(object)
def __init__(self, text: str = None, styleSheet: str = None, extend: object = None):
super(ButtonData, self).__init__()
# 定义数据
self._text: str
self._styleSheet: str
# 可以存储一个附在按钮上的数据,比如按钮在表格中,我们可以很轻松的存储按钮所在行列信息和对象数据。
self._extend: object
# 初始化数据
self.text = text
self.styleSheet = styleSheet
self.extend = extend
@property
def text(self):
return self._text
@text.setter
def text(self, value):
self._text = value
# 数据改变时发出信号
self.text_changed.emit(self.text)
@property
def styleSheet(self):
return self._styleSheet
@styleSheet.setter
def styleSheet(self, value):
self._styleSheet = value
# 数据改变时发出信号
self.styleSheet_changed.emit(self.styleSheet)
@property
def extend(self):
return self._extend
@extend.setter
def extend(self, value):
self._extend = value
# 数据改变时发出信号
self.extend_changed.emit(self.extend)
二、按钮Ui
import os
import platform
from PySide6 import QtWidgets
from PySide6.QtCore import Signal, QObject
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QVBoxLayout, QPushButton, QWidget, QApplication
"""
初始化CSS文件位置
"""
CSS_PATH = None
if str(platform.system().lower()) == 'windows':
path = __file__.replace(fr"\{os.path.basename(__file__)}", "").replace("\\\\", "\\")
CSS_PATH = fr'{path}\button.css'
elif str(platform.system().lower()) == 'linux':
path = __file__.replace(fr"/{os.path.basename(__file__)}", "").replace("//", "/")
CSS_PATH = fr'{path}/button.css'
else:
print(f"未知系统:{platform.system().lower()}")
class ButtonUi(object):
"""
User Interface
"""
def setupUi(self, ButtonUi):
if not ButtonUi.objectName():
ButtonUi.setObjectName('Button')
with open(CSS_PATH, "r", encoding="UTF-8") as f:
ButtonUi.setStyleSheet(f.read())
ButtonUi.setWindowTitle("Button")
ButtonUi.setWindowIcon(QIcon(':/icons/template_edit.png'))
ButtonUi.resize(40, 30)
# 隐藏标题栏
ButtonUi.setWindowFlags(
Qt.Window
| Qt.FramelessWindowHint
| Qt.WindowSystemMenuHint
| Qt.WindowMinimizeButtonHint
| Qt.WindowMaximizeButtonHint
)
# 设置整个窗体的透明度
ButtonUi.setWindowOpacity(0.90) # 设置窗体透明度
# 设置隐藏背景
ButtonUi.setAttribute(Qt.WA_TranslucentBackground)
# 垂直布局
self.layout = QVBoxLayout()
# 设置边距为0
self.layout.setContentsMargins(0, 0, 0, 0)
ButtonUi.setLayout(self.layout)
self._button = QPushButton()
self._button.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
self.layout.addWidget(self._button)
/*button.css*/
/*按钮:按下*/
QPushButton:pressed{
padding-left:5px;
padding-top:5px;
}
三、按钮逻辑
from PySide6 import QtWidgets
from PySide6.QtCore import Signal, QObject
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QVBoxLayout, QPushButton, QWidget, QApplication
class Button(QWidget, ButtonUi):
"""
Control
"""
clicked: Signal = Signal(object)
def __init__(self, buttonData: ButtonData, parent=None):
super(Button, self).__init__(parent=parent)
self.setupUi(self)
self.data = buttonData
self.__bind()
# 将按钮的点击信号绑定到当前类的点击信号
self._button.clicked.connect(lambda: self.clicked.emit(self.data.extend))
def set_button_data(self, button_data: ButtonData):
self.data = button_data
def __bind(self):
"""
数据绑定到控件属性
"""
self.data.text_changed.connect(lambda text: self._button.setText(text))
self.data.styleSheet_changed.connect(lambda text: self._button.setStyleSheet(text))
"""
初始化设置
"""
self._button.setText(self.data.text)
self._button.setStyleSheet(self.data.styleSheet)
四、使用示例:
if __name__ == '__main__':
"""
Test Demo
"""
app = QApplication([])
button = Button(ButtonData(text="OK", extend={'id': 1}))
button.clicked.connect(lambda extend: print("点击->扩展数据为:", extend))
button.show()
app.exec()
五、思考:双向绑定怎样避免死循环
六、完整代码
import os
import platform
from PySide6 import QtWidgets
from PySide6.QtCore import Signal, QObject, Qt
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QVBoxLayout, QPushButton, QWidget, QApplication
class ButtonData(QObject):
"""
Data Model
"""
text_changed: Signal = Signal(str)
styleSheet_changed: Signal = Signal(str)
extend_changed: Signal = Signal(object)
def __init__(self, text: str = None, styleSheet: str = None, extend: object = None):
super(ButtonData, self).__init__()
# 定义数据
self._text: str
self._styleSheet: str
# 可以存储一个附在按钮上的数据,比如按钮在表格中,我们可以很轻松的存储按钮所在行列信息和对象数据。
self._extend: object
# 初始化数据
self.text = text
self.styleSheet = styleSheet
self.extend = extend
@property
def text(self):
return self._text
@text.setter
def text(self, value):
self._text = value
# 数据改变时发出信号
self.text_changed.emit(self.text)
@property
def styleSheet(self):
return self._styleSheet
@styleSheet.setter
def styleSheet(self, value):
self._styleSheet = value
# 数据改变时发出信号
self.styleSheet_changed.emit(self.styleSheet)
@property
def extend(self):
return self._extend
@extend.setter
def extend(self, value):
self._extend = value
# 数据改变时发出信号
self.extend_changed.emit(self.extend)
"""
初始化CSS文件位置
"""
CSS_PATH = None
if str(platform.system().lower()) == 'windows':
path = __file__.replace(fr"\{os.path.basename(__file__)}", "").replace("\\\\", "\\")
CSS_PATH = fr'{path}\button.css'
elif str(platform.system().lower()) == 'linux':
path = __file__.replace(fr"/{os.path.basename(__file__)}", "").replace("//", "/")
CSS_PATH = fr'{path}/button.css'
else:
print(f"未知系统:{platform.system().lower()}")
class ButtonUi(object):
"""
User Interface
"""
def setupUi(self, window):
if not window.objectName():
window.setObjectName('Button')
with open(CSS_PATH, "r", encoding="UTF-8") as f:
window.setStyleSheet(f.read())
window.setWindowTitle("Button")
window.setWindowIcon(QIcon(':/icons/template_edit.png'))
window.resize(40, 30)
# 隐藏标题栏
window.setWindowFlags(
Qt.Window
| Qt.FramelessWindowHint
| Qt.WindowSystemMenuHint
| Qt.WindowMinimizeButtonHint
| Qt.WindowMaximizeButtonHint
)
# 设置整个窗体的透明度
window.setWindowOpacity(0.90) # 设置窗体透明度
# 设置隐藏背景
window.setAttribute(Qt.WA_TranslucentBackground)
# 垂直布局
self.layout = QVBoxLayout()
# 设置边距为0
self.layout.setContentsMargins(0, 0, 0, 0)
window.setLayout(self.layout)
self._button = QPushButton()
self._button.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
self.layout.addWidget(self._button)
class Button(QWidget, ButtonUi):
"""
Control
"""
clicked: Signal = Signal(object)
def __init__(self, buttonData: ButtonData, parent=None):
super(Button, self).__init__(parent=parent)
self.setupUi(self)
self.data = buttonData
self.__bind()
# 将按钮的点击信号绑定到当前类的点击信号
self._button.clicked.connect(lambda: self.clicked.emit(self.data.extend))
def __bind(self):
"""
数据绑定到控件属性
"""
self.data.text_changed.connect(lambda text: self._button.setText(text))
self.data.styleSheet_changed.connect(lambda text: self._button.setStyleSheet(text))
"""
初始化设置
"""
self._button.setText(self.data.text)
self._button.setStyleSheet(self.data.styleSheet)
if __name__ == '__main__':
"""
Test Demo
"""
app = QApplication([])
button = Button(ButtonData(text="OK", extend={'id': 1}))
button.clicked.connect(lambda extend: print("点击->扩展数据为:", extend))
button.show()
app.exec()