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.

447 lines
18 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 csv import excel
import sys
import pandas as pd
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.Qt import *
from PyQt5.QtWidgets import QApplication, QMainWindow, QStackedWidget, QMessageBox, QStackedWidget, QWidget, QTabWidget, QFileDialog, QPushButton, QGraphicsDropShadowEffect
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPainter, QBrush, QColor
from PyQt5.QtCore import QEvent
from ctypes import POINTER, cast
from ctypes.wintypes import MSG
from win32 import win32api, win32gui
from win32.lib import win32con
from model.ProjectModel.VarManage import GlobalVarManager
from UI.Main.MainLeft import MainLeft
from UI.Main.MainTop import MainTop
from ..ProjectManages.ProjectWidget import ProjectWidgets
from ..VarManages.VarWidget import VarWidgets, HartWidgets, TcRtdWidgets, AnalogWidgets, HartSimulateWidgets
from UI.VarManages.VarTable import RpcVarTableView
from ..UserManage.UserWidget import UserWidgets
from ..TrendManage.TrendWidget import TrendWidgets
from ..Setting.Setting import SettingWidget
from model.ClientModel.Client import Client
from utils import Globals
from utils.DBModels.InitParameterDB import InitParameterDB
from UI.ProfibusWidgets.ProfibusWindow import ProfibusWidgets
from UI.ProcedureManager.ProcedureManager import ProcedureManager
from UI.ControlManage.ControlWidget import getControlWidget
class CommonHelper:
def __init__(self):
pass
@staticmethod
def readQss(style):
with open(style, "r", encoding='utf-8') as f:
return f.read()
class MainWindow(QMainWindow):
BORDER_WIDTH = 5 #设圆角
def __init__(self):
super(MainWindow, self).__init__()
InitParameterDB()
self.setupUi()
# self.setStyleSheet(CommonHelper.readQss('Static/main.qss'))
self.setWindowFlags(Qt.FramelessWindowHint|Qt.WindowSystemMenuHint)
self.SHADOW_WIDTH = 0 #边框距离
self.isLeftPressDown = False #鼠标左键是否按下
self.dragPosition = 0 #拖动时坐标
self.padding = 4
self.Numbers = self.enum(UP=0, DOWN=1, LEFT=2, RIGHT=3, LEFTTOP=4, LEFTBOTTOM=5, RIGHTBOTTOM=6, RIGHTTOP=7, NONE=8) #枚举参数
self.setMinimumHeight(900) #窗体最小高度
self.setMinimumWidth(1600) #窗体最小宽度
self.dir = self.Numbers.NONE #初始鼠标状态
#开启鼠标追踪
self.setMouseTracking(True)
self.centralwidget.setMouseTracking(True)
self.leftWidget.setMouseTracking(True)
self.rightWidget.setMouseTracking(True)
self.topWidget.setMouseTracking(True)
#初始化接口
# self.windowEffect = WindowEffect()
# # 添加DWM阴影效果
# self.windowEffect.addWindowAnimation(self.winId())
# self.windowEffect.addShadowEffect(self.winId())
# 解决报错
self.windowHandle().screenChanged.connect(self.__onScreenChanged)
def setupUi(self):
self.showMaximized()
self.centralwidget = QtWidgets.QWidget(self)
self.centralwidget.setObjectName("centralwidget")
self.leftWidget = MainLeft()
self.leftWidget.setObjectName("leftWidget")
self.projectWidget = ProjectWidgets()
self.userWidget = UserWidgets()
self.procedureManagerWidget = ProcedureManager()
self.controlWidget = getControlWidget()
self.ModbusTcpMasterWidget = VarWidgets('ModbusTcpMaster')
self.ModbusTcpSlaveWidget = VarWidgets('ModbusTcpSlave')
self.ModbusRtuMasterWidget = VarWidgets('ModbusRtuMaster')
self.ModbusRtuSlaveWidget = VarWidgets('ModbusRtuSlave')
modbusWidgetList = [self.ModbusTcpMasterWidget, self.ModbusTcpSlaveWidget, self.ModbusRtuMasterWidget, self.ModbusRtuSlaveWidget]
modbusNameList = ['MODBUSTCP主站', 'MODBUSTCP从站', 'MODBUSRTU主站', 'MODBUSRTU从站',]
self.profibusWidget = ProfibusWidgets()
self.trendWidget = TrendWidgets()
self.SettingWidget = SettingWidget()
self.hartWidget = HartWidgets()
self.tcrtdWidget = TcRtdWidgets()
self.analogWidget = AnalogWidgets()
self.hartsimulateWidget = HartSimulateWidgets()
self.rpcVarTableWidget = RpcVarTableView()
self.userWidget.setObjectName('userWidget')
self.projectWidget.setObjectName('projectWidget')
self.trendWidget.setObjectName('trendWidget')
# self.ModBusWidget.setObjectName('varWidget')
self.analogWidget.setObjectName('analogWidget')
self.hartsimulateWidget.setObjectName('hartsimulateWidget')
self.varManageTabWidget = QTabWidget()
self.varManageTabWidget.setObjectName("varManageTabWidget")
self.varManageTabWidget.tabBar().setObjectName('varManageTabBar')
self.rightWidget = QStackedWidget()
self.rightWidget.setObjectName("rightWidget")
self.rightWidget.addWidget(self.projectWidget)
for widget, name in zip(modbusWidgetList, modbusNameList):
widget.setObjectName('varWidget')
self.varManageTabWidget.addTab(widget, name)
self.varManageTabWidget.addTab(self.analogWidget,'IO')
self.varManageTabWidget.addTab(self.tcrtdWidget,'TCRTD')
self.varManageTabWidget.addTab(self.hartWidget,'HART读取')
self.varManageTabWidget.addTab(self.hartsimulateWidget,'HART模拟')
self.varManageTabWidget.addTab(self.profibusWidget,'PROFIBUS')
self.varManageTabWidget.addTab(self.rpcVarTableWidget, '远程通讯')
#添加导入变量按钮
self.importVarButton = QPushButton(QIcon('./Static/import.png'), '导入变量')
self.importVarButton.setObjectName('importBtn')
self.importVarButton.setIconSize(QSize(22, 22))
self.importVarButton.setFlat(True)
self.importVarButton.clicked.connect(self.loadVar)
self.varManageTabWidget.setCornerWidget(self.importVarButton)
self.rightWidget.addWidget(self.varManageTabWidget)
self.rightWidget.addWidget(self.trendWidget)
self.rightWidget.addWidget(self.userWidget)
self.rightWidget.addWidget(self.SettingWidget)
self.rightWidget.addWidget(self.procedureManagerWidget)
self.rightWidget.addWidget(self.controlWidget)
self.rightWidget.widget(1)
self.leftWidget.createProject.clicked.connect(lambda: self.exButtonClicked(0))
self.leftWidget.varMag.clicked.connect(self.varShow)
self.leftWidget.trendMag.clicked.connect(self.reFreshTrendWidget)
self.leftWidget.userMag.clicked.connect(lambda: self.exButtonClicked(3))
self.leftWidget.protocolMag.clicked.connect(self.showSetting)
self.leftWidget.procedureMag.clicked.connect(lambda: self.initProcedureDB())
self.leftWidget.controlMag.clicked.connect(lambda: self.showControlSystem())
self.setCentralWidget(self.centralwidget)
self.setWindowOpacity(0.995) # 设置窗口透明度
self.topWidget = MainTop(self)
self.topWidget.setObjectName('topWidget')
# 初始化搜索框状态在topWidget创建后
self.topWidget.updateSearchPlaceholder()
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout.addWidget(self.leftWidget)
self.horizontalLayout.addWidget(self.rightWidget)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 7)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setSpacing(5)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.addWidget(self.topWidget)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout.setStretch(0, 1)
self.verticalLayout.setStretch(1, 18)
self.verticalLayout.setSpacing(0)
# self.setAttribute(Qt.WA_TranslucentBackground) # 设置窗口背景透明
self.setWindowFlag(Qt.FramelessWindowHint)
# 添加阴影效果接近Windows原生
# 添加圆角QSS
# self.centralwidget.setStyleSheet("")
def exButtonClicked(self, index):
if Globals.getValue('currentPro') == -1 and index not in [0, 3]:
return -1
print(index, Globals.getValue('currentPro'))
self.rightWidget.setCurrentIndex(index)
def showSetting(self):
if Globals.getValue('currentPro') == -1:
return
self.SettingWidget.setupUI()
self.rightWidget.setCurrentIndex(4)
def varShow(self):
self.exButtonClicked(1)
Globals.setValue('SearchWidget', 1)
def reFreshTrendWidget(self):
if Globals.getValue('currentPro') == -1:
return
# self.trendWidget.getMems()
self.exButtonClicked(2)
def initProcedureDB(self):
if Globals.getValue('currentPro') == -1:
return
self.procedureManagerWidget.initDB()
self.procedureManagerWidget.initUI()
self.exButtonClicked(5)
def showControlSystem(self):
"""显示控制系统界面"""
if Globals.getValue('currentPro') == -1:
return
# 重新加载控制系统(确保规则正确加载)
self.controlWidget.reloadControlSystem()
self.exButtonClicked(6)
def loadVar(self):
file_path, _ = QFileDialog.getOpenFileName(self, '选择Excel文件', '', 'Excel Files (*.xlsx *.xls)')
if not file_path:
return
excelData = pd.read_excel(file_path, sheet_name=None)
allVarNames = []
for sheet_name, data in excelData.items():
if '变量名' in data.columns:
# 确保所有变量名转换为字符串
allVarNames.extend(data['变量名'].astype(str).tolist())
if len(allVarNames) != len(set(allVarNames)):
# 确保重复名称列表中的元素都是字符串
repeated_names = [str(name) for name in set(allVarNames) if allVarNames.count(name) > 1]
QMessageBox.warning(self, '警告', f'发现重复的变量名: {", ".join(repeated_names)}')
return
allErrInfo = []
for sheet_name, data in excelData.items():
if '变量名' in data.columns:
importInfo = GlobalVarManager.importVar(sheet_name, data)
allErrInfo.append(importInfo)
if allErrInfo:
# 过滤掉空字符串
allErrInfo = [info for info in allErrInfo if info]
if not allErrInfo:
QMessageBox.information(self, '导入信息', '所有变量导入成功')
else:
errorMessages = '\n'.join(allErrInfo)
QMessageBox.information(self, '导入信息', errorMessages)
def enum(self,**enums):
return type('Enum', (), enums)
def region(self,cursorGlobalPoint):
#获取窗体在屏幕上的位置区域tl为topleft点rb为rightbottom点
rect = self.rect()
tl = self.mapToGlobal(rect.topLeft())
rb = self.mapToGlobal(rect.bottomRight())
x = cursorGlobalPoint.x()
y = cursorGlobalPoint.y()
if(tl.x() + self.padding >= x and tl.x() <= x and tl.y() + self.padding >= y and tl.y() <= y):
#左上角
self.dir = self.Numbers.LEFTTOP
self.setCursor(QCursor(Qt.SizeFDiagCursor)) #设置鼠标形状
elif(x >= rb.x() - self.padding and x <= rb.x() and y >= rb.y() - self.padding and y <= rb.y()):
#右下角
self.dir = self.Numbers.RIGHTBOTTOM
self.setCursor(QCursor(Qt.SizeFDiagCursor))
elif(x <= tl.x() + self.padding and x >= tl.x() and y >= rb.y() - self.padding and y <= rb.y()):
#左下角
self.dir = self.Numbers.LEFTBOTTOM
self.setCursor(QCursor(Qt.SizeBDiagCursor))
elif(x <= rb.x() and x >= rb.x() - self.padding and y >= tl.y() and y <= tl.y() + self.padding):
#右上角
self.dir = self.Numbers.RIGHTTOP
self.setCursor(QCursor(Qt.SizeBDiagCursor))
elif(x <= tl.x() + self.padding and x >= tl.x()):
#左边
self.dir = self.Numbers.LEFT
self.setCursor(QCursor(Qt.SizeHorCursor))
elif( x <= rb.x() and x >= rb.x() - self.padding):
#右边
self.dir = self.Numbers.RIGHT
self.setCursor(QCursor(Qt.SizeHorCursor))
elif(y >= tl.y() and y <= tl.y() + self.padding):
#上边
self.dir = self.Numbers.UP
self.setCursor(QCursor(Qt.SizeVerCursor))
elif(y <= rb.y() and y >= rb.y() - self.padding):
#下边
self.dir = self.Numbers.DOWN
self.setCursor(QCursor(Qt.SizeVerCursor))
else:
#默认
self.dir = self.Numbers.NONE
self.setCursor(QCursor(Qt.ArrowCursor))
def mouseReleaseEvent(self,event):
if(event.button() == Qt.LeftButton):
self.isLeftPressDown = False
if(self.dir != self.Numbers.NONE):
self.releaseMouse()
self.setCursor(QCursor(Qt.ArrowCursor))
def mousePressEvent(self,event):
if(event.button()==Qt.LeftButton):
self.isLeftPressDown=True
if(self.dir != self.Numbers.NONE):
self.mouseGrabber()
else:
self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
def mouseMoveEvent(self,event):
gloPoint = event.globalPos()
rect = self.rect()
tl = self.mapToGlobal(rect.topLeft())
rb = self.mapToGlobal(rect.bottomRight())
if(not self.isLeftPressDown):
self.region(gloPoint)
else:
if(self.dir != self.Numbers.NONE):
rmove=QRect(tl, rb)
if(self.dir==self.Numbers.LEFT):
if(rb.x() - gloPoint.x() <= self.minimumWidth()):
rmove.setX(tl.x())
else:
rmove.setX(gloPoint.x())
elif(self.dir==self.Numbers.RIGHT):
rmove.setWidth(gloPoint.x() - tl.x())
elif(self.dir==self.Numbers.UP):
if(rb.y() - gloPoint.y() <= self.minimumHeight()):
rmove.setY(tl.y())
else:
rmove.setY(gloPoint.y())
elif(self.dir==self.Numbers.DOWN):
rmove.setHeight(gloPoint.y() - tl.y())
elif(self.dir==self.Numbers.LEFTTOP):
if(rb.x() - gloPoint.x() <= self.minimumWidth()):
rmove.setX(tl.x())
else:
rmove.setX(gloPoint.x())
if(rb.y() - gloPoint.y() <= self.minimumHeight()):
rmove.setY(tl.y())
else:
rmove.setY(gloPoint.y())
elif(self.dir==self.Numbers.RIGHTTOP):
rmove.setWidth(gloPoint.x() - tl.x())
rmove.setY(gloPoint.y())
elif(self.dir==self.Numbers.LEFTBOTTOM):
rmove.setX(gloPoint.x())
rmove.setHeight(gloPoint.y() - tl.y())
elif(self.dir==self.Numbers.RIGHTBOTTOM):
rmove.setWidth(gloPoint.x() - tl.x())
rmove.setHeight(gloPoint.y() - tl.y())
else:
pass
self.setGeometry(rmove)
else:
self.move(event.globalPos() - self.dragPosition)
event.accept()
def __isWindowMaximized(self, hWnd) -> bool:
""" Determine whether the window is maximized """
# GetWindowPlacement() returns the display state of the window and the restored,
# maximized and minimized window position. The return value is tuple
windowPlacement = win32gui.GetWindowPlacement(hWnd)
if not windowPlacement:
return False
return windowPlacement[1] == win32con.SW_MAXIMIZE
def __monitorNCCALCSIZE(self, msg: MSG):
""" 调整窗口大小 """
monitor = win32api.MonitorFromWindow(msg.hWnd)
# If the display information is not saved, return directly
if monitor is None and not self.__monitorInfo:
return
elif monitor is not None:
self.__monitorInfo = win32api.GetMonitorInfo(monitor)
# adjust the size of window
params = cast(msg.lParam, POINTER(NCCALCSIZE_PARAMS)).contents
params.rgrc[0].left = self.__monitorInfo['Work'][0]
params.rgrc[0].top = self.__monitorInfo['Work'][1]
params.rgrc[0].right = self.__monitorInfo['Work'][2]
params.rgrc[0].bottom = self.__monitorInfo['Work'][3]
def __onScreenChanged(self):
hWnd = int(self.windowHandle().winId())
win32gui.SetWindowPos(hWnd, None, 0, 0, 0, 0, win32con.SWP_NOMOVE |
win32con.SWP_NOSIZE | win32con.SWP_FRAMECHANGED)
def closeEvent(self, event):
reply = QMessageBox.question(self,
'Quit',
"是否要退出程序?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if reply == QMessageBox.Yes:
try:
# 保存控制系统配置
if hasattr(self, 'controlWidget') and self.controlWidget:
self.controlWidget.controlManager.shutdown()
Client.close()
except Exception as e:
print(f"关闭程序时出错: {e}")
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle(QtWidgets.QStyleFactory.create('Fusion'))
app.setStyleSheet(CommonHelper.readQss('Static/main.qss'))
# print(QtWidgets.QStyleFactory.keys())
ex = MainWindow()
ex.show()
sys.exit(app.exec_())