pyside6自定义部件库和软件框架的建设记录

自定义的部件库原则上尽量做到前后端分离,接口方便,复制简单。

单选框部件

# encoding: utf-8
###################################################
#               自定义的单选框
###################################################

# 对外接口:
# stateNum = Signal(int)  状态号,从1开始,是几就几被选中
# customSetup():   # 定制的初始化程序


import PySide6
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QWidget, QGroupBox, QFrame


class PysideCustomRadiobutton(QFrame):
    stateNum = Signal(int)    # 状态号,从1开始,是几就几被选中

    def __init__(self, parent=None):
        super().__init__(parent)
        self.RadioButtons = []

    def customSetupUi(self):   # 定制的初始化程序
        self.RadioButtons = self.findChildren(PySide6.QtWidgets.QRadioButton)   # 部件中所有的单选按钮
        self.RadioButtons.sort(key=lambda child: child.objectName())    # 按名称排序

        for i in range(len(self.RadioButtons)):
            def callback(idx):
                return lambda: self.stateNum.emit(idx+1)
            self.RadioButtons[i].clicked.connect(callback(i))     # 单击按钮后发射信号

        self.stateNum.connect(self.changeState)                   # 状态号信号的连接

        self.stateNum.emit(1)                                     # 初始化状态下,状态1被选中

    def changeState(self, thisTurnOn):    # 改变状态
        self.RadioButtons[thisTurnOn-1].setChecked(True)




前端的范例: 

框架的建立和调用

# 阶段v1的调用
import sys
from time import strftime

from PySide6.QtCore import QObject, Signal, QTimer
from PySide6.QtWidgets import QWidget, QApplication, QMainWindow
import template_rc  # 导入需要显示的画面


# ############导入并需要显示的画面集中在这里定义#############################
# ############template.ui的显示#############################
class MainWindow(QMainWindow, template_rc.Ui_MainWindow):  # 定义需要显示的画面类
    def __int__(self):
        super().__init__()


# ########################部件初始化的通用函数###################################
def init_widgets(widget):  # 画面里的部件的初始化
    sons = widget.children()  # 儿辈部件

    def find_child(child):  # 查找子部件
        try:
            child.customSetupUi()  # 自定义的初始化
        except AttributeError:
            pass

        if child.children():
            for grandson in child.children():
                find_child(grandson)  # 递归查找

    for son in sons:
        find_child(son)


# #############################主程序###################################
if __name__ == '__main__':
    app = QApplication(sys.argv)

    # #######################本项目的定义###################################
    class UI(QObject):
        # ##########项目范围内的信号#############
        sysClock_1S = Signal()  # 秒时钟信号

        # ##########定时器#############
        timer_1s = QTimer()  # 定义全局0.1s的周期定时器
        timer_1s.start(1000)

        # ###########__init__###############
        def __init__(self):
            super().__init__()
            self.widgets = []  # 所有的视窗列表


    # ########################本项目的实例化###################################
    ui = UI()

    # #######################画面实例化和初始化#################################
    # ########################实例化模板画面#################################
    mainWindow = MainWindow()
    mainWindow.setupUi(mainWindow)  # 模板画面的本体初始化
    init_widgets(mainWindow)  # 模板画面的部件初始化
    mainWindow.show()
    ui.widgets.append(mainWindow)  # 将画面加入视窗列表,方便管理

    # ###########################信号的连接####################################
    # ###########################项目级别信号的连接###############################
    ui.timer_1s.timeout.connect(ui.sysClock_1S.emit)
    ui.sysClock_1S.connect(lambda: mainWindow.labelSystemClock.setText(strftime('%Y-%m-%d %H:%M:%S')))
    # ###########################mainWindow画面信号的连接######################
    mainWindow.RadioButtons_1.stateNum.connect(lambda x: mainWindow.label_1.setText(str(x)))
    mainWindow.pushButton_1.clicked.connect(lambda: mainWindow.RadioButtons_1.stateNum.emit(1))
    mainWindow.doubleSpinBox_1.valueChanged.connect(lambda x: mainWindow.label_22.setText(str(x)))

    # ###########################槽函数####################################

    sys.exit(app.exec())

运行截图:

本阶段的资源链接:(v1)https://download.csdn.net/download/xulibo5828/89120987?spm=1001.2101.3001.9499

自定义功能的仪表盘

        一个具有设定值输入、微调,实时值显示,历史曲线实时显示的仪表盘。

        在CAD软件中大致规划一下功能区的尺寸和排列:

         功能和分工规划:编程阶段可视的部件在QT designer中完成部署和设置,运行阶段可视的动态部件在程序中根据QT designer中的设置值自动计算和部署。

        对外接口:外部传入设定值和实时值。

        画面设计:在QT Designer里面完成画面的组态,布置和复制部件也仅在QT Designer中进行。部件的总容器和历史数据显示窗口用QFrame,进度条和历史曲线的显示在程序里用QPainter实现。历史曲线尝试过用专门的绘图库matplotlib,这个库在嵌入pyside6的部件时无法做到0边距无缝衔接,左右两边无法布满pyside6的部件。如下图:

所以,还是决定用QPainter的 drawline来实现。

在QT Designer里设计好部件的各个组件:

 为之配套编写代码,命名为PysideCustomDashboard.py,并将部件“提升为”这个自定义的类。

 部件的代码:

# encoding: utf-8
###################################################
#               自定义的仪表盘
###################################################

# 对外接口:
#   ActualValueInput = Signal(float)  # 从外部传入的实时值
#   customSetup():   # 定制的初始化程序


import PySide6
from PySide6.QtCore import Signal, Qt, QPoint
from PySide6.QtGui import QColor, QPen, QPainter
from PySide6.QtWidgets import QFrame


class PysideCustomDashboard(QFrame):
    ActualValueInput = Signal(float)  # 从外部传入的实时值
    PresetValueInput = Signal(float)  # 从外部传入的设定值
    PresetValueOutput = Signal(float)  # 向外部传出的设定值

    def __init__(self, parent=None):
        super().__init__(parent)
        # self.preset_value = 0.0            # 预设值的value
        self.processBar_preset_width = 0  # 预设值进度条的宽度
        self.processBar_preset_radius = 0  # 预设值进度条的半径
        self.processBar_actual_radius = 0  # 实时值进度条的半径
        self.processBar_y_start = 0  # 进度条的外轮廓距y方向边缘距离
        self.processBar_x_start = 0  # 进度条的外轮廓距x方向边缘距离
        self.center_point = QPoint(0, 0)  # 部件的中心坐标

        self.actual_text_color = QColor()  # 实时值窗口文字颜色
        self.spinBox_preset_text_color = QColor()  # 预设值窗口文字颜色
        self.progressBar_backColor = QColor('#515d80')  # 进度条背景色
        self.frame_history_Palette = None  # 历史曲线显示窗口的调色板
        self.frame_history_styleSheet = None  # 历史曲线显示窗口的样式表
        self.frame_history_geometry = None  # 历史曲线显示窗口的几何特征

        self.label_name = None  # 仪表盘名称文字框
        self.label_unit = None  # 显示单位文字框
        self.label_actual = None  # 实时值的显示文字框
        self.spinBox_preset = None  # 带微调的设置值输入部件
        self.frame_history = None  # 历史曲线显示窗口

        self.begin_degree = 225  # 进度条总的起始角度
        self.span_degree = -225  # 进度条总的跨度角度

        self.actual_begin_degree = 225  # 实时值的进度条起始角度
        self.actual_span_degree = 0  # 实时值的进度条跨度始角度

        self.preset_begin_degree = 225  # 设定值的进度条起始角度
        self.preset_span_degree = 0  # 设定值的进度条跨度始角度

        self.min_value = 0.0  # 最小值
        self.max_value = 100.0  # 最大值
        self.processBar_actual_width = 0  # 进度条的宽度
        self.processBar_center = QPoint(0, 0)  # 进度条的中心
        self.points = 10  # 显示的点数

    # #######################定制的初始化程序###########################
    def customSetupUi(self):  # 定制的初始化程序

        # ######################子部件的定义############################
        children = self.children()
        for child in children:
            # ##############历史曲线显示窗口#####################
            if 'frame_history' in child.objectName():
                self.frame_history_geometry = child.geometry()  # 几何数据
                self.frame_history_styleSheet = child.styleSheet()  # 样式表
                self.frame_history_Palette = child.palette()  # 颜色表
            # #################带微调的设置值输入部件################
            elif 'spinBox_preset' in child.objectName():
                self.spinBox_preset = child
                p = child.palette()  # 颜色表
                self.spinBox_preset_text_color = p.color(p.ColorRole.Text)  # 获取预设值窗口文字颜色
                self.min_value = child.minimum()  # 最小值
                self.max_value = child.maximum()  # 最大值
            # ###################实时值的显示文字框###################
            elif 'label_actual' in child.objectName():
                self.label_actual = child
                p = child.palette()  # 颜色表
                self.actual_text_color = p.color(p.ColorRole.Text)  # 获取实时值窗口文字颜色
            # #################显示单位文字#######################
            elif 'label_unit' in child.objectName():
                self.label_unit = child
            # ##############仪表盘名称文字####################
            elif 'label_name' in child.objectName():  #
                self.label_name = child

        # ##################本体几何参数########################
        self.center_point = QPoint(int(self.width() / 2), int((self.height() / 2)))  # 中心点坐标

        # ####################历史曲线显示窗口的定义###################
        self.frame_history = CustomLineChart(self.spinBox_preset_text_color, self.actual_text_color, self.min_value,
                                             self.max_value, self.points, self)  # 实体化历史曲线显示窗口
        self.frame_history.setGeometry(self.frame_history_geometry)  # 克隆几何尺寸
        self.frame_history.setStyleSheet(self.frame_history_styleSheet)  # 克隆样式表
        self.frame_history.setPalette(self.frame_history_Palette)  # 克隆颜色表
        self.frame_history.show()  # 显示
        # self.frame_history.customSetupUi()  # 历史曲线显示窗口初始化

        # ####################进度条的定义##############################
        self.processBar_actual_width = int((self.width() / 50))  # 实时值进度条的宽度
        # self.processBar_preset_width = self.processBar_actual_width  # 预设值进度条的宽度
        self.processBar_preset_width = int(self.processBar_actual_width * 0.66)  # 预设值进度条的宽度
        # self.processBar_preset_width = int(self.processBar_actual_width / 3)
        self.processBar_x_start = int(self.width() / 20)  # 进度条的外轮廓距x方向边缘距离
        self.processBar_y_start = int(self.width() / 20)  # 进度条的外轮廓距y方向边缘距离
        self.processBar_actual_radius = int(
            (self.width() / 2) - self.processBar_x_start - (self.processBar_actual_width / 2) * 0.9)  # 实时值进度条的半径
        self.processBar_preset_radius = int(
            self.processBar_actual_radius - self.processBar_actual_width)  # 预设值进度条的半径

        self.signal_handing([self.spinBox_preset.value(), 'preset'])  # 初始化一下预设值进度条的显示

        # ######################信号的连接############################
        self.ActualValueInput.connect(lambda x: self.signal_handing([x, 'actual']))  # 接收到外部传入实时数据后的信号处理
        self.PresetValueInput.connect(self.spinBox_preset.setValue)    # 接收到外部传入设置值后的信号处理
        self.spinBox_preset.valueChanged.connect(lambda x: self.signal_handing([x, 'preset']))  # 设置值发生改变后的信号处理

    # #######################发射信号的处理############################
    def signal_handing(self, args):
        if args[1] == 'actual':  # 如果是实时值发射的信号
            self.frame_history.PointValues.emit(self.spinBox_preset.value(), args[0])  # 将预设值和实际值打包发送到历史曲线的部件的输入信号
            self.label_actual.setText(str(args[0]))  # 实时显示实际值的数字

            self.actual_span_degree = int(
                (args[0] - self.min_value) / (self.max_value - self.min_value) * self.span_degree)  # 计算实时值进度条的角度
            self.update()  # 刷新画面

        else:  # 如果是设定值发射的信号
            self.frame_history.PointValues.emit(self.spinBox_preset.value(),
                                                float(self.label_actual.text()))  # 将预设值和实际值打包发送到历史曲线的部件的输入信号

            self.preset_span_degree = int(
                (args[0] - self.min_value) / (self.max_value - self.min_value) * self.span_degree)  # 计算设定值进度条的角度
            self.update()  # 刷新画面

    # ########################绘制进度条圆弧###########################
    def draw_arc(self, color, center, startAngle, spanAngle, width, radius):
        painter = QPainter(self)  # 设定画板
        painter.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        painter.setPen(QPen(color, width, Qt.SolidLine))  # 设置画笔颜色、粗细和线型
        painter.drawArc(center[0] - radius, center[1] - radius, radius * 2, radius * 2, startAngle * 16, spanAngle * 16)

    # #########################重写paintEvent########################
    def paintEvent(self, event):
        # ##################绘制实时值背景进度条###############################
        self.draw_arc(self.progressBar_backColor, (self.center_point.x(), self.center_point.y()), self.begin_degree,
                      self.span_degree, self.processBar_actual_width, self.processBar_actual_radius)

        # ##################绘制设置值背景进度条###############################
        self.draw_arc(self.progressBar_backColor, (self.center_point.x(), self.center_point.y()), self.begin_degree,
                      self.span_degree, self.processBar_preset_width, self.processBar_preset_radius)

        # ##################绘制实时值的显示进度条###############################
        self.draw_arc(self.actual_text_color, (self.center_point.x(), self.center_point.y()), self.actual_begin_degree,
                      self.actual_span_degree, self.processBar_actual_width, self.processBar_actual_radius)

        # ##################绘制设置值的显示进度条###############################
        self.draw_arc(self.spinBox_preset_text_color, (self.center_point.x(), self.center_point.y()),
                      self.preset_begin_degree,
                      self.preset_span_degree, self.processBar_preset_width, self.processBar_preset_radius)


# #########################历史曲线显示窗口的class定义########################
class CustomLineChart(QFrame):  # 自定义的折线表
    PointValues = Signal(float, float)  # 点值,设置值在前,实时值在后

    def __init__(self, preset_color, actual_color, min_value, max_value, points, parent=None):
        super().__init__(parent)
        self.points = points  # 显示的点数
        self.preset_value_points = [0.0]  # 设置值的数据点集
        self.actual_value_points = [0.0]  # 实时值的数据点集
        self.overflow = False  # 数据超量程

        self.background_color = QColor()  # 背景色
        self.preset_color = preset_color  # 预置值颜色
        self.actual_color = actual_color  # 实时值颜色
        self.preset_pen = QPen()  # 预设值波形的画笔
        self.actual_pen = QPen()  # 实时值波形的画笔
        self.min_value = min_value  # 最小值
        self.max_value = max_value  # 最大值

        self.x_points = []  # X轴的分度
        self.x_step = 0  # X轴的步调节拍

    def customSetupUi(self):  # 定制的初始化程序
        # #####################建立x轴的分度表############################
        x = 0
        s = self.width() / (self.points - 1)
        for i in range(self.points):
            self.x_points.append(int(x))
            x += s

        # #####################初始化颜色特征############################
        p = self.palette()  # 获取调色板
        self.background_color = p.color(p.ColorRole.Window)  # 获取背景色
        self.preset_pen = QPen(self.preset_color, 1)  # 预置值的笔
        self.actual_pen = QPen(self.actual_color, 1)  # 实时值的笔

        # #####################信号的连接############################
        self.PointValues.connect(self.pretreat)  # 当传入值以后预处理数据
        self.PointValues.connect(self.update)  # 当传入值以后更新画面

    def pretreat(self, preset_value, actual_value):  # 数据的预处理
        # #####################预设值数据的预处理############################
        k = (preset_value - self.min_value) / (self.max_value - self.min_value)
        v = int(self.height() * (1 - k))
        self.preset_value_points.append(v)
        if len(self.preset_value_points) > self.points:
            del self.preset_value_points[0]

        # #####################实时值数据的预处理############################
        if actual_value < self.min_value:  # 输入值超出量程
            actual_value = self.min_value
            self.overflow = True
        elif actual_value > self.max_value:
            actual_value = self.max_value
            self.overflow = True
        else:
            self.overflow = False

        k = (actual_value - self.min_value) / (self.max_value - self.min_value)  # 显示比例转换
        v = int(self.height() * (1 - k))
        self.actual_value_points.append(v)
        if len(self.actual_value_points) > self.points:
            del self.actual_value_points[0]

    # ############################绘图事件####################################
    def paintEvent(self, event):  # 重新定义paintEvent
        painter = QPainter(self)  # 设定画板
        painter.setPen(Qt.NoPen)  # 设定画笔

        # #########################重画背景###################################
        if self.overflow:
            color = QColor('#363636')
        else:
            color = self.background_color
        painter.setBrush(color)  # 设置笔刷
        painter.drawRect(0, 0, self.width(), self.height())  # 重画背景

        # #########################绘制预设值的折线#############################
        pen = QPen(self.preset_color)
        pen.setWidth(1)
        painter.setPen(pen)
        if len(self.preset_value_points) > 1:
            i = 1
            while i < len(self.preset_value_points):
                painter.drawLine(self.x_points[i - 1], int(self.preset_value_points[i - 1]), self.x_points[i],
                                 int(self.preset_value_points[i]))
                i += 1

        # #########################绘制实时值的折线#############################
        pen = QPen(self.actual_color)
        pen.setWidth(1)
        painter.setPen(pen)
        if len(self.actual_value_points) > 1:
            i = 1
            while i < len(self.actual_value_points):                
                painter.drawLine(self.x_points[i - 1], int(self.actual_value_points[i - 1]),
                                 self.x_points[i], int(self.actual_value_points[i]))
                i += 1

 编写调用程序:基本的调用框架.py

# 阶段v1的调用
import random
import sys
from time import strftime

from PySide6.QtCore import QObject, Signal, QTimer
from PySide6.QtWidgets import QApplication, QMainWindow

import template_rc  # 导入需要显示的画面
import 按钮图标_rc


# ############导入并需要显示的画面集中在这里定义#############################
# ############template.ui的显示#############################
class MainWindow(QMainWindow, template_rc.Ui_MainWindow):  # 定义需要显示的画面类
    def __int__(self):
        super().__init__()


# ########################部件初始化的通用函数###################################
def init_widgets(widget):  # 画面里的部件的初始化
    sons = widget.children()  # 儿辈部件

    def find_child(child):  # 查找子部件
        try:
            child.customSetupUi()  # 自定义的初始化
        except AttributeError:
            pass

        if child.children():
            for grandson in child.children():
                find_child(grandson)  # 递归查找

    for son in sons:
        find_child(son)


def test():  # 测试程序
    n1 = random.randint(-300, 300)
    window1.dashboard_1.ActualValueInput.emit(float(n1 / 100.0 + 80))


# #############################主程序###################################
if __name__ == '__main__':
    app = QApplication(sys.argv)


    # #######################项目级别的定义###################################
    class UI(QObject):  # 将项目定义为QObject,用来管理项目级别的信号和变量
        # ##########项目范围内的信号#############
        sysClock_1S = Signal()  # 秒时钟信号

        # ##########定时器#############
        timer_1s = QTimer()  # 定义全局0.1s的周期定时器

        timer_1s.start(300)

        # ###########__init__###############
        def __init__(self):
            super().__init__()
            self.widgets = []  # 所有的视窗列表


    # ########################本项目的实例化###################################
    ui = UI()

    # #######################画面实例化和初始化#################################
    # ########################实例化模板画面#################################
    window1 = MainWindow()
    window1.setupUi(window1)  # 模板画面的本体初始化
    window1.show()  # 显示画面
    ui.widgets.append(window1)  # 将画面加入视窗列表,方便管理

    # ###########################信号的连接####################################

    # # ###########################项目级别信号的连接###############################
    ui.timer_1s.timeout.connect(ui.sysClock_1S.emit)
    ui.timer_1s.timeout.connect(test)
    ui.sysClock_1S.connect(lambda: window1.labelSystemClock.setText(strftime('%Y-%m-%d %H:%M:%S')))

    # ###########################Window1画面信号的连接######################
    # ###########################槽函数####################################

    init_widgets(window1)  # 画面的子部件初始化

    sys.exit(app.exec())

运行截图:

 

进一步的,在组态画面里按ctrl拖动复制一份 ,并改变一些几何和颜色特征,增加一个外部设定部件:

编写调用程序:仪表盘调用demo.py 

# 阶段v1的调用
import random
import sys
from time import strftime
from PySide6.QtCore import QObject, Signal, QTimer
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QDoubleSpinBox

import template_rc  # 导入需要显示的画面
import 按钮图标_rc


# ############导入并需要显示的画面集中在这里定义#############################
# ############template.ui的显示#############################
class MainWindow(QMainWindow, template_rc.Ui_MainWindow):  # 定义需要显示的画面类
    def __int__(self):
        super().__init__()


# ########################部件初始化的通用函数###################################
def init_widgets(widget):  # 画面里的部件的初始化
    sons = widget.children()  # 儿辈部件

    def find_child(child):  # 查找子部件
        try:
            child.customSetupUi()  # 自定义的初始化
        except AttributeError:
            pass

        if child.children():
            for grandson in child.children():
                find_child(grandson)  # 递归查找

    for son in sons:
        find_child(son)


def test():  # 测试程序
    n1 = random.randint(-300, 300)
    n2 = random.randint(-300, 300)
    window1.dashboard_1.ActualValueInput.emit(float(n1 / 100.0 + 80))
    window1.dashboard_2.ActualValueInput.emit(float(n2 / 100.0 + 150))


# #############################主程序###################################
if __name__ == '__main__':
    app = QApplication(sys.argv)


    # #######################项目级别的定义###################################
    class UI(QObject):  # 将项目定义为QObject,用来管理项目级别的信号和变量
        # ##########项目范围内的信号#############
        sysClock_1S = Signal()  # 秒时钟信号

        # ##########定时器#############
        timer_1s = QTimer()  # 定义全局0.1s的周期定时器

        timer_1s.start(1000)

        # ###########__init__###############
        def __init__(self):
            super().__init__()
            self.widgets = []  # 所有的视窗列表


    # ########################本项目的实例化###################################
    ui = UI()

    # #######################画面实例化和初始化#################################
    # ########################实例化模板画面#################################
    window1 = MainWindow()
    window1.setupUi(window1)  # 模板画面的本体初始化
    window1.show()  # 显示画面
    ui.widgets.append(window1)  # 将画面加入视窗列表,方便管理

    # ###########################信号的连接####################################

    # # ###########################项目级别信号的连接###############################
    ui.timer_1s.timeout.connect(ui.sysClock_1S.emit)
    ui.timer_1s.timeout.connect(test)
    ui.sysClock_1S.connect(lambda: window1.labelSystemClock.setText(strftime('%Y-%m-%d %H:%M:%S')))

    # ###########################Window1画面信号的连接######################
    window1.doubleSpinBox.valueChanged.connect(window1.dashboard_1.PresetValueInput.emit)

    # ###########################槽函数####################################

    init_widgets(window1)  # 画面的子部件初始化
    # QLabel
    # QDoubleSpinBox

    sys.exit(app.exec())

 运行截图:

基本实现了设计初衷 ,前后端图像和代码分离,接口和调用简单,复制部署方便。

 资源链接:(本阶段为v2)https://download.csdn.net/download/xulibo5828/89148225?spm=1001.2101.3001.9499 

20240501,增加了进度条的渐变色效果:

 改进后的仪表盘部件代码:

# encoding: utf-8
###################################################
#               自定义的仪表盘
###################################################

# 对外接口:
#   ActualValueInput = Signal(float)  # 从外部传入的实时值
#   customSetup():   # 定制的初始化程序


import PySide6
from PySide6.QtCore import Signal, Qt, QPoint, QPointF
from PySide6.QtGui import QColor, QPen, QPainter, QConicalGradient
from PySide6.QtWidgets import QFrame


class PysideCustomDashboard(QFrame):
    ActualValueInput = Signal(float)  # 从外部传入的实时值
    PresetValueInput = Signal(float)  # 从外部传入的设定值
    PresetValueOutput = Signal(float)  # 向外部传出的设定值

    def __init__(self, parent=None):
        super().__init__(parent)
        # self.preset_value = 0.0            # 预设值的value
        self.processBar_preset_width = 0  # 预设值进度条的宽度
        self.processBar_preset_radius = 0  # 预设值进度条的半径
        self.processBar_actual_radius = 0  # 实时值进度条的半径
        self.processBar_y_start = 0  # 进度条的外轮廓距y方向边缘距离
        self.processBar_x_start = 0  # 进度条的外轮廓距x方向边缘距离
        self.center_point = QPoint(0, 0)  # 部件的中心坐标

        self.actual_text_color = QColor()  # 实时值窗口文字颜色
        self.spinBox_preset_text_color = QColor()  # 预设值窗口文字颜色
        self.progressBar_backColor = QColor('#515d80')  # 进度条背景色
        self.frame_history_Palette = None  # 历史曲线显示窗口的调色板
        self.frame_history_styleSheet = None  # 历史曲线显示窗口的样式表
        self.frame_history_geometry = None  # 历史曲线显示窗口的几何特征

        self.label_name = None  # 仪表盘名称文字框
        self.label_unit = None  # 显示单位文字框
        self.label_actual = None  # 实时值的显示文字框
        self.spinBox_preset = None  # 带微调的设置值输入部件
        self.frame_history = None  # 历史曲线显示窗口

        self.begin_degree = 225  # 进度条总的起始角度
        self.span_degree = -225  # 进度条总的跨度角度

        self.actual_begin_degree = 225  # 实时值的进度条起始角度
        self.actual_span_degree = 0  # 实时值的进度条跨度始角度

        self.preset_begin_degree = 225  # 设定值的进度条起始角度
        self.preset_span_degree = 0  # 设定值的进度条跨度始角度

        self.min_value = 0.0  # 最小值
        self.max_value = 100.0  # 最大值
        self.processBar_actual_width = 0  # 进度条的宽度
        self.processBar_center = QPoint(0, 0)  # 进度条的中心
        self.points = 10  # 显示的点数

    # #######################定制的初始化程序###########################
    def customSetupUi(self):  # 定制的初始化程序

        # ######################子部件的定义############################
        children = self.children()
        for child in children:
            # ##############历史曲线显示窗口#####################
            if 'frame_history' in child.objectName():
                self.frame_history_geometry = child.geometry()  # 几何数据
                self.frame_history_styleSheet = child.styleSheet()  # 样式表
                self.frame_history_Palette = child.palette()  # 颜色表
            # #################带微调的设置值输入部件################
            elif 'spinBox_preset' in child.objectName():
                self.spinBox_preset = child
                p = child.palette()  # 颜色表
                self.spinBox_preset_text_color = p.color(p.ColorRole.Text)  # 获取预设值窗口文字颜色
                self.min_value = child.minimum()  # 最小值
                self.max_value = child.maximum()  # 最大值
            # ###################实时值的显示文字框###################
            elif 'label_actual' in child.objectName():
                self.label_actual = child
                p = child.palette()  # 颜色表
                self.actual_text_color = p.color(p.ColorRole.Text)  # 获取实时值窗口文字颜色
            # #################显示单位文字#######################
            elif 'label_unit' in child.objectName():
                self.label_unit = child
            # ##############仪表盘名称文字####################
            elif 'label_name' in child.objectName():  #
                self.label_name = child

        # ##################本体几何参数########################
        self.center_point = QPoint(int(self.width() / 2), int((self.height() / 2)))  # 中心点坐标

        # ####################历史曲线显示窗口的定义###################
        self.frame_history = CustomLineChart(self.spinBox_preset_text_color, self.actual_text_color, self.min_value,
                                             self.max_value, self.points, self)  # 实体化历史曲线显示窗口
        self.frame_history.setGeometry(self.frame_history_geometry)  # 克隆几何尺寸
        self.frame_history.setStyleSheet(self.frame_history_styleSheet)  # 克隆样式表
        self.frame_history.setPalette(self.frame_history_Palette)  # 克隆颜色表
        self.frame_history.show()  # 显示
        # self.frame_history.customSetupUi()  # 历史曲线显示窗口初始化

        # ####################进度条的定义##############################
        self.processBar_actual_width = int((self.width() / 50))  # 实时值进度条的宽度
        # self.processBar_preset_width = self.processBar_actual_width  # 预设值进度条的宽度
        self.processBar_preset_width = int(self.processBar_actual_width * 0.66)  # 预设值进度条的宽度
        # self.processBar_preset_width = int(self.processBar_actual_width / 3)
        self.processBar_x_start = int(self.width() / 20)  # 进度条的外轮廓距x方向边缘距离
        self.processBar_y_start = int(self.width() / 20)  # 进度条的外轮廓距y方向边缘距离
        self.processBar_actual_radius = int(
            (self.width() / 2) - self.processBar_x_start - (self.processBar_actual_width / 2) * 0.9)  # 实时值进度条的半径
        self.processBar_preset_radius = int(
            self.processBar_actual_radius - self.processBar_actual_width)  # 预设值进度条的半径

        self.signal_handing([self.spinBox_preset.value(), 'preset'])  # 初始化一下预设值进度条的显示

        # ######################信号的连接############################
        self.ActualValueInput.connect(lambda x: self.signal_handing([x, 'actual']))  # 接收到外部传入实时数据后的信号处理
        self.PresetValueInput.connect(self.spinBox_preset.setValue)    # 接收到外部传入设置值后的信号处理
        self.spinBox_preset.valueChanged.connect(lambda x: self.signal_handing([x, 'preset']))  # 设置值发生改变后的信号处理

    # #######################发射信号的处理############################
    def signal_handing(self, args):
        if args[1] == 'actual':  # 如果是实时值发射的信号
            self.frame_history.PointValues.emit(self.spinBox_preset.value(), args[0])  # 将预设值和实际值打包发送到历史曲线的部件的输入信号
            self.label_actual.setText(str(args[0]))  # 实时显示实际值的数字

            self.actual_span_degree = int(
                (args[0] - self.min_value) / (self.max_value - self.min_value) * self.span_degree)  # 计算实时值进度条的角度
            self.update()  # 刷新画面

        else:  # 如果是设定值发射的信号
            self.frame_history.PointValues.emit(self.spinBox_preset.value(),
                                                float(self.label_actual.text()))  # 将预设值和实际值打包发送到历史曲线的部件的输入信号

            self.preset_span_degree = int(
                (args[0] - self.min_value) / (self.max_value - self.min_value) * self.span_degree)  # 计算设定值进度条的角度
            self.update()  # 刷新画面

    # ########################绘制进度条圆弧###########################
    def draw_arc(self, color, center, startAngle, spanAngle, width, radius):
        painter = QPainter(self)  # 设定画板
        painter.setRenderHint(QPainter.Antialiasing)  # 抗锯齿
        painter.setPen(QPen(color, width, Qt.SolidLine))  # 设置画笔颜色、粗细和线型
        painter.drawArc(center[0] - radius, center[1] - radius, radius * 2, radius * 2, startAngle * 16, spanAngle * 16)

    # #########################重写paintEvent########################
    def paintEvent(self, event):
        # ##################绘制实时值背景进度条###############################
        self.draw_arc(self.progressBar_backColor, (self.center_point.x(), self.center_point.y()), self.begin_degree,
                      self.span_degree, self.processBar_actual_width, self.processBar_actual_radius)
        # print(self.preset_begin_degree, self.preset_span_degree)

        # ##################绘制设置值背景进度条###############################
        self.draw_arc(self.progressBar_backColor, (self.center_point.x(), self.center_point.y()), self.begin_degree,
                      self.span_degree, self.processBar_preset_width, self.processBar_preset_radius)

        # ##################绘制实时值的显示进度条###############################
        # self.draw_arc(self.actual_text_color, (self.center_point.x(), self.center_point.y()), self.actual_begin_degree,
        #               self.actual_span_degree, self.processBar_actual_width, self.processBar_actual_radius)   # 选择1:画单色的圆弧

        gradient = QConicalGradient(self.center_point.x(), self.center_point.y(), self.actual_begin_degree + self.actual_span_degree)  # 创建锥形梯度
        color = self.actual_text_color        # 定义临时颜色
        gradient.setColorAt(0, color)         # 定义渐变色的起点颜色

        color.setAlpha(120)        # 定义渐变色的中点颜色
        k = abs(self.actual_span_degree) / 360
        gradient.setColorAt(0.2*k, color)

        color.setAlpha(10)  # 定义渐变色的终点颜色
        gradient.setColorAt(k, color)
        self.draw_arc(gradient, (self.center_point.x(), self.center_point.y()), self.actual_begin_degree,       # 选择2:画渐变色的圆弧
                      self.actual_span_degree, self.processBar_actual_width, self.processBar_actual_radius)
        color.setAlpha(255)       # 复位颜色

        # ##################绘制设置值的显示进度条###############################
        # self.draw_arc(self.spinBox_preset_text_color, (self.center_point.x(), self.center_point.y()),
        #               self.preset_begin_degree,
        #               self.preset_span_degree, self.processBar_preset_width, self.processBar_preset_radius)   # 选择1:画单色的圆弧

        gradient = QConicalGradient(self.center_point.x(), self.center_point.y(),
                                    self.preset_begin_degree + self.preset_span_degree)  # 创建锥形梯度
        color = self.spinBox_preset_text_color  # 定义临时颜色
        gradient.setColorAt(0, color)  # 定义渐变色的起点颜色

        color.setAlpha(120)  # 定义渐变色的中点颜色
        k = abs(self.preset_span_degree) / 360
        gradient.setColorAt(0.2 * k, color)

        color.setAlpha(10)
        k = abs(self.preset_span_degree) / 360
        gradient.setColorAt(k, color)  # 定义渐变色的终点颜色
        self.draw_arc(gradient, (self.center_point.x(), self.center_point.y()), self.preset_begin_degree,  # 选择2:画渐变色的圆弧
                      self.preset_span_degree, self.processBar_preset_width, self.processBar_preset_radius)
        color.setAlpha(255)  # 复位颜色


# #########################历史曲线显示窗口的class定义########################
class CustomLineChart(QFrame):  # 自定义的折线表
    PointValues = Signal(float, float)  # 点值,设置值在前,实时值在后

    def __init__(self, preset_color, actual_color, min_value, max_value, points, parent=None):
        super().__init__(parent)
        self.points = points  # 显示的点数
        self.preset_value_points = [0.0]  # 设置值的数据点集
        self.actual_value_points = [0.0]  # 实时值的数据点集
        self.overflow = False  # 数据超量程

        self.background_color = QColor()  # 背景色
        self.preset_color = preset_color  # 预置值颜色
        self.actual_color = actual_color  # 实时值颜色
        self.preset_pen = QPen()  # 预设值波形的画笔
        self.actual_pen = QPen()  # 实时值波形的画笔
        self.min_value = min_value  # 最小值
        self.max_value = max_value  # 最大值

        self.x_points = []  # X轴的分度
        self.x_step = 0  # X轴的步调节拍

    def customSetupUi(self):  # 定制的初始化程序
        # #####################建立x轴的分度表############################
        x = 0
        s = self.width() / (self.points - 1)
        for i in range(self.points):
            self.x_points.append(int(x))
            x += s

        # #####################初始化颜色特征############################
        p = self.palette()  # 获取调色板
        self.background_color = p.color(p.ColorRole.Window)  # 获取背景色
        self.preset_pen = QPen(self.preset_color, 1)  # 预置值的笔
        self.actual_pen = QPen(self.actual_color, 1)  # 实时值的笔

        # #####################信号的连接############################
        self.PointValues.connect(self.pretreat)  # 当传入值以后预处理数据
        self.PointValues.connect(self.update)  # 当传入值以后更新画面

    def pretreat(self, preset_value, actual_value):  # 数据的预处理
        # #####################预设值数据的预处理############################
        k = (preset_value - self.min_value) / (self.max_value - self.min_value)
        v = int(self.height() * (1 - k))
        self.preset_value_points.append(v)
        if len(self.preset_value_points) > self.points:
            del self.preset_value_points[0]

        # #####################实时值数据的预处理############################
        if actual_value < self.min_value:  # 输入值超出量程
            actual_value = self.min_value
            self.overflow = True
        elif actual_value > self.max_value:
            actual_value = self.max_value
            self.overflow = True
        else:
            self.overflow = False

        k = (actual_value - self.min_value) / (self.max_value - self.min_value)  # 显示比例转换
        v = int(self.height() * (1 - k))
        self.actual_value_points.append(v)
        if len(self.actual_value_points) > self.points:
            del self.actual_value_points[0]

    # ############################绘图事件####################################
    def paintEvent(self, event):  # 重新定义paintEvent
        painter = QPainter(self)  # 设定画板
        painter.setPen(Qt.NoPen)  # 设定画笔

        # #########################重画背景###################################
        if self.overflow:
            color = QColor('#666666')
        else:
            color = self.background_color
        painter.setBrush(color)  # 设置笔刷
        painter.drawRect(0, 0, self.width(), self.height())  # 重画背景

        # #########################绘制预设值的折线#############################
        pen = QPen(self.preset_color)
        pen.setWidth(1)
        painter.setPen(pen)
        if len(self.preset_value_points) > 1:
            i = 1
            while i < len(self.preset_value_points):
                painter.drawLine(self.x_points[i - 1], int(self.preset_value_points[i - 1]), self.x_points[i],
                                 int(self.preset_value_points[i]))
                i += 1

        # #########################绘制实时值的折线#############################
        pen = QPen(self.actual_color)
        pen.setWidth(1)
        painter.setPen(pen)
        if len(self.actual_value_points) > 1:
            i = 1
            while i < len(self.actual_value_points):
                painter.drawLine(self.x_points[i - 1], int(self.actual_value_points[i - 1]),
                                 self.x_points[i], int(self.actual_value_points[i]))
                i += 1

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深蓝海拓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值