Skip to content

Commit 903a68d

Browse files
committed
- adds two more MODBUS device channels
- adds autoCHARGE/autoDROP recognition to the IMF 'control' setup which picks up CHARGE and DROP events from the machine - adds Midnight Glow theme - adds support for MAX subscription - adds Config menu item to update machine name - moves settings menu items to Expert mode - UI updates (round elements, friendly icons, improved layout, nice tables, reworked events editor) - improved performance on theme loading - replaces QTextEdit by the faster QPlainTextEdit in various places - simplified about box - improved MODBUS/S7 scanner - removed all png icons - some code refactoring reducing duplicate code
1 parent 784effc commit 903a68d

224 files changed

Lines changed: 101109 additions & 98946 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/artisan-mac.spec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ Qt_modules = [
277277
'QtNetwork',
278278
'QtDBus',
279279
'QtPdf',
280+
'QtSvgWidgets',
280281
]
281282
Qt_frameworks = [module + '.framework' for module in Qt_modules]
282283

src/artisanlib/alarms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from artisanlib.dialogs import ArtisanResizeablDialog
4545
from artisanlib.widgets import (MyQComboBox, MyTableWidgetItemNumber, MyTableWidgetItemQCheckBox,
4646
MyTableWidgetItemQComboBox, MyTableWidgetItemQLineEdit, MyTableWidgetItemQTime)
47+
from artisanlib.table_style import horizontal_header_style
4748

4849

4950
from PyQt6.QtCore import (Qt, pyqtSlot, QSettings, QTimer)
@@ -79,6 +80,9 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
7980

8081
#table for alarms
8182
self.alarmtable = QTableWidget()
83+
horizontal_header = self.alarmtable.horizontalHeader()
84+
if horizontal_header is not None:
85+
horizontal_header.setStyleSheet(horizontal_header_style(self.aw.app.darkmode))
8286
self.alarmtable.itemSelectionChanged.connect(self.selectionChanged)
8387
allonButton = QPushButton(QApplication.translate('Button','All On'))
8488
allonButton.clicked.connect(self.alarmsAllOn)

src/artisanlib/background.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from artisanlib.util import deltaLabelUTF8, deltaLabelPrefix, stringfromseconds
3333
from artisanlib.dialogs import ArtisanResizeablDialog
3434
from artisanlib.widgets import (MyTableWidgetItemNumber)
35+
from artisanlib.table_style import horizontal_header_style, vertical_header_style
36+
3537
from typing import override, Final, TYPE_CHECKING
3638

3739
if TYPE_CHECKING:
@@ -185,6 +187,13 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
185187
#TAB 2 EVENTS
186188
#table for showing events
187189
self.eventtable = QTableWidget()
190+
horizontal_header = self.eventtable.horizontalHeader()
191+
if horizontal_header is not None:
192+
horizontal_header.setStyleSheet(horizontal_header_style(self.aw.app.darkmode))
193+
vertical_header = self.eventtable.verticalHeader()
194+
if vertical_header is not None:
195+
vertical_header.setStyleSheet(vertical_header_style(self.aw.app.darkmode))
196+
vertical_header.setDefaultAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignVCenter)
188197
self.eventtable.setTabKeyNavigation(True)
189198
self.copyeventTableButton = QPushButton(QApplication.translate('Button', 'Copy Table'))
190199
self.copyeventTableButton.setToolTip(QApplication.translate('Tooltip','Copy table to clipboard, OPTION or ALT click for tabular text'))
@@ -195,6 +204,13 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
195204
#TAB 3 DATA
196205
#table for showing data
197206
self.datatable = QTableWidget()
207+
horizontal_header = self.datatable.horizontalHeader()
208+
if horizontal_header is not None:
209+
horizontal_header.setStyleSheet(horizontal_header_style(self.aw.app.darkmode))
210+
vertical_header = self.datatable.verticalHeader()
211+
if vertical_header is not None:
212+
vertical_header.setStyleSheet(vertical_header_style(self.aw.app.darkmode))
213+
vertical_header.setDefaultAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignVCenter)
198214
self.datatable.setTabKeyNavigation(True)
199215
self.copydataTableButton = QPushButton(QApplication.translate('Button', 'Copy Table'))
200216
self.copydataTableButton.setToolTip(QApplication.translate('Tooltip','Copy table to clipboard, OPTION or ALT click for tabular text'))

src/artisanlib/canvas.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,7 @@ def __init__(self, parent:QWidget, dpi:int, locale:str, aw:'ApplicationWindow')
10161016
'+MQTT 78', #204
10171017
'+MQTT 910', #205
10181018
'+MQTT 1112', #206
1019+
'+MODBUS 1112', #207
10191020
]
10201021

10211022
# ADD DEVICE:
@@ -4492,10 +4493,9 @@ def updateLargeExtraLCDs(self, extra1:list[str|None]|None = None, extra2:list[st
44924493
_log.exception(e)
44934494

44944495

4495-
# ADD DEVICE:
4496-
44974496
# returns True if the extra device n, channel c, is of type MODBUS or S7, has no factor defined, nor any math formula, and is of type int
44984497
# channel c is either 0 or 1
4498+
# ADD DEVICE:
44994499
@functools.cache # noqa: B019 # pylint: disable=W1518 # Not relevant here, as qmc is only created once: [B019] Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks
45004500
def intChannel(self, n:int, c:int) -> bool:
45014501
if len(self.extradevices) > n:
@@ -4505,7 +4505,7 @@ def intChannel(self, n:int, c:int) -> bool:
45054505
if c == 1:
45064506
no_math_formula_defined = bool(self.extramathexpression2[n] == '')
45074507
# MODBUS channels
4508-
for idx, dev_type in enumerate([29,33,55,109,150]): # MODBUS, MODBUS_34, MODBUS_56, MODBUS_78, MODBUS_910
4508+
for idx, dev_type in enumerate([29,33,55,109,150,207]): # MODBUS, MODBUS_34, MODBUS_56, MODBUS_78, MODBUS_910, MODBUS_1112
45094509
if self.extradevices[n] == dev_type:
45104510
return ((self.aw.modbus.inputFloatsAsInt[idx*2 + c] or self.aw.modbus.inputBCDsAsInt[idx*2 + c] or not self.aw.modbus.inputFloats[idx*2 + c]) and
45114511
self.aw.modbus.inputDivs[idx*2 + c] == 0 and
@@ -5531,7 +5531,7 @@ def updateLCDs(self, time:float|None, temp1:list[float], temp2:list[float], delt
55315531
if i < self.aw.nLCDS:
55325532
try:
55335533
extra1_value = resLCD
5534-
if idx is not None and XTs1[i] and idx < len(XTs1[i]):
5534+
if idx is not None and i < len(XTs1) and idx < len(XTs1[i]):
55355535
fmt = lcdformat
55365536
v = float(XTs1[i][idx])
55375537
if v != -1:
@@ -5553,7 +5553,7 @@ def updateLCDs(self, time:float|None, temp1:list[float], temp2:list[float], delt
55535553
self.aw.extraLCD1[i].display(extra1_value)
55545554
try:
55555555
extra2_value = resLCD
5556-
if idx is not None and XTs2[i] and idx < len(XTs2[i]):
5556+
if idx is not None and i < len(XTs2) and idx < len(XTs2[i]):
55575557
fmt = lcdformat
55585558
v = float(XTs2[i][idx])
55595559
if v != -1:
@@ -9275,9 +9275,9 @@ def redraw(self, recomputeAllDeltas:bool = True, re_smooth_foreground:bool = Tru
92759275

92769276
decay_smoothing_p = (not self.optimalSmoothing) or self.flagon
92779277

9278-
scale = 1 if self.graphstyle == 1 else 0
9279-
length = 700 # 100 (128 the default)
9280-
randomness = 12 # 2 (16 default)
9278+
scale = 1.5 if self.graphstyle == 1 else 0
9279+
length = 800 # 100 (128 the default)
9280+
randomness = 16 # 2 (16 default)
92819281
rcParams['path.sketch'] = (scale, length, randomness)
92829282

92839283
# if no axis are set, we need to forceRenewAxis in any case
@@ -12574,7 +12574,6 @@ def changeGColor(self, color:int) -> None:
1257412574
self.EvalueColor = self.EvalueColor_default.copy()
1257512575
self.EvalueTextColor = self.EvalueTextColor_default.copy()
1257612576
self.aw.sendmessage(QApplication.translate('Message','Colors set to defaults'))
12577-
# self.aw.closeEventSettings()
1257812577

1257912578
elif color == 2:
1258012579
self.aw.sendmessage(QApplication.translate('Message','Colors set to grey'))
@@ -12596,7 +12595,6 @@ def changeGColor(self, color:int) -> None:
1259612595
self.backgroundxtcolor = self.aw.convertToGreyscale(self.backgroundxtcolor)
1259712596
self.backgroundytcolor = self.aw.convertToGreyscale(self.backgroundytcolor)
1259812597
self.aw.setLCDsBW()
12599-
# self.aw.closeEventSettings()
1260012598

1260112599
elif color == 3:
1260212600
from artisanlib.colors import graphColorDlg
@@ -12640,23 +12638,15 @@ def changeGColor(self, color:int) -> None:
1264012638
self.backgrounddeltabtcolor = str(dialog.bgdeltabtButton.text())
1264112639
self.backgroundxtcolor = str(dialog.bgextraButton.text())
1264212640
self.backgroundytcolor = str(dialog.bgextra2Button.text())
12643-
# self.aw.closeEventSettings()
12644-
12645-
# #deleteLater() will not work here as the dialog is still bound via the parent
12646-
# #dialog.deleteLater() # now we explicitly allow the dialog an its widgets to be GCed
12647-
# # the following will immediately release the memory despite this parent link
12648-
# QApplication.processEvents() # we ensure events concerning this dialog are processed before deletion
12649-
# try: # sip not supported on older PyQt versions (RPi!)
12650-
# sip.delete(dialog)
12651-
# #print(sip.isdeleted(dialog))
12652-
# except Exception: # pylint: disable=broad-except
12653-
# pass
12641+
# release the dialog
12642+
dialog.destroy()
12643+
del dialog
1265412644

1265512645
#update screen with new colors
1265612646
self.aw.updateCanvasColors()
1265712647
self.aw.applyStandardButtonVisibility()
1265812648
self.aw.update_extraeventbuttons_visibility()
12659-
self.fig.canvas.draw_idle() #.redraw()
12649+
self.fig.canvas.draw_idle()
1266012650

1266112651
def clearFlavorChart(self) -> None:
1266212652
self.flavorchart_plotf = None
@@ -12950,6 +12940,7 @@ def StopAsyncSamplingAction(self) -> None:
1295012940
# indicating which curves should not be temperature converted
1295112941
# True indicates a non-temperature device (data should not be converted)
1295212942
# False indicates a temperature device (data should be converted if temperature unit changes)
12943+
# ADD DEVICE:
1295312944
def generateNoneTempHints(self) -> None:
1295412945
self.extraNoneTempHint1 = []
1295512946
self.extraNoneTempHint2 = []
@@ -12969,6 +12960,12 @@ def generateNoneTempHints(self) -> None:
1296912960
elif d == 109: # +MODBUS 78
1297012961
self.extraNoneTempHint1.append(self.aw.modbus.inputModes[6] == '')
1297112962
self.extraNoneTempHint2.append(self.aw.modbus.inputModes[7] == '')
12963+
elif d == 150: # +MODBUS 910
12964+
self.extraNoneTempHint1.append(self.aw.modbus.inputModes[8] == '')
12965+
self.extraNoneTempHint2.append(self.aw.modbus.inputModes[9] == '')
12966+
elif d == 207: # +MODBUS 1112
12967+
self.extraNoneTempHint1.append(self.aw.modbus.inputModes[10] == '')
12968+
self.extraNoneTempHint2.append(self.aw.modbus.inputModes[11] == '')
1297212969
elif d == 79: # S7
1297312970
self.extraNoneTempHint1.append(not bool(self.aw.s7.mode[0]))
1297412971
self.extraNoneTempHint2.append(not bool(self.aw.s7.mode[1]))
@@ -12999,9 +12996,6 @@ def generateNoneTempHints(self) -> None:
1299912996
elif d == 119: # +WebSocket 910
1300012997
self.extraNoneTempHint1.append(not bool(self.aw.ws.channel_modes[8]))
1300112998
self.extraNoneTempHint2.append(not bool(self.aw.ws.channel_modes[9]))
13002-
elif d == 150: # +MODBUS 910
13003-
self.extraNoneTempHint1.append(self.aw.modbus.inputModes[8] == '')
13004-
self.extraNoneTempHint2.append(self.aw.modbus.inputModes[9] == '')
1300512999
elif d == 151: # +S7 1112
1300613000
self.extraNoneTempHint1.append(not bool(self.aw.s7.mode[10]))
1300713001
self.extraNoneTempHint2.append(not bool(self.aw.s7.mode[11]))

src/artisanlib/comm.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,8 @@ def __init__(self, aw:'ApplicationWindow') -> None:
591591
self.MQTT_56, #203
592592
self.MQTT_78, #204
593593
self.MQTT_910, #205
594-
self.MQTT_1112 #206
594+
self.MQTT_1112, #206
595+
self.MODBUS_1112, #207
595596
]
596597
#string with the name of the program for device #27
597598
self.externalprogram:str = 'test.py'
@@ -1664,6 +1665,9 @@ def MODBUS_78(self) -> tuple[float,float,float]:
16641665
def MODBUS_910(self) -> tuple[float,float,float]:
16651666
return self.aw.extraMODBUStx,self.aw.extraMODBUStemps[9],self.aw.extraMODBUStemps[8]
16661667

1668+
def MODBUS_1112(self) -> tuple[float,float,float]:
1669+
return self.aw.extraMODBUStx,self.aw.extraMODBUStemps[11],self.aw.extraMODBUStemps[10]
1670+
16671671
def HH802U(self) -> tuple[float,float,float]:
16681672
tx = self.aw.qmc.timeclock.elapsedMilli()
16691673
t2,t1 = self.HH806AUtemperature()

src/artisanlib/comparator.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from artisanlib.dialogs import ArtisanDialog
5252
from artisanlib.widgets import MyQComboBox
5353
from artisanlib.qcheckcombobox import CheckComboBox
54+
from artisanlib.table_style import horizontal_header_style, vertical_header_style
5455

5556
with suppress_stdout_stderr():
5657
from matplotlib import colormaps # pyrefly:ignore[missing-module-attribute]
@@ -1062,19 +1063,20 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', foreground:str|None =
10621063
settings1Layout = QHBoxLayout()
10631064
settings1Layout.addWidget(self.modeComboBox)
10641065
settings1Layout.addStretch()
1065-
settings1Layout.addSpacing(10)
1066-
settings1Layout.addStretch()
1066+
settings1Layout.addSpacing(8)
10671067
settings1Layout.addWidget(alignLabel)
1068+
settings1Layout.addSpacing(4)
10681069
settings1Layout.addWidget(self.alignComboBox)
10691070

10701071
settings2Layout = QHBoxLayout()
10711072
settings2Layout.addWidget(self.cb)
1072-
settings1Layout.addSpacing(2)
1073+
settings2Layout.addSpacing(4)
10731074
settings2Layout.addWidget(self.eventsComboBox)
10741075

10751076
settingsLayout = QVBoxLayout()
10761077
settingsLayout.addLayout(settings2Layout)
10771078
settingsLayout.addLayout(settings1Layout)
1079+
settingsLayout.setSpacing(2)
10781080

10791081
buttonLayout = QHBoxLayout()
10801082
buttonLayout.addStretch()
@@ -1575,33 +1577,33 @@ def createProfileTable(self) -> None:
15751577
vheader: QHeaderView|None = self.profileTable.verticalHeader()
15761578
if vheader is not None:
15771579
vheader.setSectionResizeMode(QHeaderView.ResizeMode.Fixed)
1580+
vheader.setSectionsMovable(True)
1581+
vheader.setDragDropMode(QTableWidget.DragDropMode.InternalMove)
1582+
vheader.sectionMoved.connect(self.sectionMoved)
1583+
vheader.sectionDoubleClicked.connect(self.tableSectionClicked)
1584+
vheader.setStyleSheet(vertical_header_style(self.aw.app.darkmode))
1585+
vheader.setDefaultAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignVCenter)
1586+
1587+
15781588
self.profileTable.setHorizontalHeaderLabels(['',
15791589
QApplication.translate('Label','ON'),
15801590
QApplication.translate('Label','Title')])
1581-
hheader: QHeaderView|None = self.profileTable.horizontalHeader()
1582-
if hheader is not None:
1583-
hheader.sectionClicked.connect(self.columnHeaderClicked)
15841591
self.profileTable.setCornerButtonEnabled(True) # click in the left header corner selects all entries in the table
15851592
self.profileTable.setSortingEnabled(False)
15861593

1587-
vheader = self.profileTable.verticalHeader()
1588-
if vheader is not None:
1589-
vheader.setSectionsMovable(True)
1590-
vheader.setDragDropMode(QTableWidget.DragDropMode.InternalMove)
1591-
vheader.sectionMoved.connect(self.sectionMoved)
1592-
vheader.sectionDoubleClicked.connect(self.tableSectionClicked)
1593-
15941594
self.profileTable.itemSelectionChanged.connect(self.selectionChanged)
15951595
self.profileTable.deleteKeyPressed.connect(self.deleteSelected)
15961596

15971597
header: QHeaderView|None = self.profileTable.horizontalHeader()
15981598
if header is not None:
1599+
header.sectionClicked.connect(self.columnHeaderClicked)
15991600
header.setStretchLastSection(True)
16001601
header.setMinimumSectionSize(10) # color column size
16011602
# header.setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
16021603
header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
16031604
header.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
16041605
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
1606+
header.setStyleSheet(horizontal_header_style(self.aw.app.darkmode))
16051607

16061608
self.profileTable.setColumnWidth(0,10) # color column size
16071609
self.profileTable.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

0 commit comments

Comments
 (0)