p3lib 1.1.98__tar.gz → 1.1.99__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {p3lib-1.1.98 → p3lib-1.1.99}/PKG-INFO +1 -1
  2. {p3lib-1.1.98 → p3lib-1.1.99}/setup.cfg +1 -1
  3. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/ngt.py +95 -65
  4. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib.egg-info/PKG-INFO +1 -1
  5. {p3lib-1.1.98 → p3lib-1.1.99}/LICENSE +0 -0
  6. {p3lib-1.1.98 → p3lib-1.1.99}/README.md +0 -0
  7. {p3lib-1.1.98 → p3lib-1.1.99}/pyproject.toml +0 -0
  8. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/__init__.py +0 -0
  9. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/ate.py +0 -0
  10. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/bokeh_auth.py +0 -0
  11. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/bokeh_gui.py +0 -0
  12. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/boot_manager.py +0 -0
  13. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/conduit.py +0 -0
  14. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/database_if.py +0 -0
  15. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/helper.py +0 -0
  16. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/json_networking.py +0 -0
  17. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/mqtt_rpc.py +0 -0
  18. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/netif.py +0 -0
  19. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/netplotly.py +0 -0
  20. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/pconfig.py +0 -0
  21. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/ssh.py +0 -0
  22. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/table_plot.py +0 -0
  23. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib/uio.py +0 -0
  24. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib.egg-info/SOURCES.txt +0 -0
  25. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib.egg-info/dependency_links.txt +0 -0
  26. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib.egg-info/requires.txt +0 -0
  27. {p3lib-1.1.98 → p3lib-1.1.99}/src/p3lib.egg-info/top_level.txt +0 -0
  28. {p3lib-1.1.98 → p3lib-1.1.99}/tests/test_conduit.py +0 -0
  29. {p3lib-1.1.98 → p3lib-1.1.99}/tests/test_json_networking.py +0 -0
  30. {p3lib-1.1.98 → p3lib-1.1.99}/tests/test_netif.py +0 -0
  31. {p3lib-1.1.98 → p3lib-1.1.99}/tests/test_ssh.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p3lib
3
- Version: 1.1.98
3
+ Version: 1.1.99
4
4
  Summary: A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
5
5
  Home-page: https://github.com/pjaos/p3lib
6
6
  Author: Paul Austen
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = p3lib
3
- version = 1.1.98
3
+ version = 1.1.99
4
4
  author = Paul Austen
5
5
  author_email = pausten.os@gmail.com
6
6
  description = A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
@@ -1,7 +1,7 @@
1
1
  # !/usr/bin/env python3
2
2
 
3
3
  """NiceGui Tools
4
- Responsible for providing helper classes for nicegui interfaces
4
+ Responsible for providing helper classes for nicegui interfaces
5
5
  aimed at reducing coding required for a GUI.
6
6
  """
7
7
 
@@ -19,9 +19,9 @@ class TabbedNiceGui(object):
19
19
  """@brief Responsible for starting the providing a tabbed GUI.
20
20
  This class is designed to ease the creation of a tabbed GUI interface.
21
21
  The contents of each tab can be defined in the subclass.
22
- The GUI includes a message log area below each tab. Tasks can send messages
22
+ The GUI includes a message log area below each tab. Tasks can send messages
23
23
  to this log area.
24
- If a subclass sets the self._logFile attributed then all messages sent to the
24
+ If a subclass sets the self._logFile attributed then all messages sent to the
25
25
  log area are written to a log file with timestamps."""
26
26
 
27
27
  # This can be used in the markdown text for a TAB description to give slightly larger text
@@ -47,7 +47,7 @@ class TabbedNiceGui(object):
47
47
  def GetDateTimeStamp():
48
48
  """@return The log file date/time stamp """
49
49
  return strftime("%Y%m%d%H%M%S", localtime()).lower()
50
-
50
+
51
51
  @staticmethod
52
52
  def GetInstallFolder():
53
53
  """@return The folder where the apps are installed."""
@@ -55,7 +55,7 @@ class TabbedNiceGui(object):
55
55
  if not os.path.isdir(installFolder):
56
56
  raise Exception(f"{installFolder} folder not found.")
57
57
  return installFolder
58
-
58
+
59
59
  @staticmethod
60
60
  def GetLogFileName(logFilePrefix):
61
61
  """@param logFilePrefix The text in the log file name before the timestamp.
@@ -63,7 +63,7 @@ class TabbedNiceGui(object):
63
63
  dateTimeStamp = TabbedNiceGui.GetDateTimeStamp()
64
64
  logFileName = f"{logFilePrefix}_{dateTimeStamp}.log"
65
65
  return logFileName
66
-
66
+
67
67
  @staticmethod
68
68
  def CheckPort(port):
69
69
  """@brief Check the server port.
@@ -72,7 +72,7 @@ class TabbedNiceGui(object):
72
72
  raise Exception("The minimum TCP port that you can bind the GUI server to is 1024.")
73
73
  if port > 65535:
74
74
  raise Exception("The maximum TCP port that you can bind the GUI server to is 65535.")
75
-
75
+
76
76
  @staticmethod
77
77
  def GetProgramVersion():
78
78
  """@brief Get the program version from the poetry pyproject.toml file.
@@ -86,7 +86,7 @@ class TabbedNiceGui(object):
86
86
  poetryConfigFile = os.path.join(cwd, TabbedNiceGui.POETRY_CONFIG_FILE)
87
87
  if not os.path.isfile(poetryConfigFile):
88
88
  raise Exception(f"{poetryConfigFile}, {poetryConfigFile2} and {poetryConfigFile} not found.")
89
-
89
+
90
90
  programVersion = None
91
91
  with open(poetryConfigFile, 'r') as fd:
92
92
  lines = fd.readlines()
@@ -144,8 +144,8 @@ class TabbedNiceGui(object):
144
144
 
145
145
  # Start ------------------------------
146
146
  # Methods that allow the GUI to display standard UIO messages
147
- # This allows the GUI to be used with code that was written
148
- # to be used on the command line using UIO class instances
147
+ # This allows the GUI to be used with code that was written
148
+ # to be used on the command line using UIO class instances
149
149
  #
150
150
  def info(self, msg):
151
151
  """@brief Send a info message to be displayed in the GUI.
@@ -160,14 +160,14 @@ class TabbedNiceGui(object):
160
160
  @param msg The message to be displayed."""
161
161
  msgDict = {TabbedNiceGui.WARN_MESSAGE: str(msg)}
162
162
  self.updateGUI(msgDict)
163
-
163
+
164
164
  def error(self, msg):
165
165
  """@brief Send a error message to be displayed in the GUI.
166
166
  This can be called from outside the GUI thread.
167
167
  @param msg The message to be displayed."""
168
168
  msgDict = {TabbedNiceGui.ERROR_MESSAGE: str(msg)}
169
169
  self.updateGUI(msgDict)
170
-
170
+
171
171
  def infoDialog(self, msg):
172
172
  """@brief Display an info level dialog.
173
173
  @param msg The message dialog."""
@@ -198,7 +198,7 @@ class TabbedNiceGui(object):
198
198
  else:
199
199
  returnText = inputObj.value
200
200
  return returnText
201
-
201
+
202
202
  def reportException(self, exception):
203
203
  """@brief Report an exception.
204
204
  If debug is enabled a full stack trace is displayed.
@@ -235,18 +235,18 @@ class TabbedNiceGui(object):
235
235
  # Check we have a table to display
236
236
  if len(table) == 0:
237
237
  raise Exception("No table rows to display")
238
-
238
+
239
239
  # Check all rows have the same number of columns in the table
240
240
  colCount = len(table[0])
241
241
  for row in table:
242
242
  if len(row) != colCount:
243
243
  raise Exception(f"{str(row)} column count different from first row ({colCount})")
244
-
244
+
245
245
  for row in table:
246
246
  for col in row:
247
247
  if not isinstance(col, str):
248
248
  raise Exception(f"Table column is not a string: {col} in {row}")
249
-
249
+
250
250
  # Get the max width for each column
251
251
  for col in range(0,colCount):
252
252
  maxWidth=0
@@ -258,10 +258,10 @@ class TabbedNiceGui(object):
258
258
  tableWidth = 1
259
259
  for columnWidth in columnWidths:
260
260
  tableWidth += columnWidth + 3 # Space each side of the column + a column divider character
261
-
261
+
262
262
  # Add the top line of the table
263
263
  self.info(rowSeparatorChar*tableWidth)
264
-
264
+
265
265
  # The starting row index
266
266
  for rowIndex in range(0, len(table)):
267
267
  rowText = colSeparatorChar
@@ -280,7 +280,7 @@ class TabbedNiceGui(object):
280
280
  def setLogFile(self, logFile):
281
281
  pass
282
282
 
283
- # End ------------------------------
283
+ # End ------------------------------
284
284
 
285
285
  def _saveLogMsg(self, msg):
286
286
  """@brief Save the message to a log file.
@@ -295,7 +295,7 @@ class TabbedNiceGui(object):
295
295
  with open(self._logFile, 'a') as fd:
296
296
  dateTimeStamp = TabbedNiceGui.GetDateTimeStamp()
297
297
  fd.write(dateTimeStamp + ": " + msg + '\n')
298
-
298
+
299
299
  def _getDisplayMsg(self, msg, prefix):
300
300
  """@brief Get the msg to display. If the msg does not already have a msg level we add one.
301
301
  @param msg The source msg.
@@ -315,7 +315,7 @@ class TabbedNiceGui(object):
315
315
  self._log.push(msg)
316
316
  self._saveLogMsg(msg)
317
317
  self._logMessageCount += 1
318
- # We've received a log message so update progress bar if required.
318
+ # We've received a log message so update progress bar if required.
319
319
  self._updateProgressBar(msg)
320
320
 
321
321
  def _infoGT(self, msg):
@@ -377,7 +377,7 @@ class TabbedNiceGui(object):
377
377
  def initGUI(self, tabNameList, tabMethodInitList, reload=True, address="0.0.0.0", port=DEFAULT_SERVER_PORT, pageTitle="NiceGUI"):
378
378
  """@brief Init the tabbed GUI.
379
379
  @param tabNameList A list of the names of each tab to be created.
380
- @param tabMethodInitList A list of the methods to be called to init each of the above tabs.
380
+ @param tabMethodInitList A list of the methods to be called to init each of the above tabs.
381
381
  The two lists must be the same size.
382
382
  @param reload If reload is set False then changes to python files will not cause the server to be restarted.
383
383
  @param address The address to bind the server to.
@@ -392,7 +392,7 @@ class TabbedNiceGui(object):
392
392
  for tabName in tabNameList:
393
393
  tabObj = ui.tab(tabName)
394
394
  tabObjList.append(tabObj)
395
-
395
+
396
396
  with ui.tab_panels(tabs, value=tabObjList[0]).classes('w-full'):
397
397
  for tabObj in tabObjList:
398
398
  with ui.tab_panel(tabObj):
@@ -425,7 +425,7 @@ class TabbedNiceGui(object):
425
425
  ui.run(host=address, port=port, title=pageTitle, dark=True, uvicorn_logging_level=guiLogLevel, reload=reload)
426
426
 
427
427
  def progressTimerCallback(self):
428
- """@brief Time to update the progress bar. We run the timer all the time because there appears to be a
428
+ """@brief Time to update the progress bar. We run the timer all the time because there appears to be a
429
429
  bug in the ui.timer instance. Calling cancel() does not appear to cancel the timer."""
430
430
  if self._updateProgressOnTimer and self._progress.visible:
431
431
  # Increment the progress bar
@@ -433,17 +433,17 @@ class TabbedNiceGui(object):
433
433
 
434
434
  def _startProgress(self, durationSeconds=0, startMessage=None, expectedMsgList=[], expectedMsgCount=0):
435
435
  """@brief Start a timer that will update the progress bar.
436
- The progress bar can simply update on a timer every second with durationSeconds set to the expected length
436
+ The progress bar can simply update on a timer every second with durationSeconds set to the expected length
437
437
  of the task.
438
438
 
439
- If startMessage is set to a text string the progress time will not start until the log message area contains
439
+ If startMessage is set to a text string the progress time will not start until the log message area contains
440
440
  the start message.
441
-
442
- Alternatively if expectedMsgList contains a list of strings we expect to receive then the progress bar is
441
+
442
+ Alternatively if expectedMsgList contains a list of strings we expect to receive then the progress bar is
443
443
  updated as each message is received. The messages may be the entire line of a log message or parts of a
444
444
  log message line.
445
445
 
446
- Alternatively if expectedMsgCount is set to a value > 0 then the progress bar is updated as each message is
446
+ Alternatively if expectedMsgCount is set to a value > 0 then the progress bar is updated as each message is
447
447
  added to the log and reaches 100% when the number of messages added to the log file reaches the expectedMsgCount.
448
448
 
449
449
  @param startMessage The text of the log message we expect to receive to trigger the progress bar timer start.
@@ -495,7 +495,7 @@ class TabbedNiceGui(object):
495
495
  if self._expectedProgressBarMsgCount > 0:
496
496
  self._progressValue = self._progressValue + self._progressStepValue
497
497
  self._progress.set_value( self._progressValue )
498
-
498
+
499
499
  # If we have a list of log messages to update the progress bar.
500
500
  elif len(self._progressBarExpectedMessageList) > 0:
501
501
  if self._expectedProgressBarMessageIndex < len(self._progressBarExpectedMessageList):
@@ -517,7 +517,7 @@ class TabbedNiceGui(object):
517
517
  """@brief Should be called before a task is started."""
518
518
  self._enableAllButtons(False)
519
519
  self._clearMessages()
520
-
520
+
521
521
  def _clearLog(self):
522
522
  """@brief Clear the log text"""
523
523
  if self._log:
@@ -526,11 +526,11 @@ class TabbedNiceGui(object):
526
526
  def _showLogMsgCount(self):
527
527
  """@brief Show the number of log messages"""
528
528
  ui.notify(f"{self._getLogMessageCount()} messages in the log.")
529
-
529
+
530
530
  def close(self):
531
531
  """@brief Close down the app server."""
532
532
  ui.notify("Press 'CTRL C' at command line or close the terminal window to quit.")
533
- # A subclass close() method can call
533
+ # A subclass close() method can call
534
534
  # app.shutdown()
535
535
  # if reload=False on ui.run()
536
536
 
@@ -557,7 +557,7 @@ class TabbedNiceGui(object):
557
557
  elif TabbedNiceGui.DEBUG_MESSAGE in rxDict:
558
558
  msg = rxDict[TabbedNiceGui.DEBUG_MESSAGE]
559
559
  self._debugGT(msg)
560
-
560
+
561
561
  elif TabbedNiceGui.ENABLE_BUTTONS in rxDict:
562
562
  state = rxDict[TabbedNiceGui.ENABLE_BUTTONS]
563
563
  self._enableAllButtons(state)
@@ -565,7 +565,7 @@ class TabbedNiceGui(object):
565
565
  elif TabbedNiceGui.NOTIFY_DIALOG in rxDict:
566
566
  message = rxDict[TabbedNiceGui.NOTIFY_DIALOG]
567
567
  ui.notify(message, close_button='OK', type="positive", position="center")
568
-
568
+
569
569
  else:
570
570
 
571
571
  self._handleGUIUpdate(rxDict)
@@ -601,47 +601,50 @@ class TabbedNiceGui(object):
601
601
 
602
602
  elif time() >= timeoutT:
603
603
  raise Exception(f"{timeout} second GUI response timeout.")
604
-
604
+
605
605
  else:
606
606
  # Don't spin to fast
607
607
  sleep(0.1)
608
-
608
+
609
609
  return rxDict
610
-
610
+
611
611
  def _handleGUIUpdate(self, rxDict):
612
612
  """@brief Process the dicts received from the GUI message queue
613
613
  that were not handled by the parent class.
614
614
  @param rxDict The dict received from the GUI message queue."""
615
615
  raise NotImplementedError("_handleGUIUpdate() is not implemented. Implement this method in a subclass of TabbedNiceGUI")
616
-
616
+
617
617
 
618
618
  class YesNoDialog(object):
619
619
  """@brief Responsible for displaying a dialog box to the user with a boolean (I.E yes/no, ok/cancel) response."""
620
- TEXT_INPUT_FIELD_TYPE = 1
621
- NUMBER_INPUT_FIELD_TYPE = 2
622
- SWITCH_INPUT_FIELD_TYPE = 3
623
- DROPDOWN_INPUT_FIELD = 4
624
- COLOR_INPUT_FIELD = 5
625
- DATE_INPUT_FIELD = 6
626
- TIME_INPUT_FIELD = 7
627
- KNOB_INPUT_FIELD = 8
628
- VALID_FIELD_TYPE_LIST = (TEXT_INPUT_FIELD_TYPE,
629
- NUMBER_INPUT_FIELD_TYPE,
630
- SWITCH_INPUT_FIELD_TYPE,
620
+ TEXT_INPUT_FIELD_TYPE = 1
621
+ NUMBER_INPUT_FIELD_TYPE = 2
622
+ SWITCH_INPUT_FIELD_TYPE = 3
623
+ DROPDOWN_INPUT_FIELD = 4
624
+ COLOR_INPUT_FIELD = 5
625
+ DATE_INPUT_FIELD = 6
626
+ TIME_INPUT_FIELD = 7
627
+ KNOB_INPUT_FIELD = 8
628
+ HOUR_MIN_INPUT_FIELD_TYPE = 9
629
+ VALID_FIELD_TYPE_LIST = (TEXT_INPUT_FIELD_TYPE,
630
+ NUMBER_INPUT_FIELD_TYPE,
631
+ SWITCH_INPUT_FIELD_TYPE,
631
632
  DROPDOWN_INPUT_FIELD,
632
633
  COLOR_INPUT_FIELD,
633
634
  DATE_INPUT_FIELD,
634
635
  TIME_INPUT_FIELD,
635
- KNOB_INPUT_FIELD)
636
+ KNOB_INPUT_FIELD,
637
+ HOUR_MIN_INPUT_FIELD_TYPE)
636
638
 
637
639
  FIELD_TYPE_KEY = "FIELD_TYPE_KEY" # The type of field to be displayed.
638
- VALUE_KEY = "VALUE_KEY" # The value to be displayed in the field when the dialog is displayed.
640
+ VALUE_KEY = "VALUE_KEY" # The value to be displayed in the field when the dialog is displayed.
639
641
  MIN_NUMBER_KEY = "MIN_NUMBER_KEY" # If the type is NUMBER_INPUT_FIELD_TYPE, the min value that can be entered.
640
642
  MAX_NUMBER_KEY = "MAX_NUMBER_KEY" # If the type is NUMBER_INPUT_FIELD_TYPE, the max value that can be entered.
641
643
  WIDGET_KEY = "WIDGET_KEY" # The key to the GUI widget (E.G ui.input, ui.number etc)
642
644
  OPTIONS_KEY = "OPTIONS_KEY" # Some input fields require a list of options (E.G DROPDOWN_INPUT_FIELD).
645
+ STEP_KEY = "STEP_KEY" # The step size for numerical input fields
643
646
 
644
- def __init__(self,
647
+ def __init__(self,
645
648
  prompt,
646
649
  successMethod,
647
650
  failureMethod=None,
@@ -656,7 +659,7 @@ class YesNoDialog(object):
656
659
  self._successMethod = None # The method to be called when the success button is selected.
657
660
  self._failureMethod = None # The method to be called when the failure button is selected.
658
661
  self._inputFieldDict = {} # A dict of input field details to be included in the dialog. Can be left as an empty dict if no input fields are required.
659
- # The key in this dict is the name of the input field that the user sees.
662
+ # The key in this dict is the name of the input field that the user sees.
660
663
  # The value in this dict is another dict containing details of the input field which may be
661
664
 
662
665
  self.setPrompt(prompt)
@@ -666,13 +669,14 @@ class YesNoDialog(object):
666
669
  self.setFailureButtonLabel(failureButtonText)
667
670
 
668
671
 
669
- def addField(self, name, fieldType, value=None, minNumber=None, maxNumber=None, options=None):
672
+ def addField(self, name, fieldType, value=None, minNumber=None, maxNumber=None, options=None, step=1):
670
673
  """@brief Add a field to the dialog.
671
674
  @param name The name of the field to be added.
672
675
  @param fieldType The type of field to be entered.
673
676
  @param value The optional initial value for the field when the dialog is displayed.
674
677
  @param minNumber The optional min value if the fieldType = NUMBER_INPUT_FIELD_TYPE.
675
678
  @param maxNumber The optional max value if the fieldType = NUMBER_INPUT_FIELD_TYPE.
679
+ @param step The step size for numerical input fields.
676
680
  """
677
681
  if name and len(name) > 0:
678
682
  if fieldType in YesNoDialog.VALID_FIELD_TYPE_LIST:
@@ -680,14 +684,15 @@ class YesNoDialog(object):
680
684
  YesNoDialog.VALUE_KEY: value,
681
685
  YesNoDialog.MIN_NUMBER_KEY: minNumber,
682
686
  YesNoDialog.MAX_NUMBER_KEY: maxNumber,
683
- YesNoDialog.OPTIONS_KEY: options}
687
+ YesNoDialog.OPTIONS_KEY: options,
688
+ YesNoDialog.STEP_KEY: step}
684
689
 
685
690
  else:
686
691
  raise Exception(f"YesNoDialog.addField() {fieldType} is an invalid field type.")
687
692
 
688
693
  else:
689
694
  raise Exception("YesNoDialog.addField() name not set.")
690
-
695
+
691
696
  def _init(self):
692
697
  """@brief Init the dialog."""
693
698
  with ui.dialog() as self._dialog, ui.card():
@@ -701,11 +706,13 @@ class YesNoDialog(object):
701
706
  value = self._inputFieldDict[fieldName][YesNoDialog.VALUE_KEY]
702
707
  min = self._inputFieldDict[fieldName][YesNoDialog.MIN_NUMBER_KEY]
703
708
  max = self._inputFieldDict[fieldName][YesNoDialog.MAX_NUMBER_KEY]
704
- widget = ui.number(label=fieldName,
705
- value=value,
706
- min=min,
707
- max=max).style('width: 200px;')
708
-
709
+ step = self._inputFieldDict[fieldName][YesNoDialog.STEP_KEY]
710
+ widget = ui.number(label=fieldName,
711
+ value=value,
712
+ min=min,
713
+ max=max,
714
+ step=step).style('width: 200px;')
715
+
709
716
  elif fieldType == YesNoDialog.SWITCH_INPUT_FIELD_TYPE:
710
717
  widget = ui.switch(fieldName)
711
718
 
@@ -717,7 +724,7 @@ class YesNoDialog(object):
717
724
  widget.tooltip(fieldName)
718
725
  else:
719
726
  raise Exception("BUG: DROPDOWN_INPUT_FIELD defined without defining the options.")
720
-
727
+
721
728
  elif fieldType == YesNoDialog.COLOR_INPUT_FIELD:
722
729
  widget = ui.color_input(label=fieldName)
723
730
 
@@ -733,6 +740,10 @@ class YesNoDialog(object):
733
740
  widget = ui.knob(show_value=True)
734
741
  widget.tooltip(fieldName)
735
742
 
743
+ elif fieldType == YesNoDialog.HOUR_MIN_INPUT_FIELD_TYPE:
744
+ widget = self._get_input_time_field(fieldName)
745
+ widget.tooltip(fieldName)
746
+
736
747
  # Save a ref to the widet in the field dict
737
748
  self._inputFieldDict[fieldName][YesNoDialog.WIDGET_KEY] = widget
738
749
 
@@ -745,6 +756,25 @@ class YesNoDialog(object):
745
756
  ui.button(self._successButtonText, on_click=self._internalSuccessMethod)
746
757
  ui.button(self._failureButtonText, on_click=self._internalFailureMethod)
747
758
 
759
+ def _get_input_time_field(self, label):
760
+ """@brief Add a control to allow the user to enter the time as an hour and min.
761
+ @param label The label for the time field.
762
+ @return The input field containing the hour and minute entered."""
763
+ # Put this off the bottom of the mobile screen as most times it will not be needed
764
+ # and there is not enough room on the mobile screen above the plot pane.
765
+ with ui.row().classes('w-full'):
766
+ ui.label(label)
767
+ with ui.row().classes('w-full'):
768
+ time_input = ui.input("Time (HH:MM)")
769
+ with time_input as time:
770
+ with ui.menu().props('no-parent-event') as menu:
771
+ with ui.time().bind_value(time):
772
+ with ui.row().classes('justify-end'):
773
+ ui.button('Close', on_click=menu.close).props('flat')
774
+ with time.add_slot('append'):
775
+ ui.icon('access_time').on('click', menu.open).classes('cursor-pointer')
776
+ return time_input
777
+
748
778
  def setPrompt(self, prompt):
749
779
  """@brief Set the user prompt.
750
780
  @param prompt The user prompt."""
@@ -769,7 +799,7 @@ class YesNoDialog(object):
769
799
  """@brief Set the text of the failure button.
770
800
  @param label The failure button text."""
771
801
  self._failureButtonText = label
772
-
802
+
773
803
  def show(self):
774
804
  """@brief Allow the user to select yes/no, ok/cancel etc in response to a question."""
775
805
  self._init()
@@ -796,7 +826,7 @@ class YesNoDialog(object):
796
826
  widget = self._inputFieldDict[fieldName][YesNoDialog.WIDGET_KEY]
797
827
  if hasattr(widget, 'value'):
798
828
  self._inputFieldDict[fieldName][YesNoDialog.VALUE_KEY] = self._inputFieldDict[fieldName][YesNoDialog.WIDGET_KEY].value
799
- # If defined call the method
829
+ # If defined call the method
800
830
  if self._successMethod:
801
831
  self._successMethod()
802
832
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p3lib
3
- Version: 1.1.98
3
+ Version: 1.1.99
4
4
  Summary: A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
5
5
  Home-page: https://github.com/pjaos/p3lib
6
6
  Author: Paul Austen
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes