from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import QSize, Qt, QTimer from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QSplitter, QPushButton, QFileDialog, QMessageBox, QDialog import qtawesome as qta from UI.VarManages.VarTable import VarTableView, HartTableView, TcRtdTableView, AnalogTableView, \ HartSimulateTableView from UI.VarManages.MessageWidget import MessageWidget from model.ProjectModel.VarManage import ModbusVarManage, TcRtdManage, AnalogManage, HartVarManage, HartSimulateVarManage from UI.VarManages.SwitchVarModelWidget import SwitchVarModelDialog from utils import Globals # 移除 Celery 相关导入,现在使用 ProtocolManage 和 ModbusManager from protocol.TCP.TemToMv import temToMv from protocol.TCP.Analog import getRealAO import re class VarWidgets(QtWidgets.QWidget): _isPopenOpen = False def __init__(self, modbusType, parent=None): super(VarWidgets, self).__init__(parent) self.modbusType = modbusType self.setupUI() def setupButtonIcon(self, button, icon_name, color): """设置按钮图标,支持不同状态的颜色变化""" # 正常状态图标 normal_icon = qta.icon(icon_name, color=color) # 悬停状态图标(稍微亮一些) hover_color = self.lightenColor(color, 0.2) hover_icon = qta.icon(icon_name, color=hover_color) # 按下状态图标(稍微暗一些) pressed_color = self.darkenColor(color, 0.2) pressed_icon = qta.icon(icon_name, color=pressed_color) # 设置图标 button.setIcon(normal_icon) # 存储不同状态的图标,用于状态切换 button._normal_icon = normal_icon button._hover_icon = hover_icon button._pressed_icon = pressed_icon # 连接事件 button.enterEvent = lambda event: self.onButtonEnter(button, event) button.leaveEvent = lambda event: self.onButtonLeave(button, event) button.mousePressEvent = lambda event: self.onButtonPress(button, event) button.mouseReleaseEvent = lambda event: self.onButtonRelease(button, event) def lightenColor(self, color, factor): """使颜色变亮""" if color.startswith('#'): # 十六进制颜色 r = int(color[1:3], 16) g = int(color[3:5], 16) b = int(color[5:7], 16) r = min(255, int(r + (255 - r) * factor)) g = min(255, int(g + (255 - g) * factor)) b = min(255, int(b + (255 - b) * factor)) return f"#{r:02x}{g:02x}{b:02x}" return color def darkenColor(self, color, factor): """使颜色变暗""" if color.startswith('#'): # 十六进制颜色 r = int(color[1:3], 16) g = int(color[3:5], 16) b = int(color[5:7], 16) r = max(0, int(r * (1 - factor))) g = max(0, int(g * (1 - factor))) b = max(0, int(b * (1 - factor))) return f"#{r:02x}{g:02x}{b:02x}" return color def onButtonEnter(self, button, event): """按钮鼠标进入事件""" if hasattr(button, '_hover_icon'): button.setIcon(button._hover_icon) # 调用原始的enterEvent QPushButton.enterEvent(button, event) def onButtonLeave(self, button, event): """按钮鼠标离开事件""" if hasattr(button, '_normal_icon'): button.setIcon(button._normal_icon) # 调用原始的leaveEvent QPushButton.leaveEvent(button, event) def onButtonPress(self, button, event): """按钮鼠标按下事件""" if hasattr(button, '_pressed_icon'): button.setIcon(button._pressed_icon) # 调用原始的mousePressEvent QPushButton.mousePressEvent(button, event) def onButtonRelease(self, button, event): """按钮鼠标释放事件""" # 检查鼠标是否还在按钮上 if button.rect().contains(event.pos()): if hasattr(button, '_hover_icon'): button.setIcon(button._hover_icon) else: if hasattr(button, '_normal_icon'): button.setIcon(button._normal_icon) # 调用原始的mouseReleaseEvent QPushButton.mouseReleaseEvent(button, event) def setupUI(self): self.setAttribute(Qt.WA_StyledBackground, True) # 新建变量按钮 self.createBtn = QPushButton('新建变量') self.createBtn.setObjectName('createBtn') self.createBtn.setIconSize(QSize(18, 18)) self.createBtn.clicked.connect(self.createVar) self.setupButtonIcon(self.createBtn, 'fa5s.plus', '#2277EF') # 批量强制按钮 self.forceBtn = QPushButton('批量强制') self.forceBtn.setObjectName('forceBtn') self.forceBtn.setIconSize(QSize(18, 18)) self.forceBtn.clicked.connect(self.forceVar) self.setupButtonIcon(self.forceBtn, 'fa5s.play', '#F59E0B') self.varModelSwitchBtn = QPushButton('值类型切换') self.varModelSwitchBtn.setObjectName('forceBtn') self.varModelSwitchBtn.setIconSize(QSize(18, 18)) self.varModelSwitchBtn.clicked.connect(self.switchVarModel) self.setupButtonIcon(self.varModelSwitchBtn, 'fa5s.play', '#F59E0B') # 清除颜色按钮 self.clearBtn = QPushButton('清除颜色') self.clearBtn.setObjectName('clearBtn') self.clearBtn.setIconSize(QSize(18, 18)) self.clearBtn.clicked.connect(self.clearColour) self.setupButtonIcon(self.clearBtn, 'fa5s.eraser', '#DC2626') # 查看报文按钮 self.messageBtn = QPushButton('查看报文') self.messageBtn.setObjectName('messageBtn') self.messageBtn.setIconSize(QSize(18, 18)) self.messageBtn.clicked.connect(self.showMessage) self.setupButtonIcon(self.messageBtn, 'fa5s.envelope', '#8B5CF6') # 开始通讯按钮 self.startProtocolBtn = QPushButton('开始通讯') self.startProtocolBtn.setObjectName('startProtocolBtn') self.startProtocolBtn.setIconSize(QSize(18, 18)) self.startProtocolBtn.clicked.connect(self.startProtocol) self.setupButtonIcon(self.startProtocolBtn, 'fa5s.play-circle', '#059669') self.varView = VarTableView(self.modbusType, self) self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) self.varView.setModel(self.proxy) self.varView.proxy = self.proxy self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) self.timer.start(500) # 启动timer Globals.setValue(str(self.modbusType) + 'Table', self.varView) self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.addWidget(self.createBtn,0, 0, 1, 1) self.gridLayout.addWidget(self.forceBtn, 0, 1, 1, 1) self.gridLayout.addWidget(self.messageBtn, 0, 2, 1, 1) self.gridLayout.addWidget(self.startProtocolBtn, 0, 3, 1, 1) self.gridLayout.addWidget(QSplitter(), 0, 4, 1, 20) self.gridLayout.addWidget(self.clearBtn, 0, 4, 1, 1) self.gridLayout.addWidget(self.varModelSwitchBtn, 0, 5, 1, 1) # self.gridLayout.addWidget(self.importBtn, 0, 24, 1, 1) # self.gridLayout.addWidget(self.exportBtn, 0, 25, 1, 1) self.gridLayout.addWidget(self.varView, 1, 0, 10, 26) self.gridLayout.setSpacing(20) self.gridLayout.setContentsMargins(20, 30, 20, 20) # self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged) self.horizontalHeader = self.varView.horizontalHeader() self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked) def switchVarModel(self): # 切换变量值类型 check = [i for i, x in enumerate(self.varView.model.checkList) if x == 'Checked'] if not check: QMessageBox.information(self.parent(), '提示', '请先勾选要切换的变量', QMessageBox.Yes) return False dialog = SwitchVarModelDialog() if dialog.exec_() == QDialog.Accepted: selectedType = dialog.getSelectedType() else: return False successCount = 0 failedVars = [] for i in check: varName = self.varView.model.datas[i][3] # 变量名在第3列 try: # 根据不同的变量类型调用相应的数据库更新方法 if hasattr(self.varView.model, 'modbusType') and self.varView.model.modbusType: # Modbus 变量 ModbusVarManage.editVarModel(varName, selectedType, self.varView.model.modbusType) else: # 根据表格类型判断变量类型 if isinstance(self.varView.model, __import__('UI.VarManages.TCRTDModel', fromlist=['TcRtdModel']).TcRtdModel): TcRtdManage.editVarModel(varName, selectedType) elif isinstance(self.varView.model, __import__('UI.VarManages.AnalogModel', fromlist=['AnalogModel']).AnalogModel): AnalogManage.editVarModel(varName, selectedType) elif isinstance(self.varView.model, __import__('UI.VarManages.HartModel', fromlist=['HartModel']).HartModel): HartVarManage.editVarModel(varName, selectedType) elif isinstance(self.varView.model, __import__('UI.VarManages.HartSimulateModel', fromlist=['HartSimulateModel']).HartSimulateModel): HartSimulateVarManage.editVarModel(varName, selectedType) else: # 默认使用 Modbus 变量管理 ModbusVarManage.editVarModel(varName, selectedType, 'ModbusTcpMaster') # 更新表格数据 - 根据变量类型确定正确的列索引 if hasattr(self.varView.model, 'modbusType') and self.varView.model.modbusType: # Modbus 变量 - 第10列 self.varView.model.datas[i][10] = selectedType elif isinstance(self.varView.model, __import__('UI.VarManages.TCRTDModel', fromlist=['TcRtdModel']).TcRtdModel): # TcRtd 变量 - 第11列 self.varView.model.datas[i][11] = selectedType elif isinstance(self.varView.model, __import__('UI.VarManages.AnalogModel', fromlist=['AnalogModel']).AnalogModel): # Analog 变量 - 第10列 self.varView.model.datas[i][10] = selectedType elif isinstance(self.varView.model, __import__('UI.VarManages.HartModel', fromlist=['HartModel']).HartModel): # Hart 变量 - 第8列 self.varView.model.datas[i][8] = selectedType elif isinstance(self.varView.model, __import__('UI.VarManages.HartSimulateModel', fromlist=['HartSimulateModel']).HartSimulateModel): # HartSimulate 变量 - 第9列 self.varView.model.datas[i][9] = selectedType else: # 默认使用第10列 self.varView.model.datas[i][10] = selectedType successCount += 1 except Exception as e: failedVars.append(varName) print(f"更新变量 {varName} 失败: {str(e)}") # 立即更新界面显示 # 刷新comboBox显示 - 根据变量类型确定正确的列索引 if hasattr(self.varView.model, 'modbusType') and self.varView.model.modbusType: # Modbus 变量 - 第10列 self.varView.model.refreshComboBox(10) elif isinstance(self.varView.model, __import__('UI.VarManages.TCRTDModel', fromlist=['TcRtdModel']).TcRtdModel): # TcRtd 变量 - 第11列 self.varView.model.refreshComboBox(11) elif isinstance(self.varView.model, __import__('UI.VarManages.AnalogModel', fromlist=['AnalogModel']).AnalogModel): # Analog 变量 - 第10列 self.varView.model.refreshComboBox(10) elif isinstance(self.varView.model, __import__('UI.VarManages.HartModel', fromlist=['HartModel']).HartModel): # Hart 变量 - 第8列 self.varView.model.refreshComboBox(8) elif isinstance(self.varView.model, __import__('UI.VarManages.HartSimulateModel', fromlist=['HartSimulateModel']).HartSimulateModel): # HartSimulate 变量 - 第9列 self.varView.model.refreshComboBox(9) else: # 默认使用第10列 self.varView.model.refreshComboBox(10) # 显示结果 if failedVars: QMessageBox.warning(self.parent(), '警告', f'成功更新 {successCount} 个变量,失败 {len(failedVars)} 个变量:\n{", ".join(failedVars)}', QMessageBox.Yes) else: QMessageBox.information(self.parent(), '提示', f'已成功将 {successCount} 个变量切换为:{selectedType}', QMessageBox.Yes) def clearColour(self): Globals.clearValue('forceVars') @QtCore.pyqtSlot(int) def on_view_horizontalHeader_sectionClicked(self, logicalIndex): self.logicalIndex = logicalIndex self.on_comboBox_currentIndexChanged(self.logicalIndex) menuName = '_' + str(logicalIndex) + 'Menu' if not hasattr(self, menuName): setattr(self, menuName, QtWidgets.QMenu(self)) self.menuValues = getattr(self, menuName) menuEdit = QtWidgets.QLineEdit() inputAction = QtWidgets.QWidgetAction(self.menuValues) inputAction.setDefaultWidget(menuEdit) self.menuValues.addAction(inputAction) menuEdit.textChanged.connect(self.on_lineEdit_textChanged) self.menuValues = getattr(self, menuName) self.romoveAction(self.menuValues) self.signalMapper = QtCore.QSignalMapper(self) self.menuValues.mouseReleaseEvent = self._menu_mouseReleaseEvent actionAll = QtWidgets.QAction("All", self) actionAll.triggered.connect(self.on_actionAll_triggered) actionAll.setProperty('canHide', True) actionAll.setCheckable(True) self.menuValues.addAction(actionAll) self.menuValues.addSeparator() valuesUnique = [self.proxy.data(self.proxy.index(row, self.logicalIndex)) for row in range(self.proxy.rowCount()) ] for actionNumber, actionName in enumerate((list(set(valuesUnique)))): action = QtWidgets.QAction(str(actionName), self) self.signalMapper.setMapping(action, actionNumber) action.triggered.connect(self.signalMapper.map) action.setCheckable(True) self.menuValues.addAction(action) self.signalMapper.mapped.connect(self.on_signalMapper_mapped) headerPos =self.varView.mapToGlobal(self.horizontalHeader.pos()) posY = headerPos.y() + self.horizontalHeader.height() posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex) getattr(self, menuName).exec_(QtCore.QPoint(posX, posY)) @QtCore.pyqtSlot() def on_actionAll_triggered(self): # 显示全部 filterColumn = self.logicalIndex filterString = QtCore.QRegExp( "", QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp ) self.proxy.setFilterRegExp(filterString) self.proxy.setFilterKeyColumn(filterColumn) @QtCore.pyqtSlot(int) def on_signalMapper_mapped(self, i): # stringAction = self.signalMapper.mapping(i).text() stringActions = '|'.join([x.text() for x in getattr(self, '_' + str(self.logicalIndex) + 'Menu').actions() if x.isChecked()]) filterColumn = self.logicalIndex filterString = QtCore.QRegExp( stringActions, QtCore.Qt.CaseSensitive, # QtCore.QRegExp.FixedString ) self.proxy.setFilterRegExp(filterString) self.proxy.setFilterKeyColumn(filterColumn) @QtCore.pyqtSlot(str) def on_lineEdit_textChanged(self, text): # 搜索框文字变化函数 search = QtCore.QRegExp( text, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp ) self.proxy.setFilterRegExp(search) @QtCore.pyqtSlot(int) def on_comboBox_currentIndexChanged(self, index): self.proxy.setFilterKeyColumn(index) def _menu_mouseReleaseEvent(self, event): action = self.menuValues.actionAt(event.pos()) if not action: # 没有找到action就交给QMenu自己处理 return QtWidgets.QMenu.mouseReleaseEvent(self.menuValues, event) if action.property('canHide'): # 如果有该属性则给菜单自己处理 return QtWidgets.QMenu.mouseReleaseEvent(self.menuValues, event) # 找到了QAction则只触发Action action.activate(action.Trigger) def romoveAction(self, menu): # 删除输入框之外的按钮1 for action in menu.actions(): if type(action) != QtWidgets.QWidgetAction: menu.removeAction(action) def _checkAction(self): # 三个action都响应该函数 self.labelInfo.setText('\n'.join(['{}\t选中:{}'.format( action.text(), action.isChecked()) for action in self.menuValues.actions()])) def createVar(self): self.varView.model.append_data(['', '', '', '', '', '', '', '', '', '', '','本地值','int']) def forceVar(self): check = [i for i, x in enumerate(self.varView.model.checkList) if x == 'Checked'] if not check: QMessageBox.information(self, '提示', '请先勾选要强制的变量', QMessageBox.Yes) return protocolManage = Globals.getValue('protocolManage') if not protocolManage: QMessageBox.warning(self, '错误', '协议管理器未初始化', QMessageBox.Yes) return forceVars = Globals.getValue('forceVars') if forceVars is None: forceVars = set() for i in check: varMes = self.varView.model.datas[i] value, name, des, varType, slaveID, address, min, max, order = str(varMes[1]), str(varMes[3]), str(varMes[4]), str(varMes[5]), str(varMes[6]), str(varMes[7]), str(varMes[8]), str(varMes[9]), str(varMes[10]) pattern = re.compile(r'[^0-9\.-]+') if not value or re.findall(pattern, str(value)): QMessageBox.warning(self, '警告', "请输入强制值或数字", QMessageBox.Yes) return if min and max: try: if float(value) < float(min) or float(value) > float(max): QMessageBox.warning(self, '警告', "超出量程范围", QMessageBox.Yes) return except Exception: QMessageBox.warning(self, '警告', "量程范围格式错误", QMessageBox.Yes) return elif min and not max: try: if float(value) < float(min): QMessageBox.warning(self, '警告', "超出量程范围", QMessageBox.Yes) return except Exception: QMessageBox.warning(self, '警告', "量程范围格式错误", QMessageBox.Yes) return elif max and not min: try: if float(value) > float(max): QMessageBox.warning(self, '警告', "超出量程范围", QMessageBox.Yes) return except Exception: QMessageBox.warning(self, '警告', "量程范围格式错误", QMessageBox.Yes) return # 使用新的 ProtocolManage 进行变量写入 try: result = protocolManage.writeVariableValue(name, value) if result: forceVars.add(name) else: QMessageBox.information(self, '提示', f'变量 {name} 写入失败', QMessageBox.Yes) except Exception as e: QMessageBox.warning(self, '错误', f'写入变量 {name} 时发生错误: {str(e)}', QMessageBox.Yes) Globals.setValue('forceVars', forceVars) def importVar(self): if Globals.getValue('currentPro'): path = QFileDialog.getOpenFileName(self, "Choose File", "./","VarExcel (*.xlsx);;All Files (*)")[0] if not path: return result = ModbusVarManage.importModbusVar(path) QMessageBox.question(self.parent(), '提示', result, QMessageBox.Yes) self.varView.model.initTable() def exportVar(self): path = QFileDialog.getSaveFileName(self, "Choose File", "./","VarExcel (*.xlsx);;All Files (*)")[0] if not path: return ModbusVarManage.exportModbusVar(path) def showMessage(self): self.messageWidget = MessageWidget() self.messageWidget.show() def startProtocol(self): protocolManage = Globals.getValue('protocolManage') if not protocolManage: QMessageBox.warning(self, '错误', '协议管理器未初始化', QMessageBox.Yes) return if not self._isPopenOpen: # 根据 modbusType 启动对应的 Modbus 通讯 print(self.modbusType) try: success = False if self.modbusType == 'ModbusTcpMaster': # TCP Master success = protocolManage.startModbusTcpMaster() elif self.modbusType == 'ModbusTcpSlave': # TCP Slave success = protocolManage.startModbusTcpSlave() elif self.modbusType == 'ModbusRtuMaster': # RTU Master success = protocolManage.startModbusRtuMaster() elif self.modbusType == 'ModbusRtuSlave': # RTU Slave success = protocolManage.startModbusRtuSlave() if success: self._isPopenOpen = True self.startProtocolBtn.setText('停止通讯') self.setupButtonIcon(self.startProtocolBtn, 'fa5s.stop-circle', '#DC2626') else: QMessageBox.warning(self, '错误', 'Modbus 通讯启动失败', QMessageBox.Yes) except Exception as e: QMessageBox.warning(self, '错误', f'启动通讯时发生错误: {str(e)}', QMessageBox.Yes) else: # 停止对应的 Modbus 通讯 try: success = False if self.modbusType == 'ModbusTcpMaster': # TCP Master success = protocolManage.stopModbusTcpMaster() elif self.modbusType == 'ModbusTcpSlave': # TCP Slave success = protocolManage.stopModbusTcpSlave() elif self.modbusType == 'ModbusRtuMaster': # RTU Master success = protocolManage.stopModbusRtuMaster() elif self.modbusType == 'ModbusRtuSlave': # RTU Slave success = protocolManage.stopModbusRtuSlave() if success: self._isPopenOpen = False self.startProtocolBtn.setText('开始通讯') self.setupButtonIcon(self.startProtocolBtn, 'fa5s.play-circle', '#059669') else: QMessageBox.warning(self, '错误', 'Modbus 通讯停止失败', QMessageBox.Yes) except Exception as e: QMessageBox.warning(self, '错误', f'停止通讯时发生错误: {str(e)}', QMessageBox.Yes) def initIcon(self): self._isPopenOpen = False self.startProtocolBtn.setText('开始通讯') self.setupButtonIcon(self.startProtocolBtn, 'fa5s.play-circle', '#059669') def performSearch(self, searchText): """统一搜索接口""" try: # 设置搜索列为变量名列(通常是第3列) self.on_comboBox_currentIndexChanged(3) self.on_lineEdit_textChanged(searchText) return True except Exception as e: print(f"搜索执行失败: {e}") return False class HartWidgets(VarWidgets): def __init__(self, parent=None): super(HartWidgets, self).__init__(parent) def setupUI(self): self.setAttribute(Qt.WA_StyledBackground, True) self.startProtocolBtn = QPushButton(QIcon('./Static/startProtocol.png'), '开始通讯') self.startProtocolBtn.setObjectName('startProtocolBtn') self.startProtocolBtn.setIconSize(QSize(22, 22)) self.startProtocolBtn.clicked.connect(self.startProtocol) self.varView = HartTableView() self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) self.varView.setModel(self.proxy) self.varView.proxy = self.proxy self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) self.timer.start(50) # 启动timer Globals.setValue('HartTable', self.varView) self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.addWidget(self.startProtocolBtn, 0, 0, 1, 1) self.gridLayout.addWidget(self.varView, 1, 0, 10, 26) self.gridLayout.setSpacing(20) self.gridLayout.setContentsMargins(20, 30, 20, 20) # self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged) self.horizontalHeader = self.varView.horizontalHeader() self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked) def startProtocol(self): pass class TcRtdWidgets(VarWidgets): def __init__(self, parent=None): super(TcRtdWidgets, self).__init__(parent) def setupUI(self): self.setAttribute(Qt.WA_StyledBackground, True) # 批量强制按钮 self.forceBtn = QPushButton('批量强制') self.forceBtn.setObjectName('forceBtn') self.forceBtn.setIconSize(QSize(18, 18)) self.forceBtn.clicked.connect(self.forceVar) self.setupButtonIcon(self.forceBtn, 'fa5s.play', '#F59E0B') # 清除颜色按钮 self.clearBtn = QPushButton('清除颜色') self.clearBtn.setObjectName('clearBtn') self.clearBtn.setIconSize(QSize(18, 18)) self.clearBtn.clicked.connect(self.clearColour) self.setupButtonIcon(self.clearBtn, 'fa5s.eraser', '#DC2626') self.varModelSwitchBtn = QPushButton('值类型切换') self.varModelSwitchBtn.setObjectName('forceBtn') self.varModelSwitchBtn.setIconSize(QSize(18, 18)) self.varModelSwitchBtn.clicked.connect(self.switchVarModel) self.setupButtonIcon(self.varModelSwitchBtn, 'fa5s.play', '#F59E0B') self.varView = TcRtdTableView() self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) self.varView.setModel(self.proxy) self.varView.proxy = self.proxy self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) self.timer.start(50) # 启动timer Globals.setValue('TcRtdTable', self.varView) self.gridLayout = QtWidgets.QGridLayout(self) # self.gridLayout.addWidget(self.createBtn, 0, 0, 1, 1) self.gridLayout.addWidget(self.forceBtn, 0, 0, 1, 1) # self.gridLayout.addWidget(self.startProtocolBtn, 0, 1, 1, 1) self.gridLayout.addWidget(self.clearBtn, 0, 1, 1, 1) self.gridLayout.addWidget(self.varModelSwitchBtn, 0, 2, 1, 1) self.gridLayout.addWidget(self.varView, 1, 0, 10, 26) self.gridLayout.setSpacing(20) self.gridLayout.setContentsMargins(20, 30, 20, 20) self.horizontalHeader = self.varView.horizontalHeader() self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked) def createVar(self): self.varView.model.append_data(['', '', '', '', '', '', '', '', '','']) def forceVar(self): check = [i for i, x in enumerate(self.varView.model.checkList) if x == 'Checked'] if not check: QMessageBox.information(self, '提示', '请先勾选要强制的变量', QMessageBox.Yes) return forceVars = Globals.getValue('forceVars') if forceVars is None: forceVars = set() for i in check: value = self.varView.model.datas[i][1] varName = self.varView.model.datas[i][3] min_ = self.varView.model.datas[i][7] max_ = self.varView.model.datas[i][8] pattern = re.compile(r'[^0-9\.-]+') if not value or re.findall(pattern, str(value)): QMessageBox.warning(self, '警告', "请输入强制值或数字", QMessageBox.Yes) return if min_ and max_: try: if float(value) < float(min_) or float(value) > float(max_): QMessageBox.warning(self, '警告', "超出量程范围", QMessageBox.Yes) return except Exception: QMessageBox.warning(self, '警告', "量程范围格式错误", QMessageBox.Yes) return # print(varName, float(value)) res = Globals.getValue('protocolManage').writeVariableValue(varName, float(value)) if res: forceVars.add(varName) else: QMessageBox.information(self, '提示', f'变量 {varName} 写入失败', QMessageBox.Yes) Globals.setValue('forceVars', forceVars) # self.varView.model.refreshValueCache() # self.varView.model.layoutChanged.emit() class AnalogWidgets(VarWidgets): def __init__(self, parent=None): super(AnalogWidgets, self).__init__(parent) def setupUI(self): self.setAttribute(Qt.WA_StyledBackground, True) # 批量强制按钮 self.forceBtn = QPushButton('批量强制') self.forceBtn.setObjectName('forceBtn') self.forceBtn.setIconSize(QSize(18, 18)) self.forceBtn.clicked.connect(self.forceVar) self.setupButtonIcon(self.forceBtn, 'fa5s.play', '#F59E0B') # 清除颜色按钮 self.clearBtn = QPushButton('清除颜色') self.clearBtn.setObjectName('clearBtn') self.clearBtn.setIconSize(QSize(18, 18)) self.clearBtn.clicked.connect(self.clearColour) self.setupButtonIcon(self.clearBtn, 'fa5s.eraser', '#DC2626') self.varModelSwitchBtn = QPushButton('值类型切换') self.varModelSwitchBtn.setObjectName('forceBtn') self.varModelSwitchBtn.setIconSize(QSize(18, 18)) self.varModelSwitchBtn.clicked.connect(self.switchVarModel) self.setupButtonIcon(self.varModelSwitchBtn, 'fa5s.play', '#F59E0B') self.varView = AnalogTableView() self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) self.varView.setModel(self.proxy) self.varView.proxy = self.proxy self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) self.timer.start(50) # 启动timer Globals.setValue('AnalogTable', self.varView) self.gridLayout = QtWidgets.QGridLayout(self) # self.gridLayout.addWidget(self.createBtn, 0, 0, 1, 1) self.gridLayout.addWidget(self.forceBtn, 0, 0, 1, 1) # self.gridLayout.addWidget(self.startProtocolBtn, 0, 1, 1, 1) self.gridLayout.addWidget(self.clearBtn, 0, 1, 1, 1) self.gridLayout.addWidget(self.varModelSwitchBtn, 0, 2, 1, 1) self.gridLayout.addWidget(self.varView, 1, 0, 10, 26) self.gridLayout.setSpacing(20) self.gridLayout.setContentsMargins(20, 30, 20, 20) self.horizontalHeader = self.varView.horizontalHeader() self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked) def createVar(self): self.varView.model.append_data(['', '', '', '', '', '', '', '', '']) def forceVar(self): check = [i for i, x in enumerate(self.varView.model.checkList) if x == 'Checked'] if not check: QMessageBox.information(self, '提示', '请先勾选要强制的变量', QMessageBox.Yes) return forceVars = Globals.getValue('forceVars') if forceVars is None: forceVars = set() for i in check: value = self.varView.model.datas[i][1] varName = self.varView.model.datas[i][3] min_ = self.varView.model.datas[i][7] max_ = self.varView.model.datas[i][8] pattern = re.compile(r'[^0-9\.-]+') if not value or re.findall(pattern, str(value)): QMessageBox.warning(self, '警告', "请输入强制值或数字", QMessageBox.Yes) return if min_ and max_: try: if float(value) < float(min_) or float(value) > float(max_): QMessageBox.warning(self, '警告', "超出量程范围", QMessageBox.Yes) return except Exception: QMessageBox.warning(self, '警告', "量程范围格式错误", QMessageBox.Yes) return res = Globals.getValue('protocolManage').writeVariableValue(varName, float(value)) if res: forceVars.add(varName) else: QMessageBox.information(self, '提示', f'变量 {varName} 写入失败', QMessageBox.Yes) Globals.setValue('forceVars', forceVars) # self.varView.model.refreshValueCache() # self.varView.model.layoutChanged.emit() class HartSimulateWidgets(VarWidgets): def __init__(self, parent=None): super(HartSimulateWidgets, self).__init__(parent) def setupUI(self): self.setAttribute(Qt.WA_StyledBackground, True) self.startProtocolBtn = QPushButton(QIcon('./Static/startProtocol.png'), '开始通讯') self.startProtocolBtn.setObjectName('startProtocolBtn') self.startProtocolBtn.setIconSize(QSize(22, 22)) self.startProtocolBtn.clicked.connect(self.startProtocol) self.varView = HartSimulateTableView() self.varView.setObjectName('varView') self.proxy = QtCore.QSortFilterProxyModel(self) self.proxy.setSourceModel(self.varView.model) self.varView.setModel(self.proxy) self.varView.proxy = self.proxy self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) self.timer.start(50) # 启动timer Globals.setValue('HartSimulateTable', self.varView) self.gridLayout = QtWidgets.QGridLayout(self) self.gridLayout.addWidget(self.startProtocolBtn, 0, 0, 1, 1) self.gridLayout.addWidget(self.varView, 1, 0, 10, 26) self.gridLayout.setSpacing(20) self.gridLayout.setContentsMargins(20, 30, 20, 20) self.horizontalHeader = self.varView.horizontalHeader() self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked) def startProtocol(self): pass