资讯动态

基于QtPy (PySide6) 的PLC-HMI工程实战记录(八)创建适合项目的进度条

发布时间:2026/8/31 5:08:19 来源:尧图企业网站定制
用于推焦和装煤的行程指示。1、安全色为绿色危险色为橙色越界色为红色随着行程控制点的接近颜色由安全色向危险色过度。2、炉前减速为橙色。中限停车和前限停车位越界色。3、所有的行程和设置为以米为单位的实数进度条的value数字为厘米单位的实数显示值为以米为单位的实数。4、显示数字随进度条移动颜色为背景的反色。################################################################################ # 定制的窗口部件 ################################################################################ import sys from PySide6.QtCore import Signal, Qt, Slot from PySide6.QtGui import QPainter, QFontMetrics, QColor from PySide6.QtWidgets import QLineEdit, QWidget, QSpinBox, QDoubleSpinBox, QProgressBar, QApplication, QVBoxLayout, \ QSlider, QLabel # 自定义的QLineEdit class HmiLineEdit(QLineEdit): hmi_textChanged Signal(str) def __init__(self, arg1None, parentNone): 兼容Qt Designer提升控件两种调用形式 HmiLineEdit(text:str, parent) HmiLineEdit(parent) if isinstance(arg1, str): # 第一个参数是文本 super().__init__(arg1, parent) else: # 第一个参数是parent控件 super().__init__(parentarg1) def focusInEvent(self, event): 获得焦点开始编辑会话保存原始内容 super().focusInEvent(event) self._original_text self.text() self._is_editing True def setText(self, text: str): 重写setText 用户正在编辑时外部调用setText直接忽略 内部提交确认时先关闭编辑标记再调用父类setText if self._is_editing: # 处于编辑模式外来信号/代码调用setText不生效 return super().setText(text) def keyPressEvent(self, event): key event.key() if key Qt.Key_Escape: # Esc放弃修改恢复原始内容结束编辑 self._is_editing False super().setText(self._original_text) self.clearFocus() event.accept() return elif key in (Qt.Key_Return, Qt.Key_Enter): # 回车确认接受当前输入内容结束编辑触发editConfirmed new_text self.text() self._is_editing False super().setText(new_text) self.hmi_textChanged.emit(new_text) self.clearFocus() event.accept() return super().keyPressEvent(event) def focusOutEvent(self, event): 失去焦点回退原始值取消编辑 if self._is_editing: self._is_editing False super().setText(self._original_text) super().focusOutEvent(event) # 自定义的QSpinBox class HmiSpinBox(QSpinBox): hmi_valueChanged Signal(int) def __init__(self, parentNone): super().__init__(parent) self._is_editing False def focusInEvent(self, event): 获得焦点开始编辑会话保存原始内容 super().focusInEvent(event) self._original_value self.value() self._is_editing True def setValue(self, value: int): 重写setValue 用户正在编辑时外部调用setValue直接忽略 内部提交确认时先关闭编辑标记再调用父类setValue if self._is_editing: # 处于编辑模式外来信号/代码调用setValue不生效 return super().setValue(value) def keyPressEvent(self, event): key event.key() if key Qt.Key_Escape: # Esc放弃修改恢复原始内容结束编辑 self._is_editing False super().setValue(self._original_value) self.clearFocus() event.accept() return elif key in (Qt.Key_Return, Qt.Key_Enter): # 回车确认接受当前输入内容结束编辑触发editConfirmed new_value self.value() self._is_editing False super().setValue(new_value) self.hmi_valueChanged.emit(new_value) self.clearFocus() event.accept() return super().keyPressEvent(event) def focusOutEvent(self, event): 失去焦点回退原始值取消编辑 if self._is_editing: self._is_editing False super().setValue(self._original_value) super().focusOutEvent(event) # 自定义的QSpinBox class HmiDoubleSpinBox(QDoubleSpinBox): hmi_valueChanged Signal(float) def __init__(self, parentNone): super().__init__(parent) self._is_editing False def focusInEvent(self, event): 获得焦点开始编辑会话保存原始内容 super().focusInEvent(event) self._original_value self.value() self._is_editing True def setValue(self, value: float): 重写setValue 用户正在编辑时外部调用setValue直接忽略 内部提交确认时先关闭编辑标记再调用父类setValue if self._is_editing: # 处于编辑模式外来信号/代码调用setValue不生效 return super().setValue(value) def keyPressEvent(self, event): key event.key() if key Qt.Key_Escape: # Esc放弃修改恢复原始内容结束编辑 self._is_editing False super().setValue(self._original_value) self.clearFocus() event.accept() return elif key in (Qt.Key_Return, Qt.Key_Enter): # 回车确认接受当前输入内容结束编辑触发editConfirmed new_value self.value() self._is_editing False super().setValue(new_value) self.hmi_valueChanged.emit(new_value) self.clearFocus() event.accept() return super().keyPressEvent(event) def focusOutEvent(self, event): 失去焦点回退原始值取消编辑 if self._is_editing: self._is_editing False super().setValue(self._original_value) super().focusOutEvent(event) # 自定义的进度条 class HmiProgressBar(QProgressBar): 槽函数 set_intial_danger(initial_danger: float) 炉前减速米数 set_mid_limit(mid_limit: float) 中限位置米数 set_minimum(minimum: float) 起点米数 set_maximum(maximum: float) 前限米数 set_value(value: float) 设置实时行程值米数 def __init__(self, parentNone): super().__init__(parent) self.safe_color_h 120 # 安全色绿色 self.danger_color_h 30 # 危险色橙色 self.over_color_h 0 # 超限色红色 self.Initial_danger 0 # 起步的危险阶段炉前减速 self.mid_limit 0 # 回退的中限值 self.is_mid_limit False # 回退的中限 self._color_h self.safe_color_h # 背景色的色系 self._last_val self.value() # 最后一次值 self._real_value 0.0 self.valueChanged.connect(self.refresh) # self.label QLabel(aaaaaaaaa,self) # self.label.setGeometry(0, 0 ,100, self.height()) Slot() def set_intial_danger(self, initial_danger: float): self.Initial_danger int(initial_danger * 100.0) Slot() def set_mid_limit(self, mid_limit: float): self.mid_limit int(mid_limit * 100.0) Slot() def set_maximum(self, maximum: float): self.setMaximum(int(maximum * 100.0)) Slot() def set_minimum(self, minimum: float): self.setMinimum(int(minimum * 100.0)) Slot() def set_value(self, value: float): self._real_value value * 100.0 self.setValue(max(min(int(self._real_value), self.maximum()), self.minimum())) # 限制self.value()不越界 self.update() # 这行是必需的否则无法显示超限后的数字 def paintEvent(self, event): self.setTextVisible(False) super().paintEvent(event) painter QPainter(self) color QColor() h (self._color_h 180) % 360 print(self._color_h, h) color.setHsl(h, 200, 122) painter.setPen(color) text self.text() fm QFontMetrics(self.font()) text_width fm.horizontalAdvance(text) # 计算进度块末端 x 坐标 ratio self.value() / self.maximum() if self.maximum() 0 else 0 chunk_end_x int(self.width() * ratio) # 文字放在进度块末端右侧 6px x chunk_end_x - 120 # 防止超出控件右边界 if x text_width self.width() - 4: x self.width() - text_width - 4 if x 2: x 2 # x max(x, 60) y (self.height() - fm.height()) // 2 fm.ascent() painter.drawText(x, y, text) painter.end() Slot() def refresh(self): s self.value() - self._last_val # 本次增量 range_val self.maximum() - self.minimum() # 数值范围 range_h self.safe_color_h - self.danger_color_h # 颜色范围 if s 0: # 增量方向 self._color_h self.safe_color_h - (self.value() - self.Initial_danger) / range_val * range_h else: self._color_h self.safe_color_h - (self.maximum() - self.value()) / range_val * range_h self._color_h min(max(self._color_h, self.danger_color_h), self.safe_color_h) color_h int(self._color_h) # 起步段 if self.value() self.Initial_danger: color_h self.danger_color_h self._last_val self.value() contrast_color abs(color_h 180) self.setStyleSheet(fQProgressBar::chunk {{background-color:hsl({color_h},85%,50%);}}) # print(fQProgressBar {{color:hsl({contrast_color},85%,50%);}} # QProgressBar::chunk {{background-color:hsl({color_h},85%,50%);}}) self.style().unpolish(self) self.style().polish(self) # 超上限或超中限 if self.value() self.maximum() or self.is_mid_limit: self.setStyleSheet(fQProgressBar::chunk {{background-color:hsl({self.over_color_h},85%,50%);}});)) self.style().unpolish(self) self.style().polish(self) def text(self): # 增加工况提示 v self._real_value / 100.0 return f{v:.2f} m if __name__ __main__: def on_value_changed(value): value value / 100.0 form.progressBar.set_value(value) app QApplication(sys.argv) form QWidget() with open(../styles/style_base_setting.qss, r, encodingutf-8) as f: stylesheet f.read() app.setStyleSheet(stylesheet) layout QVBoxLayout() form.setLayout(layout) form.progressBar HmiProgressBar() form.slider QSlider() form.slider.setOrientation(Qt.Horizontal) form.slider.setRange(-200, 12000) form.slider.setValue(0) form.progressBar.set_minimum(0.0) form.progressBar.set_maximum(100.0) form.progressBar.set_intial_danger(10.26) form.progressBar.set_mid_limit(13.23) form.progressBar.setFixedSize(500,60) form.slider.valueChanged.connect(on_value_changed) layout.addWidget(form.progressBar) layout.addWidget(form.slider) form.show() sys.exit(app.exec())

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价