20
20
import re
21
21
22
22
try :
23
- from PyQt6 .QtCore import Qt , QEvent , QSettings , pyqtSlot , QRegularExpression # @UnusedImport @Reimport @UnresolvedImport
23
+ from PyQt6 .QtCore import Qt , QEvent , QSettings , pyqtSlot , pyqtSignal , QRegularExpression # @UnusedImport @Reimport @UnresolvedImport
24
24
from PyQt6 .QtWidgets import (QApplication , QWidget , QDialog , QMessageBox , QDialogButtonBox , QTextEdit , # @UnusedImport @Reimport @UnresolvedImport
25
- QHBoxLayout , QVBoxLayout , QLabel , QLineEdit , QLayout ) # @UnusedImport @Reimport @UnresolvedImport
25
+ QHBoxLayout , QVBoxLayout , QLabel , QLineEdit , QLayout , QTableWidget , QHeaderView , QPushButton ) # @UnusedImport @Reimport @UnresolvedImport
26
26
from PyQt6 .QtGui import QKeySequence , QAction , QIntValidator , QTextCharFormat , QTextCursor , QColor # @UnusedImport @Reimport @UnresolvedImport
27
27
except ImportError :
28
- from PyQt5 .QtCore import Qt , QEvent , QSettings , pyqtSlot , QRegularExpression # type: ignore # @UnusedImport @Reimport @UnresolvedImport
28
+ from PyQt5 .QtCore import Qt , QEvent , QSettings , pyqtSlot , pyqtSignal , QRegularExpression # type: ignore # @UnusedImport @Reimport @UnresolvedImport
29
29
from PyQt5 .QtWidgets import (QApplication , QWidget , QAction , QDialog , QMessageBox , QDialogButtonBox , QTextEdit , # type: ignore # @UnusedImport @Reimport @UnresolvedImport
30
- QHBoxLayout , QVBoxLayout , QLabel , QLineEdit , QLayout ) # @UnusedImport @Reimport @UnresolvedImport
30
+ QHBoxLayout , QVBoxLayout , QLabel , QLineEdit , QLayout , QTableWidget , QHeaderView , QPushButton ) # @UnusedImport @Reimport @UnresolvedImport
31
31
from PyQt5 .QtGui import QKeySequence , QIntValidator , QTextCharFormat , QTextCursor , QColor # type: ignore # @UnusedImport @Reimport @UnresolvedImport
32
32
33
- from artisanlib .widgets import MyQComboBox
33
+ from artisanlib .widgets import MyQComboBox , ClickableQLineEdit
34
+ from artisanlib .util import comma2dot
34
35
35
- from typing import Optional , List , Tuple , TYPE_CHECKING
36
+ from typing import Optional , List , Tuple , cast , Callable , TYPE_CHECKING
36
37
from typing import Final # Python <=3.7
37
38
if TYPE_CHECKING :
38
39
from artisanlib .main import ApplicationWindow # pylint: disable=unused-import
@@ -179,15 +180,15 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', title:str = '', conte
179
180
# Initialize search state variables
180
181
self .matches : List [QTextCursor ] = []
181
182
self .current_match_index = 0
182
- self .previous_search_term = ""
183
+ self .previous_search_term = ''
183
184
184
185
# Search bar
185
186
self .search_input = QLineEdit ()
186
187
self .search_input .setPlaceholderText (QApplication .translate ('Label' , 'Enter text to search' ))
187
188
188
189
# Connect Enter key to search and navigate results
189
190
self .search_input .returnPressed .connect (self .doSearch )
190
-
191
+
191
192
# Show only the ArtisanDialog standard OK button
192
193
self .dialogbuttons .removeButton (self .dialogbuttons .button (QDialogButtonBox .StandardButton .Cancel ))
193
194
@@ -251,31 +252,31 @@ def doSearch(self) -> None:
251
252
tc = self .phelp .textCursor ()
252
253
tc .clearSelection ()
253
254
self .phelp .setTextCursor (tc )
254
-
255
+
255
256
search_term = self .search_input .text ().strip ()
256
-
257
+
257
258
# Clear highlights and state when search term is empty
258
- if search_term == "" :
259
+ if search_term == '' :
259
260
self .matches = []
260
- self .previous_search_term = ""
261
+ self .previous_search_term = ''
261
262
return
262
263
263
264
# Do a fresh search when a new search_term is entered
264
265
if self .previous_search_term .lower () != search_term .lower ():
265
266
self .previous_search_term = search_term
266
267
self .current_match_index = 0
267
268
self .matches = []
268
-
269
+
269
270
# Create a case-insensitive regular expression.
270
271
regex = QRegularExpression (re .escape (search_term ))
271
272
regex .setPatternOptions (QRegularExpression .PatternOption .CaseInsensitiveOption )
272
-
273
+
273
274
# Start at the beginning of the document.
274
275
cursor = self .phelp .textCursor ()
275
276
cursor .movePosition (QTextCursor .MoveOperation .Start )
276
-
277
+
277
278
# Collect all matches.
278
- for _ in range (500000 ): # arbitrarily large limit, better than while True, should always exit via break
279
+ for _ in range (500000 ): # arbitrarily large limit, better than while True, should always exit via break
279
280
found = self .phelp .document ().find (regex , cursor ) # type: ignore #self.phelp.document() will never be None
280
281
if found .isNull ():
281
282
break
@@ -293,10 +294,10 @@ def doSearch(self) -> None:
293
294
self .current_match_index = (self .current_match_index + 1 ) % len (self .matches )
294
295
295
296
extraSelections = []
296
- match_text = " black"
297
- current_match_highlight = " #A6FF00"
298
- extra_matches_highlight = " yellow"
299
-
297
+ match_text = ' black'
298
+ current_match_highlight = ' #A6FF00'
299
+ extra_matches_highlight = ' yellow'
300
+
300
301
if self .matches :
301
302
# Highlight all matches, the current match in current_match_highlight and all others in extra_matches_highlight
302
303
for i , matchCursor in enumerate (self .matches ):
@@ -309,7 +310,7 @@ def doSearch(self) -> None:
309
310
else :
310
311
fmt .setBackground (QColor (extra_matches_highlight ))
311
312
if self .aw .app .darkmode :
312
- fmt .setForeground (QColor (" black" ))
313
+ fmt .setForeground (QColor (' black' ))
313
314
selection .format = fmt
314
315
extraSelections .append (selection )
315
316
# Move the visible cursor to the current match and clear its active selection
@@ -578,3 +579,153 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', value:int, value_min:
578
579
def accept (self ) -> None :
579
580
self .value = int (self .valueEdit .text ())
580
581
super ().accept ()
582
+
583
+
584
+ ##########################################################################
585
+ ##################### VIEW Tare ########################################
586
+ ##########################################################################
587
+
588
+
589
+ class tareDlg (ArtisanDialog ):
590
+ tare_updated_signal = pyqtSignal () # signalled after tare data table got updated
591
+
592
+ def __init__ (self , parent :ArtisanDialog , aw :'ApplicationWindow' , get_scale_weight : Callable [[], Optional [float ]]) -> None :
593
+ super ().__init__ (parent , aw )
594
+ self .parent_dialog = parent
595
+ self .get_scale_weight = get_scale_weight
596
+ self .setModal (True )
597
+ self .setWindowTitle (QApplication .translate ('Form Caption' ,'Containers' ))
598
+
599
+ self .taretable = QTableWidget ()
600
+ self .taretable .setTabKeyNavigation (True )
601
+ self .createTareTable ()
602
+
603
+ self .taretable .itemSelectionChanged .connect (self .selectionChanged )
604
+
605
+ addButton = QPushButton (QApplication .translate ('Button' ,'Add' ))
606
+ addButton .setFocusPolicy (Qt .FocusPolicy .NoFocus )
607
+ self .delButton = QPushButton (QApplication .translate ('Button' ,'Delete' ))
608
+ self .delButton .setDisabled (True )
609
+ self .delButton .setFocusPolicy (Qt .FocusPolicy .NoFocus )
610
+
611
+ addButton .clicked .connect (self .addTare )
612
+ self .delButton .clicked .connect (self .delTare )
613
+
614
+ okButton = QPushButton (QApplication .translate ('Button' ,'OK' ))
615
+ cancelButton = QPushButton (QApplication .translate ('Button' ,'Cancel' ))
616
+ cancelButton .setFocusPolicy (Qt .FocusPolicy .NoFocus )
617
+ okButton .clicked .connect (self .accept )
618
+ cancelButton .clicked .connect (self .reject )
619
+ contentbuttonLayout = QHBoxLayout ()
620
+ contentbuttonLayout .addStretch ()
621
+ contentbuttonLayout .addWidget (addButton )
622
+ contentbuttonLayout .addWidget (self .delButton )
623
+ contentbuttonLayout .addStretch ()
624
+
625
+ buttonLayout = QHBoxLayout ()
626
+ buttonLayout .addStretch ()
627
+ buttonLayout .addWidget (cancelButton )
628
+ buttonLayout .addWidget (okButton )
629
+ layout = QVBoxLayout ()
630
+ layout .addWidget (self .taretable )
631
+ layout .addLayout (contentbuttonLayout )
632
+ layout .addLayout (buttonLayout )
633
+ self .setLayout (layout )
634
+ self .setMinimumWidth (230 )
635
+ self .setMinimumHeight (250 )
636
+
637
+ @pyqtSlot ()
638
+ def selectionChanged (self ) -> None :
639
+ if len (self .taretable .selectedRanges ()) > 0 :
640
+ self .delButton .setDisabled (False )
641
+ else :
642
+ self .delButton .setDisabled (False )
643
+
644
+ @pyqtSlot ()
645
+ def accept (self ) -> None :
646
+ self .saveTareTable ()
647
+ self .tare_updated_signal .emit ()
648
+ self .close ()
649
+ super ().accept ()
650
+
651
+ def setTableRow (self , row :int , name :str , weight :float ) -> None :
652
+ name_widget = QLineEdit ()
653
+ name_widget .setAlignment (Qt .AlignmentFlag .AlignRight )
654
+ name_widget .setText (name )
655
+ weight_widget = ClickableQLineEdit ()
656
+ weight_widget .setAlignment (Qt .AlignmentFlag .AlignRight )
657
+ weight_widget .setText (str (weight ))
658
+ weight_widget .setValidator (QIntValidator (0 ,1999 , weight_widget ))
659
+ weight_widget .editingFinished .connect (self .weightEdited )
660
+ self .taretable .setCellWidget (row , 0 , name_widget )
661
+ self .taretable .setCellWidget (row , 1 , weight_widget )
662
+
663
+ @pyqtSlot (bool )
664
+ def addTare (self , _ :bool = False ) -> None :
665
+ rows = self .taretable .rowCount ()
666
+ self .taretable .setRowCount (rows + 1 )
667
+ weight = self .get_scale_weight () # read value from scale in 'g' (or None)
668
+ if weight is None or weight < 0 :
669
+ weight = 0
670
+ #add widgets to the table
671
+ self .setTableRow (rows , QApplication .translate ('Label' , 'container' ), weight )
672
+
673
+ @pyqtSlot (bool )
674
+ def delTare (self , _ :bool = False ) -> None :
675
+ selected = self .taretable .selectedRanges ()
676
+ if len (selected ) > 0 :
677
+ bindex = selected [0 ].topRow ()
678
+ if bindex >= 0 :
679
+ self .taretable .removeRow (bindex )
680
+
681
+ def saveTareTable (self ) -> None :
682
+ tars = self .taretable .rowCount ()
683
+ names = []
684
+ weights = []
685
+ for i in range (tars ):
686
+ nameWidget = cast (QLineEdit , self .taretable .cellWidget (i ,0 ))
687
+ name = nameWidget .text ()
688
+ weightWidget = cast (QLineEdit , self .taretable .cellWidget (i ,1 ))
689
+ weight = 0
690
+ try :
691
+ weight = int (round (float (comma2dot (weightWidget .text ()))))
692
+ except Exception : # pylint: disable=broad-except
693
+ pass
694
+ names .append (name )
695
+ weights .append (weight )
696
+ self .aw .qmc .container_names = names
697
+ self .aw .qmc .container_weights = weights
698
+
699
+ @pyqtSlot ()
700
+ def weightEdited (self ) -> None :
701
+ _log .debug ('PRINT weightEdited' )
702
+ sender = self .sender ()
703
+ if sender and isinstance (sender , QLineEdit ):
704
+ text = sender .text ().strip ()
705
+ if text == '' :
706
+ w = self .get_scale_weight () # read value from scale in 'g'
707
+ sender .setText (str (w if w is not None and w > 0 else 0 ))
708
+
709
+ def createTareTable (self ) -> None :
710
+ self .taretable .clear ()
711
+ self .taretable .setRowCount (len (self .aw .qmc .container_names ))
712
+ self .taretable .setColumnCount (2 )
713
+ self .taretable .setHorizontalHeaderLabels ([QApplication .translate ('Table' ,'Name' ),
714
+ QApplication .translate ('Table' ,'Weight' )])
715
+ self .taretable .setAlternatingRowColors (True )
716
+ self .taretable .setEditTriggers (QTableWidget .EditTrigger .NoEditTriggers )
717
+ self .taretable .setSelectionBehavior (QTableWidget .SelectionBehavior .SelectRows )
718
+ self .taretable .setSelectionMode (QTableWidget .SelectionMode .SingleSelection )
719
+ self .taretable .setShowGrid (True )
720
+ vheader : Optional [QHeaderView ] = self .taretable .verticalHeader ()
721
+ if vheader is not None :
722
+ vheader .setSectionResizeMode (QHeaderView .ResizeMode .Fixed )
723
+ for i , cn in enumerate (self .aw .qmc .container_names ):
724
+ #add widgets to the table
725
+ self .setTableRow (i , cn , self .aw .qmc .container_weights [i ])
726
+
727
+ header : Optional [QHeaderView ] = self .taretable .horizontalHeader ()
728
+ if header is not None :
729
+ header .setSectionResizeMode (0 , QHeaderView .ResizeMode .Stretch )
730
+ header .setSectionResizeMode (1 , QHeaderView .ResizeMode .Fixed )
731
+ self .taretable .setColumnWidth (1 ,65 )
0 commit comments