You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
26 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from ast import Mod
from math import comb
import typing
import sys
from numpy import var
import qtawesome
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
# 移除 Celery 相关导入,现在使用 ProtocolManage 和 ModbusManager
from ..TrendManage.ActualTrendWidget import ActualTrend
from model.ProjectModel.VarManage import *
from utils import Globals
import re
sys.path.append('protocol/Celery/MBTCPMaster')
class QPushButton(QtWidgets.QPushButton):
def __init__(self, *__args: any,clicked = None):
super(QPushButton,self).__init__(*__args)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(25, 25))
class VarTableModel(QAbstractTableModel):
''' 变量表模型类'''
def __init__(self, header, data: list, modbusType = None, table = None):
'''
header : 表头变量
data : 表格内容
table : 缺省参数
'''
QAbstractTableModel.__init__(self, parent=None)
self.header = header
self.datas = data
self.checkList = ['Unchecked'] * len(self.datas)
self.supportedDragActions()
self.table = table
self.editableList = [] # 表格中可编辑项
self.modbusType = modbusType
def initTable(self):
self.datas = []
self.editableList = []
if self.table:
self.table.parent.initIcon()
varDatas = ModbusVarManage.getAllVar(self.modbusType)
if not varDatas:
# self.layoutChanged.emit()
self.table.proxy.invalidate()
return
for x in varDatas:
x.insert(1, '')
x.insert(2, '')
self.datas.append(x)
self.checkList = ['Unchecked'] * len(self.datas)
# self.layoutChanged.emit()
self.table.proxy.invalidate()
self.refreshComboBox()
def append_data(self, x):
self.datas.append(x)
self.checkList = ['Unchecked'] * len(self.datas)
self.table.proxy.invalidate()
# self.layoutChanged.emit()
def insert_data(self, x, index):
self.datas.insert(index, x)
self.checkList = ['Unchecked'] * len(self.datas)
self.table.proxy.invalidate()
def remove_row(self, row):
self.datas.pop(row)
self.checkList = ['UnChecked'] * len(self.datas)
self.table.proxy.invalidate()
def rowCount(self, parent: QModelIndex = ...) -> int:
if len(self.datas) > 0:
return len(self.datas)
return 0
def columnCount(self, parent: QModelIndex = ...) -> int:
return len(self.header)
def data(self, QModelIndex, role=None):
# print(Qt.__dict__.items())
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter
if not QModelIndex.isValid():
print("行或者列有问题")
return QVariant()
if role == Qt.BackgroundColorRole:
if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'):
return QtGui.QColor('#EFEFEF')
if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'):
return QtGui.QColor('#00FF7F')
if role == Qt.TextColorRole:
return QtGui.QColor('#1A1A1A')
if role == Qt.CheckStateRole:
if QModelIndex.column() == 0:
return Qt.Checked if self.checkList[QModelIndex.row()] == 'Checked' else Qt.Unchecked
else:
return None
if role == Qt.ToolTipRole:
if QModelIndex.column() == 0:
return self.checkList[QModelIndex.row()]
if role == Qt.DisplayRole or role == Qt.EditRole:
if QModelIndex.row() in self.editableList:
return self.datas[QModelIndex.row()][QModelIndex.column()]
if role != Qt.DisplayRole:
return QVariant()
# 获取变量值并插入表格
if QModelIndex.column() == 2:
varName = self.datas[QModelIndex.row()][3]
if varName != '':
try:
protocolManage = Globals.getValue('protocolManage')
if protocolManage:
result = protocolManage.readVariableValue(varName)
if result is not None:
self.datas[QModelIndex.row()][QModelIndex.column()] = result
except Exception as e:
pass
return QVariant(self.datas[QModelIndex.row()][QModelIndex.column()])
def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any: # 表头
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return self.header[section]
def setData(self, index, value, role):
row = index.row()
col = index.column()
if role == Qt.CheckStateRole and col == 0:
self.checkList[row] = 'Checked' if value == Qt.Checked else 'Unchecked'
return True
if role == Qt.EditRole:
self.datas[row][col] = value
return True
return True
def flags(self, index):
if index.column() == 0:
return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable
if index.row() in self.editableList and index.column() or index.column() == 1:
return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsEditable
return Qt.ItemIsEnabled
def headerClick(self, isOn):
self.beginResetModel()
if isOn:
self.checkList = []
self.checkList = ['Checked'] * len(self.datas)
else:
self.checkList = []
self.checkList = ['UnChecked'] * len(self.datas)
self.endResetModel()
def dragMoveEvent(self, event):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def moveRow(self, sourceParent: QModelIndex, sourceRow: int, destinationParent: QModelIndex,
destinationChild: int) -> bool:
if self.datas[destinationChild] == self.datas[sourceRow]:
return
self.datas[sourceRow], self.datas[destinationChild] = self.datas[destinationChild], self.datas[sourceRow]
self.table.proxy.invalidate()
def refreshComboBox(self):
#功能类型的index是5通讯类型index是10
for i in range(len(self.datas)):
cbRow = str('cb' + str(i) + str(5))
index = self.index(i, int(5))
delegate = self.table.itemDelegate(index)
delegate.paint(self.table, QStyleOptionViewItem(), index)
try:
comboBox = getattr(delegate, cbRow)
# print(comboBox, i, num, cbRow)
except Exception as e:
continue
if self.datas[i][5] in [0, 1]:
comboBox.setCurrentIndex(self.datas[i][5])
elif self.datas[i][5] in [3, 4]:
comboBox.setCurrentIndex(self.datas[i][5] - 1)
else:
comboBox.setCurrentIndex(-1)
class ModbusButtonDelegate(BaseButtonDelegate):
"""该类用于向单元格中添加按钮 任务表格"""
trendWidgetDict = {}
def __init__(self, parent=None):
super(ModbusButtonDelegate, self).__init__(parent)
def trendWidget(self, varName):
if varName not in self.trendWidgetDict:
trendWidget = ActualTrend(varName = varName)
self.trendWidgetDict[varName] = trendWidget
return self.trendWidgetDict[varName]
def paint(self, painter, option, index):
if not self.parent().indexWidget(index):
button1 = QPushButton(
qtawesome.icon('fa.play', color='#1fbb6f'),
"",
self.parent(),
clicked=self.start_action
)
# button1.setIconSize(QSize(15, 15))
# button1.setStyleSheet("border:none;")
button2 = QPushButton(
qtawesome.icon('fa.pencil', color='#4c8cf2'),
"",
self.parent(),
clicked=self.edit_action
)
# button2.setStyleSheet("border:none;")
button3 = QPushButton(
qtawesome.icon('fa.trash', color='#ff6d6d'),
"",
self.parent(),
clicked=self.delete_action
)
# button3.setStyleSheet("border:none;")
button4 = QPushButton(
qtawesome.icon('fa.line-chart', color='#393c4e'),
"",
self.parent(),
clicked=self.trend_action
)
comboBox = QComboBox(self.parent())
comboBox.addItem('int', 0)
comboBox.addItem('ABCD', 1)
comboBox.addItem('CDAB', 2)
comboBox.addItem('BADC', 3)
comboBox.addItem('DCBA', 4)
comboBox.setCurrentText(str(self.parent().model.datas[index.row()][index.column()]))
# comboBox.setMinimumWidth(30)
comboBox.currentIndexChanged.connect(self.indexChange)
button1.clicked.connect(self.start_action)
button2.clicked.connect(self.edit_action)
button3.clicked.connect(self.delete_action)
button4.clicked.connect(self.trend_action)
button2.oldName = False
button2.isEdit = True
button3.editButton = button2
# button4.setStyleSheet("border:none;")
button1.index = [index.row(), index.column()]
button2.index = [index.row(), index.column()]
button3.index = [index.row(), index.column()]
button4.index = [index.row(), index.column()]
comboBox.index = [index.row(), index.column()]
data = self.parent().model.datas[index.row()]
for x in data[:-3]:
if x != '':
break
else:
button2.isEdit = False
button2.setIcon(qtawesome.icon('fa.save', color='#1fbb6f'))
self.parent().model.editableList.append(button2.index[0])
hboxLayout = QHBoxLayout()
hboxLayout.addWidget(comboBox)
hboxLayout.addWidget(button1)
hboxLayout.addWidget(button2)
hboxLayout.addWidget(button3)
hboxLayout.addWidget(button4)
hboxLayout.setContentsMargins(2, 0, 0, 2)
hboxLayout.setAlignment(Qt.AlignCenter)
widget = QWidget()
widget.setLayout(hboxLayout)
self.parent().setIndexWidget(
index,
widget
)
def indexChange(self):
sender = self.sender()
modbusType = self.parent().modbusType
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][3]
ModbusVarManage.editOrder(name, text, modbusType)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
def start_action(self):
sender = self.sender()
model = self.parent().model
# 使用基类的验证方法获取行索引
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(),
'警告',
"请输入强制值或数字",
QMessageBox.Yes)
return
if min and max:
if float(value) < float(min) or float(value) > float(max):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
elif min and not max:
if float(value) < float(min):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
elif max and not min:
if float(value) > float(max):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
# 使用新的 ProtocolManage 进行变量写入
protocolManage = Globals.getValue('protocolManage')
if protocolManage:
try:
result = protocolManage.writeVariableValue(name, value)
if result:
forceVars = Globals.getValue('forceVars')
if forceVars is None:
forceVars = set()
forceVars.add(name)
Globals.setValue('forceVars', forceVars)
else:
QMessageBox.warning(self.parent(), '错误', f'变量 {name} 写入失败', QMessageBox.Yes)
except Exception as e:
QMessageBox.warning(self.parent(), '错误', f'写入变量时发生错误: {str(e)}', QMessageBox.Yes)
else:
QMessageBox.warning(self.parent(), '错误', '协议管理器未初始化', QMessageBox.Yes)
def edit_action(self):
sender = self.sender()
model = self.parent().model
modbusType = self.parent().modbusType
# 使用基类的验证方法获取行索引
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[source_row][3]
model.editableList.append(source_row)
fucationCheckbox.setEnabled(True)
self.parent().viewport().update()
else:
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
if not name or not slaveID or not address:
reply = QMessageBox.question(self.parent(),
'警告',
"有字段为空或输入错误",
QMessageBox.Yes)
return
if sender.oldName and ModbusVarManage.getByName(sender.oldName, modbusType):
if sender.oldName == name:
# 名称未改变,直接执行修改变量操作
ModbusVarManage.editVar(name=sender.oldName, Nname=name, des=des,
varType=varType, slaveID=slaveID, address=address,
min=min, max=max, order=order, modbusType=modbusType, varModel=varModel)
else:
# 名称改变,检查新名称是否已存在
if GlobalVarManager.isVarNameExist(name):
QMessageBox.information(self.parent(), '提示', '已有同名变量')
return
else:
# 执行修改变量操作
ModbusVarManage.editVar(name=sender.oldName, Nname=name, des=des,
varType=varType, slaveID=slaveID, address=address,
min=min, max=max, order=order, modbusType=modbusType, varModel=varModel)
else:
if GlobalVarManager.isVarNameExist(name):
QMessageBox.information(self.parent(), '提示', '已有同名变量')
return
else:
ModbusVarManage.createVar(varName = name, varType = varType, des = des,
address = address, slaveID = slaveID, min = min,
max = max, order = order, modbusType = modbusType, varModel= varModel)
sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2'))
varMes = ModbusVarManage.getByName(name, modbusType)
varMes.append('本地值')
varMes.append('int')
varMes.insert(1, '')
varMes.insert(2, '')
model.insert_data(varMes, source_row)
model.remove_row(source_row + 1)
sender.isEdit = True
fucationCheckbox.setEnabled(False)
# varModelCheckbox.setEnabled(False)
model.editableList.remove(source_row)
self.parent().viewport().update()
def delete_action(self):
sender = self.sender()
model = self.parent().model
# 使用基类的验证方法获取行索引
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(source_row)
def trend_action(self):
model = self.parent().model
if model.table.parent._isPopenOpen:
sender = self.sender()
# 使用基类的验证方法获取行索引
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()
class ModbusTypeBox(QItemDelegate):
def __init__(self, parent=None):
super(ModbusTypeBox, self).__init__(parent)
def paint(self, painter, option, index):
if (index.column() == 5) and index.row() not in self.parent().model.editableList:
data = self.parent().model.datas[index.row()]
comBox = str('cb' + str(index.row()) + str(index.column()))
setattr(self, comBox, QComboBox())
comboBox = getattr(self, comBox)
items = ['Coil Status', 'Input Status', 'Input Register', 'Holding Register']
comboBox.addItems(items)
if self.parent().model.datas[index.row()][index.column()] in [0, 1]:
comboBox.setCurrentIndex(self.parent().model.datas[index.row()][index.column()])
elif self.parent().model.datas[index.row()][index.column()] in [3, 4]:
comboBox.setCurrentIndex(self.parent().model.datas[index.row()][index.column()] - 1)
else:
comboBox.setCurrentIndex(0)
comboBox.currentIndexChanged.connect( self.indexChange)
comboBox.setObjectName('ModbusTypeBox')
comboBox.setEditable(True)
comboBox.lineEdit().setAlignment(Qt.AlignCenter)
# comboBox.setMinimumWidth(200)
# comboBox.setEnabled(False)
hboxLayout = QHBoxLayout()
hboxLayout.addWidget(comboBox)
hboxLayout.setContentsMargins(0, 0, 0, 0)
# hboxLayout.setAlignment(Qt.AlignCenter)
comboBox.index = [index.row(), index.column()]
row = index.row()
if row % 2 == 0:
comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF; height: 40px; }")
else:
comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3; height: 40px; }")
if str(data[index.column()]):
comboBox.setEnabled(False)
else:
comboBox.setEnabled(True)
widget = QWidget()
widget.setLayout(hboxLayout)
self.parent().setIndexWidget(
index,
widget
)
self.parent().openPersistentEditor(index)
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
if index in [2, 3]:
index += 1
self.parent().model.datas[sender.index[0]][sender.index[1]] = index
class ModbusVarModelBox(QItemDelegate):
def __init__(self, parent=None, comBoxColumn = 10):
super(ModbusVarModelBox, self).__init__(parent)
self.comBoxColumn = comBoxColumn
def paint(self, painter, option, index):
#本地值、模拟值、远程值 对应 0, 1, 2
if (index.column() == self.comBoxColumn) and not self.parent().indexWidget(index):
data = self.parent().model.datas[index.row()]
comBox = str('cb' + str(index.row()) + str(index.column()))
setattr(self, comBox, QComboBox())
comboBox = getattr(self, comBox)
comboBox = QComboBox(self.parent())
items = ['本地值', '模拟值', '远程值']
comboBox.addItems(items)
comboBox.setCurrentText(str(self.parent().model.datas[index.row()][index.column()]))
comboBox.currentIndexChanged.connect( self.indexChange)
comboBox.setObjectName('ModbusTypeBox')
comboBox.setEditable(True)
comboBox.lineEdit().setAlignment(Qt.AlignCenter)
hboxLayout = QHBoxLayout()
hboxLayout.addWidget(comboBox)
hboxLayout.setContentsMargins(0, 0, 0, 0)
comboBox.index = [index.row(), index.column()]
row = index.row()
if row % 2 == 0:
comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF; height: 40px; }")
else:
comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3; height: 40px; }")
widget = QWidget()
widget.setLayout(hboxLayout)
self.parent().setIndexWidget(
index,
widget
)
self.parent().openPersistentEditor(index)
def indexChange(self):
sender = self.sender()
modbusType = self.parent().modbusType
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][3]
ModbusVarManage.editVarModel(name, text, modbusType)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
class HartVarModelBox(ModbusVarModelBox):
def __init__(self, parent=None, comBoxColumn = 8):
super(HartVarModelBox, self).__init__(parent)
self.comBoxColumn = comBoxColumn
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][1]
HartVarManage.editVarModel(name, text)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
class TcRtdVarModelBox(ModbusVarModelBox):
def __init__(self, parent=None, comBoxColumn = 10):
super(TcRtdVarModelBox, self).__init__(parent)
self.comBoxColumn = comBoxColumn
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][3]
TcRtdManage.editVarModel(name, text)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
class AnalogVarModelBox(ModbusVarModelBox):
def __init__(self, parent=None, comBoxColumn = 9):
super(AnalogVarModelBox, self).__init__(parent)
self.comBoxColumn = comBoxColumn
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][3]
AnalogManage.editVarModel(name, text, )
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
class HartSimulateVarModelBox(ModbusVarModelBox):
def __init__(self, parent=None, comBoxColumn = 9):
super(HartSimulateVarModelBox, self).__init__(parent)
self.comBoxColumn = comBoxColumn
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][1]
HartSimulateVarManage.editVarModel(name, text)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text