From 455857c88ed2be17a9ebb8cb07c594a6bf725c16 Mon Sep 17 00:00:00 2001 From: "ADMINISTRATOR\\Administrator" Date: Wed, 13 Mar 2024 17:49:46 +0800 Subject: [PATCH] =?UTF-8?q?0313=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Static/Area.qss | 42 ++++++-- Static/Main.qss | 71 ++++++++++--- Static/delete.png | Bin 0 -> 4973 bytes Static/newH.png | Bin 0 -> 12243 bytes Static/pause.png | Bin 0 -> 1920 bytes Static/start.png | Bin 0 -> 3235 bytes Static/varMagH.png | Bin 0 -> 9831 bytes UI/AreaTabWidget.py | 26 +++-- UI/DeviceDialogWidget.py | 8 +- UI/DeviceWidget.py | 51 ++++----- UI/MainWindow.py | 162 ++++++++++++++++++++++------- UI/RightAreaWidget.py | 8 +- log/log.txt | 112 ++++++++++++++++++++ model/ProjectModel/DeviceManage.py | 28 +++-- requirements.txt | 4 + 15 files changed, 401 insertions(+), 111 deletions(-) create mode 100644 Static/delete.png create mode 100644 Static/newH.png create mode 100644 Static/pause.png create mode 100644 Static/start.png create mode 100644 Static/varMagH.png create mode 100644 log/log.txt create mode 100644 requirements.txt diff --git a/Static/Area.qss b/Static/Area.qss index e32560e..d4ee466 100644 --- a/Static/Area.qss +++ b/Static/Area.qss @@ -1,10 +1,24 @@ -QPushButton#okBtn, QPushButton#delAreaBtn, QPushButton#forceBtn { +QPushButton#okBtn, QPushButton#delAreaBtn { font-size: 22px; + border: none; + +} + +QPushButton#okBtn:hover, QPushButton#delAreaBtn:hover { + + font-size: 22px; + + font: bold; + + border: none; + + margin-bottom: -2px + } QPushButton#wirteDIDOforceBtn { @@ -23,25 +37,39 @@ QPushButton#wirteDIDOforceBtn { QPushButton#forceBtn { - font-size: 25px; + font-size: 22px; - padding-left: 10px; + padding-left: 5px; - padding-right: 10px; + padding-right: 5px; - /* padding-top: 5px; + padding-top: 6px; + + padding-bottom: 5px; - padding-bottom: 5px; */ } -QPushButton#deviceAddButton, QPushButton#addareabutton { +QPushButton#deviceAddButton{ color: #328ffc; font-size: 20px; + padding-bottom: 8px; + + } + + +QPushButton#addareabutton{ + + color: #328ffc; + + font-size: 20px; + + padding-top: 7px; + } QPushButton#initAddDeviceButton, QPushButton#initAreaAddButton{ diff --git a/Static/Main.qss b/Static/Main.qss index 206281d..9dcca0b 100644 --- a/Static/Main.qss +++ b/Static/Main.qss @@ -24,10 +24,7 @@ QTabBar::close-button { subcontrol-origin: padding; - margin-top: 2px; - - margin-left: 4px; - } +} QTabBar::close-button:hover { @@ -46,7 +43,7 @@ QTabBar::tab{ background: #f0f0f0; - border: 2px; + /* border: 2px;*/ min-width: 200px; @@ -55,7 +52,6 @@ QTabBar::tab{ font: 20px; font-family: ".SFNSDisplay-Regular"; - } @@ -68,7 +64,6 @@ QTabBar#areaTabBar::tab{ } - QTabBar::tab:selected { background-color: #809ac2; @@ -82,15 +77,6 @@ QTabBar::tab:selected { } -QTabWidget#deviceTabWidget::pane { - - border: 1px solid #a8a8a8; - - border-radius: 3px; - - margin: 2px; - - } QDockWidget{ @@ -99,6 +85,9 @@ QDockWidget{ font: bold; font-family: ".SFNSDisplay-Regular"; + + border: 1px solid black; + } QDockWidget::title{ @@ -113,12 +102,60 @@ QDockWidget::title{ + } -QToolBar QToolButton{ + +QPushButton#startProtocolBtn, QPushButton#switchBtn{ font-size: 20px; + + border: none; + + background-color: white; + +} + +QPushButton#startProtocolBtn:hover, QPushButton#switchBtn:hover{ + + font-size: 20px; + font: bold; + border: none; + + margin-bottom: -2px; + + background-color: white; + +} + +QPushButton#startProtocolBtn:checked, QPushButton#switchBtn:checked{ + + font-size: 20px; + + border: none; + +} + + +QWidget#MainWindow{ + + background-color: white; + +} + +QWidget#deviceWidget{ + + background-color: white; + + border: 1px solid gray; + } + +QTabWidget::tab-bar{ + + background-color: black; + +} \ No newline at end of file diff --git a/Static/delete.png b/Static/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..dc513f4a890c73f6a44ef358052e09f8a36e9aea GIT binary patch literal 4973 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 literal 0 HcmV?d00001 diff --git a/Static/pause.png b/Static/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a32b96eee5092ce16e827171382f609cb173e6 GIT binary patch literal 1920 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST~P>f!*EH#WAE}&fCiyC&s4oI3(tF z2x~M7-C=y!ASP33(^tUS!Dm*+B71>JbR+AVH5OV2q@pEneLnkV@$Vqpx$P6agt95| z^0-JzbeI?jWF{zFI^eLRvC)H>c_JHiCE`LAzJ7eXyQ1=E>-nke`tkn~{=C1s+&};E z&!6FT`_CUf9Q^0Q;duW3e*ZsJqH-1M-D-bK5d3JJ-|FD^rrY+<=^vfjKhCtZF^X>$ zuK%(9k%1A&lsl26g@;wHkr3k z*l80Z<-sv|HJ5BFJCDL{;LSN#cYoI|r6>>ApUx(iy`MUtG>nCJukpxjc@c7J$_|a-eqsrs zL?b@{)CY<2Q{jRI5QjkIxz&=U>gPvabmxyp7Py zXjh5Q|Ad23u~0eJs#{?FAs3J;@d26IgNMm)|+SV`Q6r?FkjuJNOc@@G8sh;sScFi z=+7|Cs~s3I&arO6Cs!;c4g4r}Ynwg0;FmnT5F(A{ve|)SjJEi^{9|SV_WQoqnSou< ze%YbDAr{GhM5(oD+&3k%_{R6o`hJDxq*dZRuv_+c2FIHvR94B%&E)$Rk9^d5{U&o3 zyh`++LJ`!yLk1YvzWx|`d~4h_t|$g`si|kQRjU(+k7UAX^riL%sPW2*<9K`e>$`-t zj^3-v;d45KLIqJNd2%scC8z`zJ2%66ghSC6e@ZDW_Rok%qi0Y_0~b_^p!o1hLrS72 zFEZDYUxw@r3MYIHwu}l2Pf*WSj${_v{aj~vH}9-WgFf<;CqKeBs5i@i6c1 zeff(k;>$_h85trct;Lo84`PI3EzYf~EET{Z@{b%QGa+*;m&Snb@WhZ(xu)5NJnXB=}3nCGQhEk zMnU9MkiU@a23O~?q*udC0w$?-M$R!~W(0Nc0r`vBC00zS-B`TI&G>u6%&VBBQqpZ} zJ$I3vm0lwSA(If(t9=43lcYm%|FLrL5v1Ot;I}5)y`xv= zEYP==Ta%U~w8lA)#)W$3B)FmRU+*7P4SLM8U;pKnq>;8gk7%_9w z7ZbOSixcBmF|h{8r)$VN7cbi9jK#}WiL)+pR z%6dzbhGl>Y<@xx_e)WFxD<`y@cJL20(GdRjp&cK9c%umoEr)d69+I&Z=NdEb1@kV( zx-i=-JhtAuR+6z-E;Z4f314!`Ut0g_3M!bkoi~YKhfwX>;x{JwmpVxP7z2fIgEy-O zJ;y)4uhiU@Sfb-hk3&Ss*-^~ulXg100o?dR)(tlQRz@GuBIxaf*zNOrgo_ZWtMKVz zHeb^7>jvY(7BkHY81dzKcnTTp4Ht~H$=X7W- zbI79EZ&kIEi-!L6qklrEN_fx`7IZi7JBwN4`*n=ZDB&jV4eiiS(UvX0{OWx}O2A$7 z7*8uP0aWnM$lQpg?!NWN+~sp0<=GqKU~h~;sZ-#*L#L6FbPJX)v+0HD&apk;P%Q@( z4@7q$|1zgMUaJVK|I;Ac9T%-b8s9Je3l!ba@^^I=MM`2&L!Rlf_VQL<;)Qdv5bBq_ zuY?lc8s7m9S1tMuEXu`;?;Abk1T5IuI@N!C}{-VUUno^K(uV? z-hPir(piRm2U5D!#dCX+3R8}Vd0y3U2ST0fD*`wHMyq`y5!kV(2$@i6%T5b`@{iq@&m?5nP0?k|4@Bt8)z(x0JWzB z@ilrz7E{8iu4m6b8B{M|86Z@id@K%ZR~!X){B0j-?Jo!|@WfLQ3OT)nJLBj3l`ed( zKtZUjA*LSNE{Gc-)Smqt2d7($+e!R^rR%Cnxiryq15||9E%Ho?xvi^qM@Cv>p+X;( zou9lyNV{g5+GmFhu9dk;w7%dLtJo!WO|dK`Q?o1ne^3A4Ex%M&P(1IoHL1QIxP0vL MIEi=koIr318gy`HaCdjuNxr?e zYJY6)pIfz4)o<6Ce%szY-OqE*>F_V|lISReC;$KeU0O;^36}r)o3LKPo=<-qmtol} zGhsPl0H7uo_0bR!Cc`-?Ns0g}M~U}f0=bctk{kfw`4Ir{4*~%0VSoAW0st;70KmQh z002w@0N&YWG%JE&1+Tx#N{Yd9*zZoX#SfOfv6s?xg5^^GlEi?O008jeMOsW)#eLyu z*+WNl`u_Fl&N32K=&K%HJl-7q(QTbH<22FPI^w~A!%r9%X$T1hVuf&)grgfH6w0zK z%fyc59K}+MBbuEaAqYGH_SX*&qGz*hv$pOA*)ACED}MK@#iO1xeeQEsCv9BhL0BTl z6#oN{byi_80uKrq=>Y!q<n!Cd<|jL7?OPLNAByzwsha=h>LwjZE(~lwO*^6 zBt}6cw78`7GGL{pc%=}yKZv--+Uwclh+Ek#jm4EA?Sd=ja1Psv1qog53NFD>GcEELH)NebynIu7TxN<)=;sw5C-M!S%AM~pj#lgLO~onmYqG>sJ@ zz4{Hs(L(7cb&xw+Yrq!A>a|U!|CzUKSI|1%av=YWe3o~C8Csm{P36A3gwq2284
^x)o8~NQ7EuGfo9@Q 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 XnEtkQmOu<^93U+&FIFjH@a=yAcxVGm literal 0 HcmV?d00001 diff --git a/UI/AreaTabWidget.py b/UI/AreaTabWidget.py index ae62441..ea0bdcc 100644 --- a/UI/AreaTabWidget.py +++ b/UI/AreaTabWidget.py @@ -2,6 +2,7 @@ import re from socket import AI_ADDRCONFIG import sys import json +import qtawesome from tkinter import N from functools import partial from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QWidget, QVBoxLayout, QLabel, QPushButton, QLayout, \ @@ -31,7 +32,7 @@ class AreaTabWidget(QTabWidget): self.setObjectName('areaTabWidget') self.setTabPosition(QTabWidget.South) self.tabBar().setObjectName('areaTabBar') - self.addAreaButton = QPushButton("添加通道") + self.addAreaButton = QPushButton("添加通道 ") self.addAreaButton.setObjectName('addareabutton') self.addAreaButton.setIcon(QIcon('Static/add.png')) self.addAreaButton.setFlat(True) @@ -142,17 +143,18 @@ class AreaWidget(QWidget): self.mainLayout = QHBoxLayout() self.leftLayout = QGridLayout() self.rightLayout = QGridLayout() + self.mainLayout.setContentsMargins(0, 0, 0, 0) LimitData = self.devicesManange.getLimitData(self.areaTabWidget.deviceName) - self.pvUpperLimit = QLabel('量程上限: {}'.format(LimitData[0])) + self.pvUpperLimit = QLabel('量程上限: {}'.format(LimitData[0])) self.pvUpperLimit.setObjectName('pvUpperLimit') - self.pvLowerLimit = QLabel('量程下限: {}'.format(LimitData[1])) + self.pvLowerLimit = QLabel('量程下限: {}'.format(LimitData[1])) self.pvLowerLimit.setObjectName('pvLowerLimit') - self.pvUnit = QLabel('单 位: {}'.format(LimitData[2])) + self.pvUnit = QLabel('单 位: {}'.format(LimitData[2])) self.pvUnit.setObjectName('pvUnit') @@ -184,17 +186,23 @@ class AreaWidget(QWidget): self.okBtn = QPushButton('确定') # self.okBtn.setFixedSize(90, 27) self.okBtn.setObjectName('okBtn') + self.okBtn.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) + # self.okBtn.setIcon(QIcon('Static/delete.png')) self.okBtn.clicked.connect(self.addAreaWidget) self.okBtnValue = True self.delAreaBtn = QPushButton('删除') + self.delAreaBtn.setIcon(QIcon('Static/delete.png')) self.delAreaBtn.setObjectName('delAreaBtn') # self.delAreaBtn.setFixedSize(90, 27) self.delAreaBtn.clicked.connect(self.removeAreaTab) hLayout = QHBoxLayout() + hLayout.addWidget(QSplitter()) hLayout.addWidget(self.okBtn) + hLayout.addWidget(QSplitter()) hLayout.addWidget(self.delAreaBtn) + hLayout.addWidget(QSplitter()) # vlayout = QVBoxLayout() @@ -213,6 +221,7 @@ class AreaWidget(QWidget): self.leftLayout.addWidget(self.byteLineEdit,5, 1, 2, 1) self.leftLayout.addWidget(QSplitter(),6, 0, 2, 2) self.leftLayout.addLayout(hLayout, 7, 0, 2, 2) + self.leftLayout.setContentsMargins(10, 10, 0, 20) # self.leftLayout.addWidget(self.delAreaBtn, 3, 1, 1, 1) @@ -312,24 +321,28 @@ class AreaWidget(QWidget): pattern = re.compile(r'^[1-7]$') match = pattern.match(byteLineEdit) if not match: - QMessageBox.warning(self, '提示', '请输入1 - 8。') + QMessageBox.warning(self, '提示', '请输入1 - 7。') return else: #设置点击确定后无法编辑,点击编辑后才能编辑 if self.okBtnValue: self.okBtn.setText('编辑') + self.okBtn.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2')) self.dataTypeCombox.setEnabled(False ) self.orderCombox.setEnabled(False ) self.byteLineEdit.setEnabled(False ) self.okBtnValue = False if dataType in ['DI','DO']: self.okBtn.setEnabled(False) + elif dataType in ['AI','AO']: - self.okBtn.setText('确定') + self.okBtn.setText('保存') + self.okBtn.setIcon(qtawesome.icon('fa.save', color='#1fbb6f')) self.orderCombox.setEnabled(True) self.byteLineEdit.setEnabled(True) self.okBtnValue = True + return #修改配置后刷新布局内容 # if self.settingValue is None: @@ -359,6 +372,7 @@ class AreaWidget(QWidget): elif areaId is not None and curIndex in areaId: # print(areaId,curIndex,2222) DevicesManange.updataAreas(dataType, order, byteLineEdit, deviceName, curIndex) + # print(deviceName, curIndex, dataType, int(byteLineEdit)) self.isRead = self.devicesManange.getDevice(deviceName).editArea(index = curIndex, type = dataType, order = order, bytes = int(byteLineEdit)) # self.readVarTimer.start(500) else: diff --git a/UI/DeviceDialogWidget.py b/UI/DeviceDialogWidget.py index 837f85a..299cb11 100644 --- a/UI/DeviceDialogWidget.py +++ b/UI/DeviceDialogWidget.py @@ -37,7 +37,7 @@ class DeviceDialog(QDialog): cancel_button = button_box.button(QDialogButtonBox.Cancel) cancel_button.setText("取消") # 设置Cancel按钮的文本 - button_box.accepted.connect(self.check_input) + button_box.accepted.connect(self.checkInput) button_box.rejected.connect(self.reject) layout.addRow(button_box) @@ -54,20 +54,20 @@ class DeviceDialog(QDialog): pvUnit = self.findChild(QLineEdit, "pvUnit").text() return deviceName, pvUpperLimit, pvLowerLimit, pvUnit - def check_input(self): + def checkInput(self): titleName, pvUpperLimit, pvLowerLimit, pvUnit = self.getParameters() deviceName = titleName + self.dataTypeAndModel if not pvUpperLimit or not pvLowerLimit or not pvUnit or not deviceName: QMessageBox.warning(self, '警告', '有值未输入。') return elif DeviceDB.getByName(deviceName): - print(DeviceDB.getByName(deviceName),2343) + # print(DeviceDB.getByName(deviceName),2343) QMessageBox.warning(self, '警告', '设备名重复') return elif not re.match(r'^[-+]?\d*\.?\d*$', pvUpperLimit) or not re.match(r'^[-+]?\d*\.?\d*$', pvLowerLimit): QMessageBox.warning(self, '警告', '请输入数字。') return - elif pvUpperLimit < pvLowerLimit: + elif float(pvUpperLimit) <= float(pvLowerLimit): QMessageBox.warning(self, '警告', '量程输入有误。') return else: diff --git a/UI/DeviceWidget.py b/UI/DeviceWidget.py index 846fdb6..dd174d7 100644 --- a/UI/DeviceWidget.py +++ b/UI/DeviceWidget.py @@ -13,11 +13,12 @@ from utils.DBModels.DeviceModels import DeviceDB from model.ProjectModel.DeviceManage import DevicesManange from UI.DeviceDialogWidget import DeviceDialog -class DeviceWidget(QMainWindow): +class DeviceWidget(QTabWidget): def __init__(self, dockWidget, devicesManange): super().__init__() self.dockWidget = dockWidget self.devicesManange = devicesManange + self.setObjectName('deviceWidget') self.initUI() @@ -27,42 +28,34 @@ class DeviceWidget(QMainWindow): self.proType = self.dataTypeAndModel[0:2] self.masterSlaveModel = self.dataTypeAndModel[2:4] - - self.deviceTabWidget = QTabWidget(self) - self.deviceTabWidget.setObjectName('deviceTabWidget') + self.setObjectName('deviceTabWidget') - - self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) - self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) - - self.addDeviceButton = QPushButton('添加设备 ') + self.addDeviceButton = QPushButton('添加设备') self.addDeviceButton.setObjectName('deviceAddButton') self.addDeviceButton.setIcon(QIcon('Static/add.png')) self.addDeviceButton.setFlat(True) self.addDeviceButton.clicked.connect(self.addDeviceWidget) - self.deviceTabWidget.setCornerWidget(self.addDeviceButton) - self.deviceTabWidget.setTabsClosable(True) - self.deviceTabWidget.tabCloseRequested.connect(self.closeTab) + self.setCornerWidget(self.addDeviceButton) + self.setTabsClosable(True) + self.tabCloseRequested.connect(self.closeTab) self.state = True self.initWidget() - # 设置主窗口的中心部分为 QTabWidget - self.setCentralWidget(self.deviceTabWidget) def closeTab(self, index): reply = QMessageBox.question(self, 'Confirmation', '确定删掉此设备吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: if index != -1: - deviceName = self.deviceTabWidget.tabText(index) + self.dockWidget.windowTitle() - print(deviceName,index,222) - self.deviceTabWidget.removeTab(index) + deviceName = self.tabText(index) + self.dockWidget.windowTitle() + # print(deviceName,index,222) + self.removeTab(index) self.devicesManange.delDevice(deviceName = deviceName ) DeviceDB.deleteDevice(deviceName = deviceName) else: return - if self.deviceTabWidget.count() == 0: + if self.count() == 0: self.creatInitWidget() @@ -76,11 +69,11 @@ class DeviceWidget(QMainWindow): DeviceDB().addDevice(deviceName = self.deviceName, proType = self.proType , masterSlaveModel = self.masterSlaveModel, pvUpperLimit=pvUpperLimit, pvLowerLimit=pvLowerLimit, pvUnit=pvUnit) areaTabWidget = AreaTabWidget(self) if init: - self.deviceTabWidget.removeTab(0) - tabIndex = self.deviceTabWidget.count() - self.deviceTabWidget.tabBar().setHidden(False) - self.deviceTabWidget.addTab(areaTabWidget,str(self.titleName)) - self.deviceTabWidget.setCurrentIndex(tabIndex) + self.removeTab(0) + tabIndex = self.count() + self.tabBar().setHidden(False) + self.addTab(areaTabWidget,str(self.titleName)) + self.setCurrentIndex(tabIndex) self.devicesManange.addDevice(proType=self.proType, masterSlaveModel = self.masterSlaveModel, deviceName = self.deviceName) else: return @@ -88,8 +81,8 @@ class DeviceWidget(QMainWindow): self.deviceName = deviceName self.titleName = self.deviceName[:-4] areaTabWidget = AreaTabWidget(self) - self.deviceTabWidget.tabBar().setHidden(False) - self.deviceTabWidget.addTab(areaTabWidget,str(self.titleName)) + self.tabBar().setHidden(False) + self.addTab(areaTabWidget,str(self.titleName)) @@ -119,12 +112,12 @@ class DeviceWidget(QMainWindow): addButton.setIcon(icon) addButton.setIconSize(iconSize) addButton.clicked.connect(lambda: self.addDeviceWidget(True)) - layout.addItem(self.horizontalSpacer) + layout.addWidget(QWidget()) layout.addWidget(addButton) - layout.addItem(self.horizontalSpacer) + layout.addWidget(QWidget()) widget.setLayout(layout) - self.deviceTabWidget.addTab(widget,'') - self.deviceTabWidget.tabBar().setHidden(True) + self.addTab(widget,'') + self.tabBar().setHidden(True) diff --git a/UI/MainWindow.py b/UI/MainWindow.py index a85f3cb..9022a39 100644 --- a/UI/MainWindow.py +++ b/UI/MainWindow.py @@ -1,13 +1,33 @@ +import os import sys - -from PyQt5.QtWidgets import QApplication, QMainWindow, QDockWidget, QToolBar, QAction, QTabWidget, QGridLayout -from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtGui import QIcon +import time +import win32con +import win32gui +import win32process +import subprocess +import qtawesome + +from PyQt5.QtWidgets import QApplication, QPushButton, QMainWindow, QDockWidget, QToolBar, QAction, QTabWidget, QGridLayout, QWidget, QHBoxLayout, QStackedWidget\ + ,QVBoxLayout +from PyQt5.QtCore import Qt, QTimer, QSize +from PyQt5.QtGui import QIcon, QWindow from utils.DBModels.BaseModel import * from model.ClientModel.Client import Client from UI.DeviceWidget import DeviceWidget from model.ProjectModel.DeviceManage import DevicesManange + +def getHwndByPid(pid): + def callback(hwnd, hwnds): + if hwnd is not None and win32gui.IsWindowVisible(hwnd): + _, found_pid = win32process.GetWindowThreadProcessId(hwnd) + if found_pid == pid: + hwnds.append(hwnd) + + hwnds = [] + win32gui.EnumWindows(callback, hwnds) + return hwnds[0] + class CommonHelper: def __init__(self): pass @@ -18,33 +38,44 @@ class CommonHelper: return f.read() -class MainWindow(QMainWindow): +class MainWindow(QWidget): def __init__(self): super().__init__() + self.setObjectName("MainWindow") self.devicesManange = DevicesManange() + self.process = None self.initUI() def initUI(self): - self.protocolTimer = QTimer() self.protocolTimer.timeout.connect(self.readValues) - self.toolbar = QToolBar() - self.addToolBar(self.toolbar) + # self.toolbarWidget = QWidget() + # self.addToolBar(Qt.LeftToolBarArea, self.toolbar) + + toolbarLayout = QHBoxLayout() - self.startProtocolBtn = QAction(self) + self.startProtocolBtn = QPushButton() self.startProtocolBtn.setObjectName("startProtocolBtn") - self.startProtocolBtn.setIcon(QIcon('Static/startProtocol.png')) - self.startProtocolBtn.setIconText('开始通讯') + # self.startProtocolBtn.setIcon(QIcon('Static/startProtocol.png')) + self.startProtocolBtn.setText('开始通讯') + self.startProtocolBtn.setIcon(QIcon('Static/start.png')) + self.startProtocolBtn.setIconSize(QSize(18, 18)) self.startProtocolBtn.setCheckable(True) - self.startProtocolBtn.triggered.connect(self.startProtocol) + self.startProtocolBtn.clicked.connect(self.startProtocol) - self.loadProjectBtn = QAction('导入工程', self) - self.loadProjectBtn.setObjectName("loadProjectBtn") - self.loadProjectBtn.triggered.connect(self.loadProject) + self.switchBtn = QPushButton('通讯组态') + self.switchBtn.setObjectName("switchBtn") + self.switchBtn.setIcon(qtawesome.icon('fa.exchange', color='#1fbb6f')) + self.switchBtn.setIconSize(QSize(20, 20)) + self.switchBtn.setCheckable(True) + self.switchBtn.clicked.connect(self.switchWidget) - # self.toolbar.addAction(self.loadProjectBtn) - self.toolbar.addAction(self.startProtocolBtn) + toolbarLayout.addWidget(self.startProtocolBtn, 1) + toolbarLayout.addWidget(self.switchBtn, 1) + toolbarLayout.addWidget(QWidget(), 20) + toolbarLayout.setSpacing(20) + toolbarLayout.setContentsMargins(0, 0, 0, 0) dpMasterDockWidget = QDockWidget('DP主站') dpMasterDockWidget.setWidget(DeviceWidget(dpMasterDockWidget, self.devicesManange)) @@ -62,41 +93,56 @@ class MainWindow(QMainWindow): paSlaveDockWidget.setWidget(DeviceWidget(paSlaveDockWidget, self.devicesManange)) paSlaveDockWidget.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable) # type: ignore - self.addDockWidget(Qt.TopDockWidgetArea, dpMasterDockWidget) # type: ignore - self.addDockWidget(Qt.TopDockWidgetArea, dpSlaveDockWidget) # type: ignore - self.addDockWidget(Qt.BottomDockWidgetArea, paMasterDockWidget) # type: ignore - self.addDockWidget(Qt.BottomDockWidgetArea, paSlaveDockWidget) # type: ignore + + self.upperWidget = QMainWindow() + self.upperWidget.addDockWidget(Qt.TopDockWidgetArea, dpMasterDockWidget) # type: ignore + self.upperWidget.addDockWidget(Qt.TopDockWidgetArea, dpSlaveDockWidget) # type: ignore + self.upperWidget.addDockWidget(Qt.BottomDockWidgetArea, paMasterDockWidget) # type: ignore + self.upperWidget.addDockWidget(Qt.BottomDockWidgetArea, paSlaveDockWidget) # type: ignore + + self.stackWidget = QStackedWidget() + + self.stackWidget.addWidget(self.upperWidget) + self.stackWidget.addWidget(QWidget()) + + self.mainLayout = QVBoxLayout(self) + self.mainLayout.addLayout(toolbarLayout, 1) + self.mainLayout.addWidget(self.stackWidget, 50) + self.setLayout(self.mainLayout) + # self.setCentralWidget(self.stackWidget) self.setWindowIcon(QIcon('Static/zhjt.ico')) self.setWindowTitle("PROFIBUS") - self.resize(800, 600) + + # self.resize(800, 600) # self.showMaximized() def startProtocol(self): - if self.startProtocolBtn.isCheckable(): + if self.startProtocolBtn.isChecked(): self.startProtocolBtn.setText('停止通讯') - self.startProtocolBtn.setCheckable(False) + self.startProtocolBtn.setIcon(QIcon('Static/pause.png')) + self.startProtocolBtn.setIconSize(QSize(22, 22)) self.protocolTimer.start(500) else: self.startProtocolBtn.setText('开始通讯') - self.startProtocolBtn.setCheckable(True) + self.startProtocolBtn.setIcon(QIcon('Static/start.png')) self.protocolTimer.stop() def readValues(self): self.devicesManange.readAreas() dockWidgets = self.findChildren(QDockWidget) #找到四个dockWidget窗口 for dockWidget in dockWidgets: - if dockWidget.widget().deviceTabWidget.currentWidget().objectName() == 'initWidget': - print(dockWidget.widget().deviceTabWidget.currentWidget().objectName()) + if dockWidget.widget().currentWidget().objectName() == 'initWidget': + # print(dockWidget.widget().currentWidget().objectName()) continue - areaTabWidget = dockWidget.widget().deviceTabWidget.currentWidget()#判断四个窗口里是否有deviceTabWidget + areaTabWidget = dockWidget.widget().currentWidget()#判断四个窗口里是否 if areaTabWidget.currentWidget().objectName() == 'initWidget': - print(2) + # print(2) continue if areaTabWidget.currentWidget().state: - print(3) + # print(3) continue # masterAndSlave = dockWidget.windowTitle()[2:5] @@ -105,19 +151,65 @@ class MainWindow(QMainWindow): # dataTypeAndModel = masterAndSlave + dataType # if dataTypeAndModel in ['主站AI', '主站DI', '从站AO', '从站DO']: - # devicecurIndex = deviceTabWidget.currentIndex() - # deviceTabText = deviceTabWidget.tabText(devicecurIndex) + # devicecurIndex =.currentIndex() + # deviceTabText =.tabText(devicecurIndex) # try: areacurindex = areaTabWidget.currentIndex() # deviceName = deviceTabText + proType - buttonlayoutWidget = areaTabWidget.currentWidget().buttonWidgets#获取buttonlayout + buttonlayoutWidget = areaTabWidget.currentWidget().rightAreaWidgets#获取buttonlayout buttonlayoutWidget.readValues(curIndex = areacurindex) # except Exception as e: # print(e) - def loadProject(self): - pass + def switchWidget(self): + if self.switchBtn.isChecked(): + if self.process: + self.stackWidget.setCurrentIndex(1) + QTimer.singleShot(50, lambda:self.stackWidget.setCurrentIndex(2)) + else: + self.stackWidget.setCurrentIndex(1) + startupInfo = subprocess.STARTUPINFO() + startupInfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupInfo.wShowWindow = 2 + self.process = subprocess.Popen("D:\\EnTalk PROFIBUS Manager\\DP.exe",startupinfo=startupInfo) + QTimer.singleShot(500, lambda:self.showLowerWidget(self.process)) + self.switchBtn.setText('变量读写') + # self.switchBtn.setIcon(QIcon('Static/varMagH.png')) + else: + # self.stackWidget.setCurrentIndex(1) + self.stackWidget.setCurrentIndex(0) + self.switchBtn.setText('通讯组态') + # self.switchBtn.setIcon(QIcon('Static/newH.png')) + + + + def showLowerWidget(self, process): + pid = process.pid + hwnd = int(getHwndByPid(pid)) + style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE) + exstyle = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) + wrect = win32gui.GetWindowRect(hwnd)[:2] + win32gui.GetClientRect(hwnd)[2:] + # print('save', hwnd, style, exstyle, wrect) + widget = QWidget.createWindowContainer(QWindow.fromWinId(hwnd)) + # print(type(widget)) + widget.hwnd = hwnd # 窗口句柄 + widget.phwnd = 0 # 父窗口句柄 + widget.style = style # 窗口样式 + widget.exstyle = exstyle # 窗口额外样式 + widget.wrect = wrect # 窗口位置 + + + self.stackWidget.addWidget(widget) + widget.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.WindowFullScreen) + + win32gui.SetParent(hwnd, int(self.winId())) + + self.stackWidget.setCurrentIndex(2) + + def closeEvent(self, event): + if self.process: + self.process.kill() if __name__ == '__main__': app = QApplication(sys.argv) diff --git a/UI/RightAreaWidget.py b/UI/RightAreaWidget.py index 8f06712..37973a6 100644 --- a/UI/RightAreaWidget.py +++ b/UI/RightAreaWidget.py @@ -134,15 +134,17 @@ class RightAreaWidgets(QWidget): def readValues(self, curIndex): - print(curIndex) + # print(curIndex) if self.areaLabel is None or self.areaLabel == dict(): return # elif curIndex == -1: # return else: - print(self.deviceName, curIndex) device = self.devicesManange.getDevice(self.deviceName) values = device.getAreaValues(curIndex) + # print(self.deviceName, curIndex, values) + if len(values) > len(self.areaLabel): + return for index, value in enumerate(values): self.areaLabel[index].setText(str(value)) # print(self.areaLabel[index],values) @@ -157,7 +159,7 @@ class RightAreaWidgets(QWidget): dataTypeAndModel = self.deviceName[-2:] + self.dataType curIndex = self.areaWidget.areaTabWidget.currentIndex() valueList = self.wirteAreaLineEditValue(dataTypeAndModel=dataTypeAndModel, valueLabel=valueLabel,number=number, value=value) - print(valueList, curIndex) + # print(valueList, curIndex) if valueList is None: return else: diff --git a/log/log.txt b/log/log.txt new file mode 100644 index 0000000..73456c2 --- /dev/null +++ b/log/log.txt @@ -0,0 +1,112 @@ +2024-03-13 17:26:51 - [Debug] (:0, ): "gsd files directory: C:/Users/Administrator/Desktop/profibus/GSD" + +2024-03-13 17:26:51 - [Warn ] (:0, ): libpng warning: iCCP: known incorrect sRGB profile +2024-03-13 17:26:51 - [Debug] (:0, ): "bin directory: D:/EnTalk PROFIBUS Manager" + +2024-03-13 17:26:51 - [Debug] (:0, ): "GSD directory: D:/EnTalk PROFIBUS Manager/GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/3000plus.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/asco089c.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/AVTR4711(1).gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/DPSlave.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/ET021.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/ET025.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/ET026.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/hen100b7.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/hen200b7.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/hil_ezr.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/KIAG0D0B.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK239-Free.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK239-Master.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK239-Slave.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK410.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK411.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK412.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): error in gsd! + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK430.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK432.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK441.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK442.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK511.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK512.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK610.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK616.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK621.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK622.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK710.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK716.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/LK720.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/M-IO1.4.GSD" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/MCPA0B25.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/MCYB0C46.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/pa139710.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/PA_39720.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/PBMDMM04.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/si01818e.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/si04801e.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/sip58052.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): gsd file path: "D:/EnTalk PROFIBUS Manager/GSD/Xone_DP.gsd" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): application started... +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:51 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:52 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:52 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:52 - [Debug] (:0, ): "row count: 0" + +2024-03-13 17:26:52 - [Debug] (:0, ): "row count: 0" + diff --git a/model/ProjectModel/DeviceManage.py b/model/ProjectModel/DeviceManage.py index 454a2a4..0c7a323 100644 --- a/model/ProjectModel/DeviceManage.py +++ b/model/ProjectModel/DeviceManage.py @@ -6,7 +6,7 @@ import numpy as np from protocol.ModBus.ByteOrder import * from protocol.ModBus.TCPMaster import * import struct - +import time #从站 "AI" "DI"可强制 #主站 "AO" "DO"可强制 class Device(): @@ -60,7 +60,9 @@ class Device(): self.inputAreas.pop(self.indexDict[index]) elif type in ["AO", "DO"]: self.outputAreas.pop(self.indexDict[index]) - # self.recalculateAddress() + self.indexDict.pop(index) + self.areas.pop(index) + self.recalculateAddress() def recalculateAddress(self): # print(self.inputStartAddress) @@ -77,6 +79,7 @@ class Device(): area.addressList = np.arange(area.startAddress, area.endAddress + 1, area.bytes).tolist() # print(area.addressList, area.startAddress, area.endAddress, self.deviceName) endAddress = area.endAddress + # print(self.deviceName, area.startAddress, area.endAddress, area.bytes, time.time()) # print(endAddress) # else: # print(endAddress) @@ -89,15 +92,19 @@ class Device(): elif inputOrOutput == 1: self.outputEndAddress = endAddress # endAddress = 0 - # print(self.deviceName, self.inputEndAddress) def editArea(self, index, type, order, bytes): - if type in ["DI", "AI"]: - self.inputAreas[self.indexDict[index]].order = order - self.inputAreas[self.indexDict[index]].bytes = bytes - elif type in ["AO", "DO"]: - self.outputAreas[self.indexDict[index]].order = order - self.outputAreas[self.indexDict[index]].bytes = bytes + # if type in ["DI", "AI"]: + # self.inputAreas[self.indexDict[index]].order = order + # self.inputAreas[self.indexDict[index]].bytes = bytes + # elif type in ["AO", "DO"]: + # self.outputAreas[self.indexDict[index]].order = order + # self.outputAreas[self.indexDict[index]].bytes = bytes + # print(bytes) + # print(self.areas[index]) + self.areas[index].order = order + self.areas[index].bytes = bytes + self.areas[index].length = int(self.areas[index].nums) * int(bytes) # self.recalculateAddress() @@ -312,6 +319,7 @@ class DevicesManange(): bytes = bytesValues[area.startAddress:area.endAddress] # print(area.startAddress, area.endAddress) if area.type in ['AI', 'AO']: + # print(area) for i in range(0, len(bytes), 4): byte = bytes[i:i+4] if len(byte) != 4: @@ -325,7 +333,7 @@ class DevicesManange(): else: bytes = bytes[::-1] area.currentValue = bytesToCoils(bytes) - print(area.currentValue, device.deviceName, area.startAddress,area.endAddress) + # print(area.currentValue, device.deviceName, area.startAddress,area.endAddress) # print() @classmethod def addAreas(self, type, order, bytes, deviceName): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a57157f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +modbus_tk==1.1.3 +numpy==1.26.4 +peewee==3.17.1 +PyQt5==5.15.10