From cf88a2c37dd6d21d109e169f3c0278f5b3747ff0 Mon Sep 17 00:00:00 2001 From: zcwBit Date: Thu, 24 Jul 2025 15:14:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=AD=9B=E9=80=89BUG=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=A1=A8=E6=A0=BC=E6=8C=89=E9=92=AE=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UI/ProfibusWidgets/BlockParameterModel.py | 25 +++++-- UI/ProfibusWidgets/BlockParameterView.py | 4 +- UI/VarManages/AnalogModel.py | 71 ++++++++++++-------- UI/VarManages/BaseButtonDelegate.py | 79 +++++++++++++++++++++++ UI/VarManages/HartModel.py | 23 ++++--- UI/VarManages/HartSimulateModel.py | 39 +++++++---- UI/VarManages/ModbusModel.py | 59 ++++++++++++----- UI/VarManages/RpcVarModel.py | 18 ++++-- UI/VarManages/TCRTDModel.py | 52 ++++++++++----- UI/VarManages/VarTable.py | 2 +- UI/VarManages/VarWidget.py | 2 +- protocol/ModBus/ModbusManager.py | 57 ++++------------ protocol/ModBus/rtuslave_example.py | 7 +- protocol/ModBus/tcpslave_example.py | 8 ++- protocol/ProtocolManage.py | 12 ++-- 15 files changed, 309 insertions(+), 149 deletions(-) create mode 100644 UI/VarManages/BaseButtonDelegate.py diff --git a/UI/ProfibusWidgets/BlockParameterModel.py b/UI/ProfibusWidgets/BlockParameterModel.py index df86322..6100e08 100644 --- a/UI/ProfibusWidgets/BlockParameterModel.py +++ b/UI/ProfibusWidgets/BlockParameterModel.py @@ -11,6 +11,7 @@ QComboBox, QStyledItemDelegate, QVBoxLayout, QSplitter from UI.ProfibusWidgets.LoadingDataWidget import LoadingDataWidget from UI.ProfibusWidgets.ObjectTypeEditlayout import ObjectTypeEditlayout +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate from utils import Globals @@ -147,10 +148,10 @@ class VarTableModel(QAbstractTableModel): -class VarButtonDelegate(QItemDelegate): +class ProfibusButtonDelegate(BaseButtonDelegate): """该类用于向单元格中添加按钮 任务表格""" def __init__(self, parent=None): - super(VarButtonDelegate, self).__init__(parent) + super(ProfibusButtonDelegate, self).__init__(parent) def paint(self, painter, option, index): if not self.parent().indexWidget(index): @@ -239,9 +240,15 @@ class VarButtonDelegate(QItemDelegate): sender = self.sender() blockView = self.parent() model = blockView.model + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + blockType = blockView.blockType blockIndex = blockView.blcokIndex - parmIndex = model.datas[sender.index[0]][1] + parmIndex = model.datas[source_row][1] blockManage = Globals.getValue('blockManage') values = [] #修改单位 @@ -261,7 +268,7 @@ class VarButtonDelegate(QItemDelegate): values = objectTypeEditlayout.getEditlineValue() - dataType = model.datas[sender.index[0]][4] + dataType = model.datas[source_row][4] if not values: reply = QMessageBox.question(self.parent(), '警告', @@ -286,7 +293,13 @@ class VarButtonDelegate(QItemDelegate): sender = self.sender() blockView = self.parent() model = blockView.model - parmIndex = model.datas[sender.index[0]][1] + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + parmIndex = model.datas[source_row][1] blockType = blockView.blockType blockIndex = blockView.blcokIndex # print(blockName, blcokIndex, index) @@ -297,7 +310,7 @@ class VarButtonDelegate(QItemDelegate): # self.loadingDataWidget.loadData(1, 1) self.loadingDataWidget = LoadingDataWidget(refreshType = True) self.loadingDataWidget.exec_() - model.updateColumn(sender.index[0], value) + model.updateColumn(source_row, value) # self.parent().resizeHeader() blockView.proxy.invalidate() diff --git a/UI/ProfibusWidgets/BlockParameterView.py b/UI/ProfibusWidgets/BlockParameterView.py index 6072960..b9aabe6 100644 --- a/UI/ProfibusWidgets/BlockParameterView.py +++ b/UI/ProfibusWidgets/BlockParameterView.py @@ -2,7 +2,7 @@ from PyQt5 import QtCore from PyQt5.QtGui import QTextDocument from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QHeaderView, QAbstractItemView, QTableView, QWidget, QDesktopWidget -from UI.ProfibusWidgets.BlockParameterModel import VarTableModel, VarButtonDelegate +from UI.ProfibusWidgets.BlockParameterModel import VarTableModel, ProfibusButtonDelegate from utils.DBModels.DeviceParModels import * @@ -49,7 +49,7 @@ class ParmView(QTableView): self.setData() def setHeader(self): - self.setItemDelegateForColumn(7, VarButtonDelegate(self)) + self.setItemDelegateForColumn(7, ProfibusButtonDelegate(self)) # self.setItemDelegateForColumn(5, ComboBoxDelegate(self)) self.model = VarTableModel(['序号','索引', '参数名', '描述', '数据类型', '访问', '当前值', '输入值'], [], table = self) diff --git a/UI/VarManages/AnalogModel.py b/UI/VarManages/AnalogModel.py index 5a60b87..d6cff97 100644 --- a/UI/VarManages/AnalogModel.py +++ b/UI/VarManages/AnalogModel.py @@ -112,7 +112,13 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): def trend_action(self): sender = self.sender() model = self.parent().model - varName = model.datas[sender.index[0]][3] + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + varName = model.datas[source_row][3] # 以变量名为key,避免重复打开 if varName not in self.trendWindows or self.trendWindows[varName] is None: self.trendWindows[varName] = ActualTrend(varName=varName) @@ -123,13 +129,19 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): def edit_action(self): sender = self.sender() model = self.parent().model + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + if sender.isEdit: sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) sender.isEdit = False - sender.oldName = model.datas[sender.index[0]][3] - model.editableList.append(sender.index[0]) + sender.oldName = model.datas[source_row][3] + model.editableList.append(source_row) else: - varMes = model.datas[sender.index[0]] + varMes = model.datas[source_row] name,channelNumber, des, varType, min, max = str(varMes[3]), str(varMes[4]), str(varMes[5]),str(varMes[6]), str(varMes[7]), str(varMes[8]) if not name or not varType: reply = QMessageBox.question(self.parent(), @@ -138,7 +150,7 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): QMessageBox.Yes) return sender.isEdit = True - model.editableList.remove(sender.index[0]) + model.editableList.remove(source_row) if sender.oldName: if sender.oldName == name: AnalogManage.editVar(name=sender.oldName, Nname=name, channelNumber = channelNumber, @@ -159,24 +171,30 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): des=des, varType=varType, min=min, max=max) sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) - rowIndex = sender.index[0] varMes = AnalogManage.getByName(name) varMes.insert(1, '') varMes.insert(2, '') varMes.append('') - model.insert_data(varMes, rowIndex) - model.remove_row(rowIndex + 1) + model.insert_data(varMes, source_row) + model.remove_row(source_row + 1) def start_action(self): sender = self.sender() model = self.parent().model - value = model.datas[sender.index[0]][1] - varType = model.datas[sender.index[0]][6] - min = model.datas[sender.index[0]][7] - max = model.datas[sender.index[0]][8] - # print(model.datas[sender.index[0]][9]) + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + # 获取强制值(从视图)和其他数据(从源模型) + value = self._get_view_data(view_row, 1) # 强制值列 + varType = model.datas[source_row][6] + min = model.datas[source_row][7] + max = model.datas[source_row][8] + # print(model.datas[source_row][9]) pattern = re.compile(r'[^0-9\.-]+') - if varType in ['AI', 'DI'] and model.datas[sender.index[0]][9] != '模拟值': + if varType in ['AI', 'DI'] and model.datas[source_row][9] != '模拟值': reply = QMessageBox.question(self.parent(), '警告', "AI,DI类型变量不允许强制值", @@ -188,14 +206,14 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): "请输入强制值或数字", QMessageBox.Yes) return - if sender.index[0] > 15: + if source_row > 15: if not value.isdigit: reply = QMessageBox.question(self.parent(), '警告', "请输入0或1", QMessageBox.Yes) return - if min and max and sender.index[0] < 16: + if min and max and source_row < 16: if float(value) < float(min) or float(value) > float(max): reply = QMessageBox.question(self.parent(), '警告', @@ -205,29 +223,30 @@ class AnalogButtonDelegate(TcRtdButtonDelegate): else: min = None max = None - # print(value, sender.index[0]) - if (float(value) > 20 or float(value)) < 4 and sender.index[0] < 8: + # print(value, source_row) + if (float(value) > 20 or float(value)) < 4 and source_row < 8: reply = QMessageBox.question(self.parent(), '警告', "超出量程范围1", QMessageBox.Yes) return - if float(value) > 10000 or float(value) < 0.1 and 8 < sender.index[0] < 16: + if float(value) > 10000 or float(value) < 0.1 and 8 < source_row < 16: reply = QMessageBox.question(self.parent(), '警告', "超出量程范围2", QMessageBox.Yes) return - # if sender.index[0] < 8: - # model.table.realList[sender.index[0]] = getRealAO(float(value), max, min) - # model.table.valueList[sender.index[0]] = float(value) + # if source_row < 8: + # model.table.realList[source_row] = getRealAO(float(value), max, min) + # model.table.valueList[source_row] = float(value) # else: - # model.table.realList[sender.index[0]] = float(value) - # model.table.valueList[sender.index[0]] = float(value) - res = Globals.getValue('protocolManage').writeVariableValue(model.datas[sender.index[0]][3], float(value)) + # model.table.realList[source_row] = float(value) + # model.table.valueList[source_row] = float(value) + # print(model.datas[source_row][3]) + res = Globals.getValue('protocolManage').writeVariableValue(model.datas[source_row][3], float(value)) if res: forceVars = Globals.getValue('forceVars') - forceVars.add(model.datas[sender.index[0]][3]) + forceVars.add(model.datas[source_row][3]) Globals.setValue('forceVars', forceVars) else: QMessageBox.information(self.parent(), '提示', '写入失败', QMessageBox.Yes) diff --git a/UI/VarManages/BaseButtonDelegate.py b/UI/VarManages/BaseButtonDelegate.py new file mode 100644 index 0000000..f8e8ecc --- /dev/null +++ b/UI/VarManages/BaseButtonDelegate.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +基础按钮委托类 +提供通用的按钮索引获取和数据访问方法 +""" + +from PyQt5.QtWidgets import QItemDelegate, QMessageBox +from PyQt5.QtCore import Qt + + +class BaseButtonDelegate(QItemDelegate): + """基础按钮委托类,提供通用的索引处理方法""" + + def __init__(self, parent=None): + super(BaseButtonDelegate, self).__init__(parent) + + def _get_button_row_indices(self, button): + """ + 获取按钮所在的视图行和源模型行索引 + 返回: (view_row, source_row) 或 (None, None) 如果找不到 + """ + table_view = self.parent() + + # 获取操作列索引(最后一列) + if hasattr(table_view, 'proxy') and table_view.proxy: + operation_col = table_view.proxy.columnCount() - 1 + row_count = table_view.proxy.rowCount() + else: + operation_col = table_view.model.columnCount() - 1 + row_count = table_view.model.rowCount() + + # 查找按钮所在的行 + for row in range(row_count): + if hasattr(table_view, 'proxy') and table_view.proxy: + index = table_view.proxy.index(row, operation_col) + else: + index = table_view.model.index(row, operation_col) + + widget = table_view.indexWidget(index) + if widget and widget.layout(): + for i in range(widget.layout().count()): + if widget.layout().itemAt(i).widget() == button: + view_row = row + + # 转换为源模型索引 + if hasattr(table_view, 'proxy') and table_view.proxy: + proxy_index = table_view.proxy.index(view_row, 0) + source_index = table_view.proxy.mapToSource(proxy_index) + source_row = source_index.row() + else: + source_row = view_row + + return view_row, source_row + + return None, None + + def _get_view_data(self, view_row, column): + """ + 从视图中获取指定行列的数据 + """ + table_view = self.parent() + if hasattr(table_view, 'proxy') and table_view.proxy: + index = table_view.proxy.index(view_row, column) + return table_view.proxy.data(index, Qt.DisplayRole) + else: + return table_view.model.datas[view_row][column] + + def _validate_button_indices(self, button): + """ + 验证并获取按钮索引,如果失败则显示错误消息 + 返回: (view_row, source_row) 或 (None, None) + """ + view_row, source_row = self._get_button_row_indices(button) + if view_row is None: + QMessageBox.warning(self.parent(), '错误', '无法确定按钮所在行', QMessageBox.Yes) + return None, None + return view_row, source_row \ No newline at end of file diff --git a/UI/VarManages/HartModel.py b/UI/VarManages/HartModel.py index 349da73..888ca60 100644 --- a/UI/VarManages/HartModel.py +++ b/UI/VarManages/HartModel.py @@ -5,7 +5,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QMessageBox from model.ProjectModel.VarManage import * from UI.VarManages.ModbusModel import * from utils import Globals - +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate class HartModel(VarTableModel): def __init__(self, header, data: list, table = None): @@ -86,7 +86,7 @@ class HartModel(VarTableModel): return Qt.ItemIsEnabled -class HartButtonDelegate(VarButtonDelegate): +class HartButtonDelegate(BaseButtonDelegate): """该类用于向单元格中添加按钮 任务表格""" def __init__(self, parent=None): @@ -128,13 +128,19 @@ class HartButtonDelegate(VarButtonDelegate): def edit_action(self): sender = self.sender() model = self.parent().model + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + if sender.isEdit: sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) sender.isEdit = False - sender.oldName = model.datas[sender.index[0]][1] - model.editableList.append(sender.index[0]) + sender.oldName = model.datas[source_row][1] + model.editableList.append(source_row) else: - varMes = model.datas[sender.index[0]] + varMes = model.datas[source_row] name, des = str(varMes[1]), str(varMes[2]) if not name : reply = QMessageBox.question(self.parent(), @@ -143,13 +149,12 @@ class HartButtonDelegate(VarButtonDelegate): QMessageBox.Yes) return sender.isEdit = True - model.editableList.remove(sender.index[0]) + model.editableList.remove(source_row) if sender.oldName: HartVarManage.editVar(name = sender.oldName, Nname = name, des = des) else: HartVarManage.createVar(varName = name, des = des) sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) - rowIndex = sender.index[0] varMes = HartVarManage.getByName(name) - model.insert_data(varMes, rowIndex) - model.remove_row(rowIndex + 1) \ No newline at end of file + model.insert_data(varMes, source_row) + model.remove_row(source_row + 1) \ No newline at end of file diff --git a/UI/VarManages/HartSimulateModel.py b/UI/VarManages/HartSimulateModel.py index ebae1b7..5ca37a6 100644 --- a/UI/VarManages/HartSimulateModel.py +++ b/UI/VarManages/HartSimulateModel.py @@ -4,6 +4,7 @@ from PyQt5.QtCore import Qt, QVariant, QSize from PyQt5.QtWidgets import QHBoxLayout, QWidget, QMessageBox, QComboBox from model.ProjectModel.VarManage import * from UI.VarManages.ModbusModel import * +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate from utils import Globals import re @@ -75,7 +76,7 @@ class HartSimulateModel(VarTableModel): return Qt.ItemIsEnabled -class HartSimulateButtonDelegate(VarButtonDelegate): +class HartSimulateButtonDelegate(BaseButtonDelegate): """该类用于向单元格中添加按钮 任务表格""" def __init__(self, parent=None): @@ -128,13 +129,19 @@ class HartSimulateButtonDelegate(VarButtonDelegate): def edit_action(self): sender = self.sender() model = self.parent().model + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + if sender.isEdit: sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) sender.isEdit = False - sender.oldName = model.datas[sender.index[0]][1] - model.editableList.append(sender.index[0]) + sender.oldName = model.datas[source_row][1] + model.editableList.append(source_row) else: - varMes = model.datas[sender.index[0]] + varMes = model.datas[source_row] name, des = str(varMes[1]), str(varMes[2]) # print(name, des) if not name : @@ -144,24 +151,30 @@ class HartSimulateButtonDelegate(VarButtonDelegate): QMessageBox.Yes) return sender.isEdit = True - model.editableList.remove(sender.index[0]) + model.editableList.remove(source_row) if sender.oldName: HartSimulateVarManage.editVar(name = sender.oldName, Nname = name, des = des) else: HartSimulateVarManage.createVar(varName = name, des = des) sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) - rowIndex = sender.index[0] varMes = HartSimulateVarManage.getByName(name) - model.insert_data(varMes, rowIndex) - model.remove_row(rowIndex + 1) + model.insert_data(varMes, source_row) + model.remove_row(source_row + 1) def start_action(self): sender = self.sender() model = self.parent().model - value1 = model.datas[sender.index[0]][3] - minSpan = model.datas[sender.index[0]][7] - maxSpan = model.datas[sender.index[0]][8] - writeList = [str(x) for x in model.datas[sender.index[0]][3:9]] + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + # 获取强制值(从视图)和其他数据(从源模型) + value1 = self._get_view_data(view_row, 3) # 强制值列 + minSpan = model.datas[source_row][7] + maxSpan = model.datas[source_row][8] + writeList = [str(x) for x in model.datas[source_row][3:9]] pattern = re.compile(r'[^0-9\.-]+') if minSpan and maxSpan and value1 and not re.findall(pattern, str(value1) + str(minSpan) + str(maxSpan)): if float(value1) < float(minSpan) or float(value1) > float(maxSpan): @@ -188,5 +201,5 @@ class HartSimulateButtonDelegate(VarButtonDelegate): return forceVars = Globals.getValue('forceVars') - forceVars.add(model.datas[sender.index[0]][1]) + forceVars.add(model.datas[source_row][1]) Globals.setValue('forceVars', forceVars) \ No newline at end of file diff --git a/UI/VarManages/ModbusModel.py b/UI/VarManages/ModbusModel.py index 86db4a1..4ed7a02 100644 --- a/UI/VarManages/ModbusModel.py +++ b/UI/VarManages/ModbusModel.py @@ -9,6 +9,7 @@ from PyQt5 import QtGui,QtCore,QtWidgets from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt, QVariant, QSize from PyQt5.QtWidgets import QItemDelegate, QHBoxLayout, QWidget, QMessageBox, QComboBox, QStyleOptionViewItem +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate from sympy import N @@ -204,12 +205,12 @@ class VarTableModel(QAbstractTableModel): -class VarButtonDelegate(QItemDelegate): +class ModbusButtonDelegate(BaseButtonDelegate): """该类用于向单元格中添加按钮 任务表格""" trendWidgetDict = {} def __init__(self, parent=None): - super(VarButtonDelegate, self).__init__(parent) + super(ModbusButtonDelegate, self).__init__(parent) def trendWidget(self, varName): if varName not in self.trendWidgetDict: @@ -310,8 +311,16 @@ class VarButtonDelegate(QItemDelegate): def start_action(self): sender = self.sender() model = self.parent().model - varMes = model.datas[sender.index[0]] - 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]) + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + # 获取强制值(从视图)和其他数据(从源模型) + value = str(self._get_view_data(view_row, 1)) # 强制值列 + varMes = model.datas[source_row] + name, des, varType, slaveID, address, min, max, order = 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)): reply = QMessageBox.question(self.parent(), @@ -363,22 +372,27 @@ class VarButtonDelegate(QItemDelegate): sender = self.sender() model = self.parent().model modbusType = self.parent().modbusType - fucationCbRow = str('cb' + str(sender.index[0]) + str(5)) - fucationIndex = self.parent().model.index(sender.index[0], 5) + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + fucationCbRow = str('cb' + str(source_row) + str(5)) + fucationIndex = self.parent().model.index(source_row, 5) fucationDelegate = self.parent().itemDelegate(fucationIndex) fucationCheckbox = getattr(fucationDelegate, fucationCbRow) if sender.isEdit: sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) sender.isEdit = False - sender.oldName = model.datas[sender.index[0]][3] - model.editableList.append(sender.index[0]) + sender.oldName = model.datas[source_row][3] + model.editableList.append(source_row) fucationCheckbox.setEnabled(True) self.parent().viewport().update() - else: - varMes = model.datas[sender.index[0]] + varMes = model.datas[source_row] name, des, varType, slaveID, address, min, max, varModel, order = str(varMes[3]), str(varMes[4]), str(varMes[5]), str(varMes[6]), str(varMes[7]), str(varMes[8]), str(varMes[9]), str(varMes[-2]), str(varMes[-1]) if varType == '': varType = 0 @@ -419,36 +433,47 @@ class VarButtonDelegate(QItemDelegate): sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) - rowIndex = sender.index[0] varMes = ModbusVarManage.getByName(name, modbusType) varMes.append('本地值') varMes.append('int') varMes.insert(1, '') varMes.insert(2, '') - model.insert_data(varMes, rowIndex) - model.remove_row(rowIndex + 1) + model.insert_data(varMes, source_row) + model.remove_row(source_row + 1) sender.isEdit = True fucationCheckbox.setEnabled(False) # varModelCheckbox.setEnabled(False) - model.editableList.remove(sender.index[0]) + model.editableList.remove(source_row) self.parent().viewport().update() def delete_action(self): sender = self.sender() model = self.parent().model - name = str(model.datas[sender.index[0]][3]) + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + name = str(model.datas[source_row][3]) modbusType = self.parent().modbusType if sender.editButton.isEdit: ModbusVarManage.deleteVar(name = name, modbusType = modbusType) - model.remove_row(sender.index[0]) + model.remove_row(source_row) def trend_action(self): model = self.parent().model if model.table.parent._isPopenOpen: sender = self.sender() - name = str(model.datas[sender.index[0]][3]) + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + name = str(model.datas[source_row][3]) trend = self.trendWidget(varName = name) trend.show() diff --git a/UI/VarManages/RpcVarModel.py b/UI/VarManages/RpcVarModel.py index 5fe93f3..e239017 100644 --- a/UI/VarManages/RpcVarModel.py +++ b/UI/VarManages/RpcVarModel.py @@ -89,8 +89,9 @@ class RpcVarModel(QAbstractTableModel): return Qt.ItemIsEnabled from PyQt5.QtWidgets import QStyledItemDelegate, QPushButton, QStyleOptionButton, QStyle, QApplication +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate -class RpcVarButtonDelegate(QStyledItemDelegate): +class RpcVarButtonDelegate(BaseButtonDelegate): def __init__(self, parent=None): super(RpcVarButtonDelegate, self).__init__(parent) @@ -116,11 +117,16 @@ class RpcVarButtonDelegate(QStyledItemDelegate): def force_action(self, sender): model = self.parent().model - row = sender.index[0] - varName = model.datas[row][2] - minValue = model.datas[row][4] - maxValue = model.datas[row][5] - forceValue = model.inputValues.get(row, '') + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + varName = model.datas[source_row][2] + minValue = model.datas[source_row][4] + maxValue = model.datas[source_row][5] + forceValue = model.inputValues.get(source_row, '') if forceValue == '': QMessageBox.warning(self.parent(), '提示', '请先输入强制值') return diff --git a/UI/VarManages/TCRTDModel.py b/UI/VarManages/TCRTDModel.py index 55b5137..68cf10b 100644 --- a/UI/VarManages/TCRTDModel.py +++ b/UI/VarManages/TCRTDModel.py @@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QMessageBox, QComboBox from protocol.TCP.TemToMv import temToMv from model.ProjectModel.VarManage import * from UI.VarManages.ModbusModel import * +from UI.VarManages.BaseButtonDelegate import BaseButtonDelegate from UI.TrendManage.ActualTrendWidget import ActualTrend from utils import Globals import re @@ -105,7 +106,7 @@ class TcRtdModel(VarTableModel): -class TcRtdButtonDelegate(VarButtonDelegate): +class TcRtdButtonDelegate(BaseButtonDelegate): """该类用于向单元格中添加按钮 任务表格""" def __init__(self, parent=None): @@ -173,12 +174,20 @@ class TcRtdButtonDelegate(VarButtonDelegate): def start_action(self): sender = self.sender() model = self.parent().model - value = model.datas[sender.index[0]][1] - varType = model.datas[sender.index[0]][6] - min = model.datas[sender.index[0]][7] - max = model.datas[sender.index[0]][8] - compensation = model.datas[sender.index[0]][9] + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + # 获取强制值(从视图)和其他数据(从源模型) + value = self._get_view_data(view_row, 1) # 强制值列 + varType = model.datas[source_row][6] + min = model.datas[source_row][7] + max = model.datas[source_row][8] + compensation = model.datas[source_row][9] pattern = re.compile(r'[^0-9\.-]+') + print(model.datas[source_row][3]) if not value or re.findall(pattern, str(value) + str(compensation)): reply = QMessageBox.question(self.parent(), '警告', @@ -216,13 +225,13 @@ class TcRtdButtonDelegate(VarButtonDelegate): # 直接写入变量值 res = Globals.getValue('protocolManage').writeVariableValue( - model.datas[sender.index[0]][3], + model.datas[source_row][3], float(value) ) if res: forceVars = Globals.getValue('forceVars') - forceVars.add(model.datas[sender.index[0]][3]) + forceVars.add(model.datas[source_row][3]) Globals.setValue('forceVars', forceVars) else: QMessageBox.information(self.parent(), '提示', '写入失败', QMessageBox.Yes) @@ -230,13 +239,19 @@ class TcRtdButtonDelegate(VarButtonDelegate): def edit_action(self): sender = self.sender() model = self.parent().model + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + if sender.isEdit: sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) sender.isEdit = False - sender.oldName = model.datas[sender.index[0]][3] - model.editableList.append(sender.index[0]) + sender.oldName = model.datas[source_row][3] + model.editableList.append(source_row) else: - varMes = model.datas[sender.index[0]] + varMes = model.datas[source_row] name, channelNumber, des, varType, min, max, compensationVar = str(varMes[3]), str(varMes[4]), str(varMes[5]),str(varMes[6]), str(varMes[7]), str(varMes[8]), str(varMes[9]) if not name or not varType: reply = QMessageBox.question(self.parent(), @@ -245,7 +260,7 @@ class TcRtdButtonDelegate(VarButtonDelegate): QMessageBox.Yes) return sender.isEdit = True - model.editableList.remove(sender.index[0]) + model.editableList.remove(source_row) if sender.oldName: if sender.oldName == name: TcRtdManage.editVar(name=sender.oldName, Nname=name, channelNumber = channelNumber, des=des, @@ -267,18 +282,23 @@ class TcRtdButtonDelegate(VarButtonDelegate): min=min, max=max, compensationVar = compensationVar) sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) - rowIndex = sender.index[0] varMes = TcRtdManage.getByName(name) varMes.insert(1, '') varMes.insert(2, '') varMes.append('') - model.insert_data(varMes, rowIndex) - model.remove_row(rowIndex + 1) + model.insert_data(varMes, source_row) + model.remove_row(source_row + 1) def trend_action(self): sender = self.sender() model = self.parent().model - varName = model.datas[sender.index[0]][3] + + # 使用基类的验证方法获取行索引 + view_row, source_row = self._validate_button_indices(sender) + if view_row is None: + return + + varName = model.datas[source_row][3] # 以变量名为key,避免重复打开 if varName not in self.trendWindows or self.trendWindows[varName] is None: self.trendWindows[varName] = ActualTrend(varName=varName) diff --git a/UI/VarManages/VarTable.py b/UI/VarManages/VarTable.py index 20e511e..e68de78 100644 --- a/UI/VarManages/VarTable.py +++ b/UI/VarManages/VarTable.py @@ -80,7 +80,7 @@ class VarTableView(QTableView): def setHeader(self): - self.setItemDelegateForColumn(11, VarButtonDelegate(self)) + self.setItemDelegateForColumn(11, ModbusButtonDelegate(self)) self.setItemDelegateForColumn(5, ModbusTypeBox(self)) self.setItemDelegateForColumn(10, ModbusVarModelBox(self)) self.model = VarTableModel(['ID', '强制值', '当前值', '变量名', '变量描述', '变量类型', diff --git a/UI/VarManages/VarWidget.py b/UI/VarManages/VarWidget.py index 757e242..3d160f2 100644 --- a/UI/VarManages/VarWidget.py +++ b/UI/VarManages/VarWidget.py @@ -71,7 +71,7 @@ class VarWidgets(QtWidgets.QWidget): self.timer = QTimer(self) # 将定时器超时信号与槽函数showTime()连接 self.timer.timeout.connect(self.proxy.invalidate) - self.timer.start(50) # 启动timer + self.timer.start(500) # 启动timer Globals.setValue(str(self.modbusType) + 'Table', self.varView) diff --git a/protocol/ModBus/ModbusManager.py b/protocol/ModBus/ModbusManager.py index 1dc8e15..9df76a8 100644 --- a/protocol/ModBus/ModbusManager.py +++ b/protocol/ModBus/ModbusManager.py @@ -51,8 +51,8 @@ class ModbusManager: """启动 Modbus TCP 主站""" try: if self.modbusTcpMaster is not None: - return True - + self.modbusTcpMaster.master.close() + self.modbusTcpMaster = None # 从数据库获取TCP设置 tcpSettings = self._getTcpSettings('master') if not tcpSettings: @@ -93,8 +93,8 @@ class ModbusManager: """启动 Modbus RTU 主站""" try: if self.modbusRtuMaster is not None: - return True - + self.modbusRtuMaster.master.close() + self.modbusRtuMaster = None # 从数据库获取RTU设置 rtuSettings = self._getRtuSettings('master') if not rtuSettings: @@ -136,8 +136,9 @@ class ModbusManager: """启动 Modbus TCP 从站""" try: if self.modbusTcpSlave is not None: - return True - + self.modbusTcpSlave.server.close() + self.modbusTcpSlave = None + # 从数据库获取TCP设置 tcpSettings = self._getTcpSettings('slave') if not tcpSettings: @@ -150,7 +151,7 @@ class ModbusManager: ) # 添加默认从站ID - self.modbusTcpSlave.addSlave(1) + # self.modbusTcpSlave.addSlave(1) return True @@ -177,10 +178,12 @@ class ModbusManager: """启动 Modbus RTU 从站""" try: if self.modbusRtuSlave is not None: - return True + self.modbusRtuSlave.server.close() + self.modbusRtuSlave = None # 从数据库获取RTU设置 rtuSettings = self._getRtuSettings('slave') + if not rtuSettings: return False @@ -197,7 +200,7 @@ class ModbusManager: self.modbusRtuSlave.start() # 添加默认从站ID - self.modbusRtuSlave.addSlave(1) + # self.modbusRtuSlave.addSlave(1) return True @@ -309,40 +312,7 @@ class ModbusManager: except Exception as e: print(f"写入RTU从站变量失败: {str(e)}") return False - - def readModbusTcpMasterValue(self, info): - """读取TCP主站变量值""" - try: - if self.modbusTcpMaster is None: - return None - - slaveId = int(info['slaveID']) - address = int(info['address']) - varType = int(info['varType']) - order = info['order'] - - return self._readModbusValue(self.modbusTcpMaster, slaveId, address, varType, order) - - except Exception as e: - print(f"读取TCP主站变量失败: {str(e)}") - return None - - def readModbusRtuMasterValue(self, info): - """读取RTU主站变量值""" - try: - if self.modbusRtuMaster is None: - return None - - slaveId = int(info['slaveID']) - address = int(info['address']) - varType = int(info['varType']) - order = info['order'] - - return self._readModbusValue(self.modbusRtuMaster, slaveId, address, varType, order) - - except Exception as e: - print(f"读取RTU主站变量失败: {str(e)}") - return None + def readModbusTcpSlaveValue(self, info): """读取TCP从站变量值""" @@ -509,6 +479,7 @@ class ModbusManager: if value != 'error' and self.variableValueCache is not None and self.cacheLock is not None: with self.cacheLock: self.variableValueCache[varName] = value + print(varName, value) except Exception as e: print(f"读取TCP主站变量失败 {varName}: {str(e)}") diff --git a/protocol/ModBus/rtuslave_example.py b/protocol/ModBus/rtuslave_example.py index 9dced66..fd066e7 100644 --- a/protocol/ModBus/rtuslave_example.py +++ b/protocol/ModBus/rtuslave_example.py @@ -69,7 +69,11 @@ class RTUSlave(): def readValue(self, slaveId, name, address, order = 'int'): try: - slave = self.server.get_slave(slaveId) + try: + slave = self.server.get_slave(slaveId) + except Exception as e: + if type(e) == MissingKeyError: + self.addSlave(slaveId) if order == 'int': value = slave.get_values(name, address, 1)[0] else: @@ -86,7 +90,6 @@ class RTUSlave(): value = ABCDToFloat(value) return value except Exception as e: - print(e) return 'error' diff --git a/protocol/ModBus/tcpslave_example.py b/protocol/ModBus/tcpslave_example.py index 9fff73f..6e101be 100644 --- a/protocol/ModBus/tcpslave_example.py +++ b/protocol/ModBus/tcpslave_example.py @@ -2,6 +2,7 @@ import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_tcp, hooks +from modbus_tk.exceptions import * import serial import struct from .ByteOrder import * @@ -80,7 +81,11 @@ class TCPSlave(): def readValue(self, slaveId, name, address, order = 'int'): try: - slave = self.server.get_slave(slaveId) + try: + slave = self.server.get_slave(slaveId) + except Exception as e: + if type(e) == MissingKeyError: + self.addSlave(slaveId) if order == 'int': value = slave.get_values(name, address, 1)[0] else: @@ -95,6 +100,7 @@ class TCPSlave(): value = CDABToFloat(value) return value except Exception as e: + return 'error' diff --git a/protocol/ProtocolManage.py b/protocol/ProtocolManage.py index d16470d..2055306 100644 --- a/protocol/ProtocolManage.py +++ b/protocol/ProtocolManage.py @@ -35,7 +35,7 @@ class ProtocolManage(object): # Modbus 管理器 self.modbusManager = ModbusManager() - self.modbusManager.setVariableCache(self.variableValueCache, None, self.varInfoCache) + # self.modbusManager.setVariableCache(self.variableValueCache, None, self.varInfoCache) self.refreshVarCache() self.cacheLock = threading.Lock() @@ -238,12 +238,12 @@ class ProtocolManage(object): info = varInfo['variableData'] try: # 拆分为独立的协议条件判断 - if modelType == 'ModbusTcpMasterVar': - value = self.modbusManager.readModbusTcpMasterValue(info) - elif modelType == 'ModbusTcpSlaveVar': + # if modelType == 'ModbusTcpMasterVar': + # value = self.modbusManager.readModbusTcpMasterValue(info) + if modelType == 'ModbusTcpSlaveVar': value = self.modbusManager.readModbusTcpSlaveValue(info) - elif modelType == 'ModbusRtuMasterVar': - value = self.modbusManager.readModbusRtuMasterValue(info) + # elif modelType == 'ModbusRtuMasterVar': + # value = self.modbusManager.readModbusRtuMasterValue(info) elif modelType == 'ModbusRtuSlaveVar': value = self.modbusManager.readModbusRtuSlaveValue(info) elif modelType == 'HartVar':