制作一个动态仪表盘。
具体过程参照前面技术储备阶段的文章:
PYQT制作动态时钟-CSDN博客
使用PYQT5简单制作动态仪表盘-CSDN博客
仪表盘由以下部件构成:背景
指针
表盘
进度条(设定值)
遮罩
设定和显示
其中表盘、 进度条和指针的外形尺寸以及几何位置相同,背景色都是透明色。通过旋转指针和进度条的角度来实现显示。遮罩的作用是把进度条的下半部分遮住。
总结一下,目前有两个画面:主画面和数据设置画面,一个选择器,一个仪表盘,一个指示灯,几个功能按钮。程序在之前的基础上进行了一些规划上的优化,基本框架也已经建立,结构如下:
如上图,每个画面对应一个同名的首字母大写的类,用来补充定义画面。
同样的,画面中的部件,每个部件对应一个同名的首字母大写的类,用来补充定义部件。
所有代码:
from sys import exit, argv
from time import strftime
from PyQt5.QtCore import Qt, QCoreApplication, pyqtSignal, QObject
from PyQt5.QtGui import QTransform
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
import buttons # 按钮的内嵌图片资源
# import dash # 仪表盘的内嵌图片资源
from PYS.MyButton import MyButton
from PYS.MyDash import *
from PYS.MyLabel import MyLabel
from PYS.MyLamp import MyLamp
from PYS.MySelectBox import *
############################################
# 公共的函数
class PublicFunction: # 公共函数
@staticmethod
def normal_button_init(button): # 通常规格按钮的初始化
# button.button.isEntered = False # 鼠标焦点是否在内
button.setImage() # 获取按钮上的图片
button.style_normal = f'background-color:#4b5075;border-radius:8px;border:2px solid rgb(43, 78, 140);color:#e6e6e6;image:{button.image};' # 常态style
button.style_enter = f'background-color:#4b5075;border-radius:8px;border:3px solid #e6e6e6;color:#e6e6e6;image:{button.image};' # 鼠标悬停后的style
button.style_clicked = f'background-color:#4b5075;border:2px solid #039806;border-radius:8px;color:#e6e6e6;image:{button.image};' # 鼠标点击后的style
button.style_on = f'background-color:#039806;border:4px solid #e6e6e6;border-radius:8px;color:#e6e6e6;image:{button.image};' # ON状态的style
button.style_disable = f'background-color:#767676;border:2px solid rgb(43, 78, 140);border-radius:8px;color:#c6c6c6;image:{button.image};' # 禁用状态下的style
@staticmethod
def green_lamp_init(lamp): # 绿色指示灯
lamp.rad = str(round(lamp.width() / 2))
lamp.off_color = '#767676' # 默认off颜色
lamp.on_color = '#039806' # 默认on颜色
lamp.border_color = '#2b4e8c' # 默认的边框颜色
@staticmethod
def red_lamp_init(lamp): # 红色指示灯
lamp.rad = str(round(lamp.width() / 2))
lamp.off_color = '#767676' # 默认off颜色
lamp.on_color = '#da1c1c' # 默认on颜色
lamp.border_color = '#2b4e8c' # 默认的边框颜色
@staticmethod
def normal_label_init(label): # 通常标签的初始化
label.isEntered = False # 鼠标焦点是否在内
label.style_normal = 'background-color:#000d47;border:2px solid #4b5075;border-radius:8px;color:#f3da83;' # 常态style
label.style_enter = 'background-color:#4b5075;border-radius:8px;border:3px solid #e6e6e6;color:#e6e6e6;' # 鼠标悬停后的style
label.style_clicked = 'background-color:#4b5075;border:2px solid #039806;border-radius:8px;color:#e6e6e6;' # 鼠标点击后的style
@staticmethod
def normal_selectorBox_init(selector): # 通常选择box的初始化
selector.start_on = 0 # 起始状态为on的序号
selector.controls_normal_style = 'color: #e6e6e6;background-color: rgba(255, 255, 255, 0);border-radius:2px;border:0px solid #1e2557;' # 默认控制器常态style
selector.controls_enter_style = 'color: #e6e6e6;background-color: rgba(255, 255, 255, 0);border-radius:2px;border:1px solid #e6e6e6;' # 默认控制器鼠标悬停style
selector.controls_clicked_style = 'color: #e6e6e6;background-color: rgba(255, 255, 255, 0);border-radius:2px;border:1px solid #039806;' # 默认控制器鼠标点击style
selector.states_off_style = 'border:2px solid rgb(43, 78, 140);background-color:#4b5075;border-radius:2px;' # 默认指示器off状态style
selector.states_on_style = 'border:2px solid rgb(43, 78, 140);background-color:#039806;border-radius:2px;' # 默认指示器on状态style
for c in selector.controls: # 初始化控制器
c.style_normal = selector.controls_normal_style
c.style_enter = selector.controls_enter_style
c.style_clicked = selector.controls_clicked_style
for i in range(len(selector.states)): # 初始化指示器
selector.states[i].states_on_style = selector.states_on_style
selector.states[i].states_off_style = selector.states_off_style
if i == selector.start_on:
selector.states[i].setStyleSheet(selector.states_on_style)
else:
selector.states[i].setStyleSheet(selector.states_off_style)
@staticmethod
def normal_dash_init(dash): # 通常仪表盘的初始化
dash.adjusts_normal_style = 'background-color:#000d47;border:2px solid #4b5075;border-radius:8px;color:#f3da83;' # “微调”按钮常态style
dash.adjusts_clicked_style = 'background-color:#000d47;border:2px solid #039806;border-radius:8px;color:#f3da83;' # “微调”按钮点击后的style
dash.adjusts_enter_style = 'background-color:#000d47;border:1px solid #e6e6e6;border-radius:8px;color:#f3da83;' # “微调”按钮鼠标悬停的style
dash.setting_normal_style = 'background-color:#000d47;border:2px solid #4b5075;border-radius:8px;color:#f3da83;' # “设置”标签常态style
dash.setting_clicked_style = 'background-color:#000d47;border:2px solid #039806;border-radius:8px;color:#f3da83;' # “设置”标签点击后的style
dash.setting_enter_style = 'background-color:#000d47;border:1px solid #e6e6e6;border-radius:8px;color:#f3da83;' # “设置”标签鼠标悬停的style
dash.panel_image = normal_dash_panel_image
dash.pointer_image = normal_dash_pointer_image
dash.preset_image = normal_dash_preset_image
dash.panel.setPixmap(dash.panel_image)
dash.pointer.setPixmap(dash.pointer_image)
dash.preset.setPixmap(dash.preset_image)
dash.pointer.setAlignment(Qt.AlignCenter)
dash.panel.setAlignment(Qt.AlignCenter)
dash.preset.setAlignment(Qt.AlignCenter)
dash.label_setting.style_normal = dash.setting_clicked_style # “设置”标签常态style
dash.label_setting.style_clicked = dash.setting_clicked_style # “设置”标签点击后的style
dash.label_setting.style_enter = dash.setting_enter_style # “设置”标签点击后的style
dash.adjust_down.style_normal = dash.adjusts_normal_style # “微调-”按钮常态style
dash.adjust_down.style_clicked = dash.adjusts_clicked_style # “微调-”按钮点击后的style
dash.adjust_down.style_enter = dash.adjusts_enter_style # “微调-”按钮点击后的style
dash.adjust_up.style_normal = dash.adjusts_normal_style # “微调-”按钮常态style
dash.adjust_up.style_clicked = dash.adjusts_clicked_style # “微调-”按钮点击后的style
dash.adjust_up.style_enter = dash.adjusts_enter_style # “微调-”按钮点击后的style
############################################
# 画面的类,每个画面对应一个同名的首字母大写的类,这个类补充定义了画面
class Form0: # 画面form0的类(form0是主窗口)
def __init__(self):
# 画面中部件的类,每个部件对应一个同名的首字母大写的类,这个类补充定义了对应的部件
self.LabelSystemClock = self.LabelSystemClock()
self.Lamp_working_1 = self.Lamp_working_1()
self.Btn_history = self.Btn_history()
self.Btn_user = self.Btn_user()
self.Btn_exit = self.Btn_exit()
self.Btn_setting = self.Btn_setting()
self.Selector_1 = self.Selector_1()
self.Dash_1 = self.Dash_1()
# 以下测试用 ######################################################
form0.slider1.valueChanged.connect(lambda: self.Dash_1.preset.setPixmap(
self.Dash_1.preset_image.transformed(QTransform().rotate(round(form0.slider1.value() * 0.3)))))
form0.slider2.valueChanged.connect(lambda: self.Dash_1.pointer.setPixmap(
self.Dash_1.pointer_image.transformed(QTransform().rotate(round(form0.slider2.value() * 0.3)))))
# 以上测试用 ######################################################
form0.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) # 关掉边框,始终最前
form0.menuBar().setVisible(False) # 关闭顶部菜单栏
form0.statusBar().setVisible(False) # 关闭底部状态栏
# 画面中的部件的类,每个部件对应一个同名的首字母大写的类,作为对部件的补充定义
class LabelSystemClock(QLabel): # 主画面时钟
def __init__(self, parent=None):
super().__init__(parent)
self.label = form0.labelSystemClock # 本class对应的实体部件
# 本部件的所有特性已经在Qt Designer样式表里面定义,无需补充和再次初始化
class Btn_exit(MyButton): # “退出系统”按钮对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.button = form0.btn_exit # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
self.button.Clicked.connect(self.beClicked) # 本按钮单机后的连接
def beClicked(self): # 本按钮点击后的操作
for f in forms: # 关闭所有窗口
f.close()
class Btn_setting(MyButton): # “参数设置”按钮对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.button = form0.btn_setting # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
# 以下测试用 ######################################################
self.button.Clicked.connect(lambda: Form0.Lamp_working_1.lamp.color_on_bool(False)) # 这两种方法的作用是相同的
form0.btn_setting.Clicked.connect(lambda: form0.lamp_working_1.color_on_bool(False))
# 以上测试用 ######################################################
class Btn_user(MyButton): # “用户管理”按钮对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.button = form0.btn_user # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
class Btn_history(MyButton): # “历史数据”按钮对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.button = form0.btn_history # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
class Lamp_working_1(MyLamp): # “工作指示1”指示灯对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.lamp = form0.lamp_working_1 # 本class对应的实体部件
PublicFunction.green_lamp_init(self.lamp) # 初始化为绿色指示灯
self.lamp.color_on_bool(True) # 初始化为on状态
class Selector_1(MySelectorBox): # 选择器1对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.control0 = form0.control0_selector_1 # 本class对应的实体部件
self.control1 = form0.control1_selector_1
self.state0 = form0.state0_selector_1
self.state1 = form0.state1_selector_1
self.controls = [self.control0, self.control1] # 选择器们的集合
self.states = [self.state0, self.state1] # 状态指示灯的集合
PublicFunction.normal_selectorBox_init(self) # 引用通常选择器的初始化
self.control0.Clicked.connect(lambda: self.control_clicked(0))
self.control1.Clicked.connect(lambda: self.control_clicked(1))
def control_clicked(self, n):
for i in range(len(self.states)):
if i == n:
self.states[i].setStyleSheet(self.states_on_style)
else:
self.states[i].setStyleSheet(self.states_off_style)
class Dash_1(MyDash): # 仪表盘1对应的类
def __init__(self, parent=None):
super().__init__(parent)
self.panel = form0.panel_dash_1 # 仪表面板
self.pointer = form0.pointer_dash_1 # 仪表指针
self.preset = form0.preset_dash_1 # 预设值指示
self.label_setting = form0.label_setting_dash_1 # "设置值“标签
self.label_actual = form0.label_actual_dash_1 # "实际值“标签
self.adjust_down = form0.adjust_down_dash_1 # 微调-按钮
self.adjust_up = form0.adjust_up_dash_1 # 微调+按钮
PublicFunction.normal_dash_init(self) # 引用通常仪表盘的初始化
self.label_setting.DoubleClicked.connect(self.label_setting_double_clicked) # “设定值”标签双击后的连接
def label_setting_double_clicked(self): # “设定值”标签双击后的动作
form_setting.move(self.label_setting.pos()) # 将数据设置画面移动至标签所在位置
form_setting.label_setting.setText(self.label_setting.text()) # 将 “设定值”标签的内容显示在数据设置画面的“设置”标签内
form_setting.show() # 显示设置画面
Form_setting.Btn_OK.todo = self.todo1 # 定义数据设置画面“确定”按钮按下后执行的函数名
def todo1(self): # 数据设置画面“确定”按钮按下后执行的函数内容
self.label_setting.setText(form_setting.label_setting.text())
class Form_setting: # 画面form_setting的类(form_setting是共用的数据设置窗口)
def __init__(self): # 画面form_setting的类的初始化
form_setting.setWindowFlags(Qt.WindowStaysOnTopHint) # 始终最前
self.Btn_OK = self.Btn_OK()
self.Btn_cancel = self.Btn_cancel()
self.Label_setting = self.Label_setting()
class Btn_OK(MyButton): # “确定”按钮的类
todo = None # 这是一个共用的画面,todo是本画面确定键按下后将要执行的函数
args = [] # todo函数的参数
def __init__(self, parent=None):
super().__init__(parent)
self.button = form_setting.btn_OK # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
self.button.Clicked.connect(self.beClicked) # “确定”按钮 点击后的连接
def beClicked(self): # “确定”按钮 点击后的动作
self.todo(*self.args) # 点击OK后执行预置的函数
form_setting.close() # 关闭画面
class Btn_cancel(MyButton): # “取消”按钮的类
def __init__(self, parent=None):
super().__init__(parent)
self.button = form_setting.btn_cancel # 本class对应的实体部件
PublicFunction.normal_button_init(self.button) # 引用通常规格按钮的初始化
self.button.Clicked.connect(form_setting.close) # 关闭画面
class Label_setting(MyLabel): # “设定值”标签的类
def __init__(self, parent=None):
super().__init__(parent)
self.label = form_setting.label_setting # 本class对应的实体部件
PublicFunction.normal_label_init(self.label) # 引用通常规格标签的初始化
############################################
# 主函数
if __name__ == '__main__':
app = QApplication(argv)
QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling) # 解决显示比例不同时的部件变形问题
# 全局公用图片资源 ###################################
normal_dash_panel_image = QPixmap("../UIS/表盘.png")
normal_dash_pointer_image = QPixmap("../UIS/指针.png")
normal_dash_preset_image = QPixmap("../UIS/预设.png")
# 界面相关 ###################################
forms = [] # 所有界面的集合
form0 = uic.loadUi('../UIS/form0.ui') # 生成并加载画面,form0是主画面
forms.append(form0)
Form0 = Form0() # form0对应的类是Form0,将类Form0实体化
form0.show()
form_setting = uic.loadUi('../UIS/设定值.ui')
forms.append(form_setting)
Form_setting = Form_setting() # form_setting对应的类是Form_setting,将类Form_setting实体化
# 全局的类 #
class Ui(QObject): # 作用全局的所有变量和信号在这里定义
signal_1S = pyqtSignal() # 秒周期信号
sys_counter = 0 # 系统计数器,用以控制节拍
timer_1S = QTimer() # 秒周期定时器
timer_1S.start(1000)
def connects(self): # 作用全局的连接在这里定义
self.timer_1S.timeout.connect(Ui.signal_1S.emit) # 秒周期定时器超时后,秒周期信号emit
self.signal_1S.connect(lambda: Form0.LabelSystemClock.label.setText(strftime('%Y-%m-%d %H:%M:%S'))) # 秒周期信号的连接
Ui = Ui() # 实体化
Ui.connects() # 定义全局连接
exit(app.exec_())
运行截图: