meerk40t 0.9.7051__py2.py3-none-any.whl → 0.9.7900__py2.py3-none-any.whl

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 (68) hide show
  1. meerk40t/balormk/controller.py +3 -3
  2. meerk40t/balormk/device.py +7 -0
  3. meerk40t/balormk/driver.py +23 -14
  4. meerk40t/balormk/galvo_commands.py +18 -3
  5. meerk40t/balormk/gui/balorconfig.py +6 -0
  6. meerk40t/balormk/livelightjob.py +36 -14
  7. meerk40t/camera/camera.py +1 -0
  8. meerk40t/camera/gui/camerapanel.py +154 -58
  9. meerk40t/camera/plugin.py +46 -5
  10. meerk40t/core/elements/branches.py +90 -20
  11. meerk40t/core/elements/elements.py +59 -37
  12. meerk40t/core/elements/trace.py +10 -6
  13. meerk40t/core/node/node.py +2 -0
  14. meerk40t/core/plotplanner.py +7 -4
  15. meerk40t/device/gui/defaultactions.py +78 -14
  16. meerk40t/dxf/dxf_io.py +42 -0
  17. meerk40t/grbl/controller.py +245 -35
  18. meerk40t/grbl/device.py +102 -26
  19. meerk40t/grbl/driver.py +8 -2
  20. meerk40t/grbl/gui/grblconfiguration.py +6 -0
  21. meerk40t/grbl/gui/grblcontroller.py +1 -1
  22. meerk40t/gui/about.py +7 -0
  23. meerk40t/gui/choicepropertypanel.py +20 -30
  24. meerk40t/gui/devicepanel.py +27 -16
  25. meerk40t/gui/icons.py +15 -0
  26. meerk40t/gui/laserpanel.py +102 -54
  27. meerk40t/gui/materialtest.py +10 -0
  28. meerk40t/gui/mkdebug.py +268 -9
  29. meerk40t/gui/navigationpanels.py +65 -7
  30. meerk40t/gui/propertypanels/operationpropertymain.py +185 -91
  31. meerk40t/gui/scenewidgets/elementswidget.py +7 -1
  32. meerk40t/gui/scenewidgets/selectionwidget.py +24 -9
  33. meerk40t/gui/simulation.py +1 -1
  34. meerk40t/gui/statusbarwidgets/shapepropwidget.py +50 -40
  35. meerk40t/gui/statusbarwidgets/statusbar.py +2 -2
  36. meerk40t/gui/toolwidgets/toolmeasure.py +1 -1
  37. meerk40t/gui/toolwidgets/toolnodeedit.py +4 -1
  38. meerk40t/gui/toolwidgets/tooltabedit.py +9 -7
  39. meerk40t/gui/wxmeerk40t.py +2 -0
  40. meerk40t/gui/wxmmain.py +23 -9
  41. meerk40t/gui/wxmribbon.py +36 -0
  42. meerk40t/gui/wxutils.py +66 -42
  43. meerk40t/kernel/inhibitor.py +120 -0
  44. meerk40t/kernel/kernel.py +38 -0
  45. meerk40t/lihuiyu/controller.py +33 -3
  46. meerk40t/lihuiyu/device.py +99 -4
  47. meerk40t/lihuiyu/driver.py +62 -5
  48. meerk40t/lihuiyu/gui/lhycontrollergui.py +69 -24
  49. meerk40t/lihuiyu/gui/lhydrivergui.py +6 -0
  50. meerk40t/lihuiyu/laserspeed.py +17 -10
  51. meerk40t/lihuiyu/parser.py +23 -0
  52. meerk40t/main.py +1 -1
  53. meerk40t/moshi/gui/moshidrivergui.py +7 -0
  54. meerk40t/newly/controller.py +3 -2
  55. meerk40t/newly/device.py +23 -2
  56. meerk40t/newly/driver.py +8 -3
  57. meerk40t/newly/gui/newlyconfig.py +7 -0
  58. meerk40t/ruida/gui/ruidaconfig.py +7 -0
  59. meerk40t/tools/geomstr.py +68 -48
  60. meerk40t/tools/rasterplotter.py +0 -5
  61. meerk40t/tools/ttfparser.py +155 -82
  62. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/METADATA +1 -1
  63. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/RECORD +68 -67
  64. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/LICENSE +0 -0
  65. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/WHEEL +0 -0
  66. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/entry_points.txt +0 -0
  67. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/top_level.txt +0 -0
  68. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/zip-safe +0 -0
meerk40t/grbl/device.py CHANGED
@@ -467,6 +467,18 @@ class GRBLDevice(Service, Status):
467
467
  "conditional": (self, "use_red_dot"),
468
468
  "section": "_10_Red Dot",
469
469
  },
470
+ {
471
+ "attr": "use_red_dot_for_outline",
472
+ "object": self,
473
+ "default": True,
474
+ "type": bool,
475
+ "label": _("Active during Outline"),
476
+ "tip": _(
477
+ "If active then the red dot will automatically be used if you outline the burn area"
478
+ ),
479
+ "conditional": (self, "use_red_dot"),
480
+ "section": "_10_Red Dot",
481
+ },
470
482
  {
471
483
  "attr": "max_vector_speed",
472
484
  "object": self,
@@ -879,7 +891,14 @@ class GRBLDevice(Service, Status):
879
891
  self("bind w +yforward")
880
892
 
881
893
  @self.console_option(
882
- "strength", "s", type=int, help="Set the dot laser strength."
894
+ "strength", "s", type=int, help=_("Set the dot laser strength (0..1000).")
895
+ )
896
+ @self.console_option(
897
+ "force",
898
+ "f",
899
+ type=bool,
900
+ action="store_true",
901
+ help=_("Force the red dot command even if a job is running."),
883
902
  )
884
903
  @self.console_argument("off", type=str)
885
904
  @self.console_command(
@@ -887,40 +906,46 @@ class GRBLDevice(Service, Status):
887
906
  help=_("Turns redlight on/off"),
888
907
  )
889
908
  def red_dot_on(
890
- command, channel, _, off=None, strength=None, remainder=None, **kwgs
909
+ command,
910
+ channel,
911
+ _,
912
+ off=None,
913
+ strength=None,
914
+ force=None,
915
+ remainder=None,
916
+ **kwgs,
891
917
  ):
918
+ if force is None:
919
+ force = False
892
920
  if not self.use_red_dot:
893
- channel("Red Dot feature is not enabled, see config")
921
+ channel(_("Red Dot feature is not enabled, see config"))
894
922
  # self.redlight_preferred = False
895
923
  return
896
- if not self.spooler.is_idle:
897
- channel("Won't interfere with a running job, abort...")
924
+ if not force and not self.spooler.is_idle:
925
+ channel(_("Won't interfere with a running job, abort..."))
898
926
  return
899
927
  if strength is not None:
900
928
  if 0 <= strength <= 1000:
901
929
  self.red_dot_level = strength
902
930
  channel(
903
- f"Laser strength for red dot is now: {self.red_dot_level/10.0}%"
931
+ _("Laser strength for red dot is now: {power}").format(
932
+ power=f"{self.red_dot_level / 10.0:.1f}%"
933
+ )
934
+ )
935
+ else:
936
+ channel(
937
+ _(
938
+ "Laser strength for red dot must be between 0 and 1000, not {power}"
939
+ ).format(power=strength)
904
940
  )
941
+ return
905
942
  if off == "off":
906
- self.driver.laser_off()
907
- # self.driver.grbl("G0")
908
- self.driver.move_mode = 0
909
- # self.redlight_preferred = False
910
- channel("Turning off redlight.")
943
+ self.red_dot(False)
944
+ channel(_("Red light is now off."))
911
945
  self.signal("grbl_red_dot", False)
912
946
  else:
913
- # self.redlight_preferred = True
914
- # self.driver.set("power", int(self.red_dot_level / 100 * 1000))
915
- self.driver._clean()
916
- rapid_speed = self.setting(float, "rapid_speed", 600.0)
917
- self.driver.laser_on(power=int(self.red_dot_level), speed=rapid_speed)
918
- # By default, any move is a G0 move which will not activate the laser,
919
- # so we need to switch to G1 mode:
920
- self.driver.move_mode = 1
921
- # An arbitrary move to turn the laser really on!
922
- # self.driver.grbl("G1")
923
- channel("Turning on redlight.")
947
+ self.red_dot(True)
948
+ channel(_("Red light is now on."))
924
949
  self.signal("grbl_red_dot", True)
925
950
 
926
951
  @self.console_option(
@@ -929,12 +954,15 @@ class GRBLDevice(Service, Status):
929
954
  action="store_true",
930
955
  help=_("override one second laser fire pulse duration"),
931
956
  )
957
+ @self.console_option("power", "p", type=str, help=_("laser fire power"))
932
958
  @self.console_argument("time", type=float, help=_("laser fire pulse duration"))
933
959
  @self.console_command(
934
960
  "pulse",
935
961
  help=_("pulse <time>: Pulse the laser in place."),
936
962
  )
937
- def pulse(command, channel, _, time=None, idonotlovemyhouse=False, **kwgs):
963
+ def pulse(
964
+ command, channel, _, time=None, power=None, idonotlovemyhouse=False, **kwgs
965
+ ):
938
966
  if time is None:
939
967
  channel(_("Must specify a pulse time in milliseconds."))
940
968
  return
@@ -949,12 +977,25 @@ class GRBLDevice(Service, Status):
949
977
  return
950
978
  except IndexError:
951
979
  return
952
-
980
+ if power is not None:
981
+ try:
982
+ if power.endswith("%"):
983
+ power = power[:-1]
984
+ power = float(power) * 10
985
+ else:
986
+ power = float(power)
987
+ except ValueError:
988
+ channel(_("Power must be valid value."))
989
+ return
990
+ if not (0 <= power <= 1000):
991
+ channel(_("Power must be between 0 and 1000."))
992
+ return
993
+ power = 1000 if power is None else int(power)
953
994
  if self.spooler.is_idle:
954
- self.driver.laser_on(power=1000, speed=1000)
995
+ self.driver.laser_on(power=power, speed=1000)
955
996
  sleep(time / 1000)
956
997
  self.driver.laser_off()
957
- label = _("Pulse laser for {time}ms").format(time=time)
998
+ label = _("Pulse laser for {time}ms").format(time=time) + f" [{power}]"
958
999
  channel(label)
959
1000
  else:
960
1001
  channel(_("Pulse laser failed: Busy"))
@@ -1044,6 +1085,13 @@ class GRBLDevice(Service, Status):
1044
1085
  name = self.label.replace(" ", "-")
1045
1086
  return name.replace("/", "-")
1046
1087
 
1088
+ @property
1089
+ def supports_pwm(self):
1090
+ """
1091
+ Returns whether this device supports PWM.
1092
+ """
1093
+ return True
1094
+
1047
1095
  def _register_console_serial(self):
1048
1096
  _ = self.kernel.translation
1049
1097
 
@@ -1169,3 +1217,31 @@ class GRBLDevice(Service, Status):
1169
1217
  return {
1170
1218
  "gantry": True,
1171
1219
  }
1220
+
1221
+ def red_dot(self, turn_on):
1222
+ if turn_on:
1223
+ # self.redlight_preferred = True
1224
+ # self.driver.set("power", int(self.red_dot_level / 100 * 1000))
1225
+ self.driver._clean()
1226
+ rapid_speed = self.setting(float, "rapid_speed", 600.0)
1227
+ self.driver.laser_on(power=int(self.red_dot_level), speed=rapid_speed)
1228
+ # By default, any move is a G0 move which will not activate the laser,
1229
+ # so we need to switch to G1 mode:
1230
+ self.driver.move_mode = 1
1231
+ # An arbitrary move to turn the laser really on!
1232
+ # self.driver.grbl("G1")
1233
+ else:
1234
+ self.driver.laser_off()
1235
+ # self.driver.grbl("G0")
1236
+ self.driver.move_mode = 0
1237
+ # self.redlight_preferred = False
1238
+
1239
+ def pre_outline(self):
1240
+ if not (self.use_red_dot and self.use_red_dot_for_outline):
1241
+ return
1242
+ yield ("console", "red on -f")
1243
+
1244
+ def post_outline(self):
1245
+ if not (self.use_red_dot and self.use_red_dot_for_outline):
1246
+ return
1247
+ yield ("console", "red off -f")
meerk40t/grbl/driver.py CHANGED
@@ -217,7 +217,7 @@ class GRBLDriver(Parameters):
217
217
  (old_current[0], old_current[1], new_current[0], new_current[1]),
218
218
  )
219
219
 
220
- def dwell(self, time_in_ms):
220
+ def dwell(self, time_in_ms, settings=None):
221
221
  """
222
222
  Requests that the laser fire in place for the given time period. This could be done in a series of commands,
223
223
  move to a location, turn laser on, wait, turn laser off. However, some drivers have specific laser-in-place
@@ -226,7 +226,13 @@ class GRBLDriver(Parameters):
226
226
  @param time_in_ms:
227
227
  @return:
228
228
  """
229
- self.laser_on() # This can't be sent early since these are timed operations.
229
+ if settings is not None and "power" in settings:
230
+ power = settings["power"]
231
+ else:
232
+ power = self.power
233
+ self.laser_on(
234
+ power=power
235
+ ) # This can't be sent early since these are timed operations.
230
236
  self.wait(time_in_ms)
231
237
  self.laser_off()
232
238
 
@@ -371,3 +371,9 @@ class GRBLConfiguration(MWindow):
371
371
  else:
372
372
  # Different command
373
373
  pass
374
+
375
+ @signal_listener("activate;device")
376
+ def on_device_changes(self, *args):
377
+ # Device activated, make sure we are still fine...
378
+ if self.context.device.name != 'GRBLDevice':
379
+ wx.CallAfter(self.Close)
@@ -260,7 +260,7 @@ class GRBLControllerPanel(wx.Panel):
260
260
 
261
261
  def edit_macro(self, idx):
262
262
  def handler(event):
263
- macro = self.macros[idx]
263
+ macro = str(self.macros[idx])
264
264
  dlg = wx.TextEntryDialog(
265
265
  self, _("Content for macro {index}").format(index = idx + 1),
266
266
  value=macro,
meerk40t/gui/about.py CHANGED
@@ -1595,6 +1595,13 @@ class InformationPanel(ScrolledPanel):
1595
1595
  info += f"Version: {uname.version}" + "\n"
1596
1596
  info += f"Machine: {uname.machine}" + "\n"
1597
1597
  info += f"Processor: {uname.processor}" + "\n"
1598
+ try:
1599
+ import psutil
1600
+ mem = psutil.virtual_memory()
1601
+ available_mb = f"{mem.available / (1024 * 1024):.2f} MB"
1602
+ info += f"Available memory: {available_mb}\n"
1603
+ except ImportError:
1604
+ pass
1598
1605
  info += f"Theme: {self.context.themes.theme}, Darkmode: {self.context.themes.dark}\n"
1599
1606
  try:
1600
1607
  info += f"Ip-Address: {socket.gethostbyname(socket.gethostname())}"
@@ -139,7 +139,7 @@ class ChoicePropertyPanel(ScrolledPanel):
139
139
  # we need to create an independent copy of the lookup, otherwise
140
140
  # any amendments to choices like injector will affect the original
141
141
  standardhelp = ""
142
-
142
+
143
143
  def on_combo_text(param, ctrl, obj, dtype, addsig):
144
144
  def select(event=None):
145
145
  v = dtype(ctrl.GetValue())
@@ -314,9 +314,7 @@ class ChoicePropertyPanel(ScrolledPanel):
314
314
  dlg = wx.ColourDialog(self, color_data)
315
315
  if dlg.ShowModal() == wx.ID_OK:
316
316
  color_data = dlg.GetColourData()
317
- data = Color(
318
- swizzlecolor(color_data.GetColour().GetRGB()), 1.0
319
- )
317
+ data = Color(swizzlecolor(color_data.GetColour().GetRGB()), 1.0)
320
318
  set_color(ctrl, data)
321
319
  try:
322
320
  data_v = data.hexa
@@ -331,7 +329,7 @@ class ChoicePropertyPanel(ScrolledPanel):
331
329
  pass
332
330
 
333
331
  return click
334
-
332
+
335
333
  def on_angle_text(param, ctrl, obj, dtype, addsig):
336
334
  def text():
337
335
  try:
@@ -407,9 +405,7 @@ class ChoicePropertyPanel(ScrolledPanel):
407
405
  # except IndexError:
408
406
  # pass
409
407
 
410
- menuitem = menu.Append(
411
- wx.ID_ANY, _("Delete this entry"), ""
412
- )
408
+ menuitem = menu.Append(wx.ID_ANY, _("Delete this entry"), "")
413
409
  self.Bind(
414
410
  wx.EVT_MENU,
415
411
  on_delete,
@@ -431,9 +427,7 @@ class ChoicePropertyPanel(ScrolledPanel):
431
427
  # pass
432
428
  fill_ctrl(ctrl, local_obj, param, columns)
433
429
 
434
- menuitem = menu.Append(
435
- wx.ID_ANY, _("Duplicate this entry"), ""
436
- )
430
+ menuitem = menu.Append(wx.ID_ANY, _("Duplicate this entry"), "")
437
431
  self.Bind(
438
432
  wx.EVT_MENU,
439
433
  on_duplicate,
@@ -498,7 +492,6 @@ class ChoicePropertyPanel(ScrolledPanel):
498
492
 
499
493
  return text
500
494
 
501
-
502
495
  for choice in choices:
503
496
  if isinstance(choice, dict):
504
497
  if "help" not in choice:
@@ -785,7 +778,6 @@ class ChoicePropertyPanel(ScrolledPanel):
785
778
  wants_listener = False
786
779
  control = wxButton(self, label=label)
787
780
 
788
-
789
781
  control.Bind(
790
782
  wx.EVT_BUTTON,
791
783
  on_button(attr, obj, additional_signal),
@@ -799,7 +791,6 @@ class ChoicePropertyPanel(ScrolledPanel):
799
791
  control.SetValue(data)
800
792
  control.SetMinSize(dip_size(self, -1, 23))
801
793
 
802
-
803
794
  control.Bind(
804
795
  wx.EVT_CHECKBOX,
805
796
  on_checkbox_check(attr, control, obj, additional_signal),
@@ -824,7 +815,6 @@ class ChoicePropertyPanel(ScrolledPanel):
824
815
  control_sizer.Add(control, 1, wx.EXPAND, 0)
825
816
  current_sizer.Add(control_sizer, expansion_flag * weight, wx.EXPAND, 0)
826
817
 
827
-
828
818
  control.SetActionRoutine(
829
819
  on_generic_multi(attr, control, obj, data_type, additional_signal)
830
820
  )
@@ -843,7 +833,6 @@ class ChoicePropertyPanel(ScrolledPanel):
843
833
  # filename = _("No File")
844
834
  control.SetValue(filename)
845
835
 
846
-
847
836
  control.SetActionRoutine(
848
837
  on_file_text(attr, control, obj, data_type, additional_signal)
849
838
  )
@@ -880,6 +869,10 @@ class ChoicePropertyPanel(ScrolledPanel):
880
869
  value = int(data)
881
870
  else:
882
871
  value = int(data)
872
+ if callable(minvalue):
873
+ minvalue = minvalue()
874
+ if callable(maxvalue):
875
+ maxvalue = maxvalue()
883
876
  control = wx.Slider(
884
877
  self,
885
878
  wx.ID_ANY,
@@ -889,7 +882,6 @@ class ChoicePropertyPanel(ScrolledPanel):
889
882
  style=wx.SL_HORIZONTAL | wx.SL_VALUE_LABEL,
890
883
  )
891
884
 
892
-
893
885
  if ctrl_width > 0:
894
886
  control.SetMaxSize(dip_size(self, ctrl_width, -1))
895
887
  control_sizer.Add(control, 1, wx.EXPAND, 0)
@@ -928,7 +920,6 @@ class ChoicePropertyPanel(ScrolledPanel):
928
920
  if least is not None:
929
921
  control.SetValue(least)
930
922
 
931
-
932
923
  if ctrl_width > 0:
933
924
  control.SetMaxSize(dip_size(self, ctrl_width, -1))
934
925
  control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
@@ -957,7 +948,6 @@ class ChoicePropertyPanel(ScrolledPanel):
957
948
  else:
958
949
  control.SetSelection(int(data))
959
950
 
960
-
961
951
  if ctrl_width > 0:
962
952
  control.SetMaxSize(dip_size(self, ctrl_width, -1))
963
953
  control_sizer.Add(control, 1, wx.ALIGN_CENTER_VERTICAL, 0)
@@ -979,7 +969,7 @@ class ChoicePropertyPanel(ScrolledPanel):
979
969
  data = c.get("default")
980
970
  display_list.insert(0, str(data))
981
971
  choice_list.insert(0, str(data))
982
- cb_style = wx.CB_DROPDOWN | wx.CB_READONLY
972
+ cb_style = wx.CB_DROPDOWN | wx.CB_READONLY
983
973
  control = wxComboBox(
984
974
  self,
985
975
  wx.ID_ANY,
@@ -995,7 +985,6 @@ class ChoicePropertyPanel(ScrolledPanel):
995
985
  # print ("Choices: %s" % choice_list)
996
986
  # print ("To set: %s" % str(data))
997
987
 
998
-
999
988
  if label != "":
1000
989
  # Try to center it vertically to the controls extent
1001
990
  wd, ht = control.GetSize()
@@ -1013,14 +1002,16 @@ class ChoicePropertyPanel(ScrolledPanel):
1013
1002
  elif data_type in (str, int, float) and data_style == "combosmall":
1014
1003
  control_sizer = wx.BoxSizer(wx.HORIZONTAL)
1015
1004
  exclusive = c.get("exclusive", True)
1016
- cb_style = wx.CB_DROPDOWN | wx.CB_READONLY if exclusive else wx.CB_DROPDOWN
1005
+ cb_style = (
1006
+ wx.CB_DROPDOWN | wx.CB_READONLY if exclusive else wx.CB_DROPDOWN
1007
+ )
1017
1008
 
1018
1009
  choice_list = list(map(str, c.get("choices", [c.get("default")])))
1019
1010
  control = wxComboBox(
1020
1011
  self,
1021
1012
  wx.ID_ANY,
1022
1013
  choices=choice_list,
1023
- style = cb_style,
1014
+ style=cb_style,
1024
1015
  )
1025
1016
  # Constrain the width
1026
1017
  testsize = control.GetBestSize()
@@ -1043,7 +1034,6 @@ class ChoicePropertyPanel(ScrolledPanel):
1043
1034
  if least is not None:
1044
1035
  control.SetValue(least)
1045
1036
 
1046
-
1047
1037
  if label != "":
1048
1038
  # Try to center it vertically to the controls extent
1049
1039
  wd, ht = control.GetSize()
@@ -1083,7 +1073,6 @@ class ChoicePropertyPanel(ScrolledPanel):
1083
1073
  else:
1084
1074
  control_sizer = wx.BoxSizer(wx.HORIZONTAL)
1085
1075
 
1086
-
1087
1076
  bit_sizer = wx.BoxSizer(wx.VERTICAL)
1088
1077
  label_text = wxStaticText(
1089
1078
  self, wx.ID_ANY, "", style=wx.ALIGN_CENTRE_HORIZONTAL
@@ -1117,7 +1106,10 @@ class ChoicePropertyPanel(ScrolledPanel):
1117
1106
  control.SetValue(bool((data >> b) & 1))
1118
1107
  if mask:
1119
1108
  control.Enable(bool((mask_bits >> b) & 1))
1120
- control.Bind(wx.EVT_CHECKBOX, on_checkbox_bitcheck(attr, control, obj, b, additional_signal), )
1109
+ control.Bind(
1110
+ wx.EVT_CHECKBOX,
1111
+ on_checkbox_bitcheck(attr, control, obj, b, additional_signal),
1112
+ )
1121
1113
 
1122
1114
  # mask bit
1123
1115
  if mask:
@@ -1177,7 +1169,8 @@ class ChoicePropertyPanel(ScrolledPanel):
1177
1169
  self,
1178
1170
  wx.ID_ANY,
1179
1171
  style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES | wx.LC_SINGLE_SEL,
1180
- context=self.context, list_name=f"list_chart_{attr}",
1172
+ context=self.context,
1173
+ list_name=f"list_chart_{attr}",
1181
1174
  )
1182
1175
  l_columns = c.get("columns", [])
1183
1176
 
@@ -1224,13 +1217,11 @@ class ChoicePropertyPanel(ScrolledPanel):
1224
1217
 
1225
1218
  fill_ctrl(chart, obj, attr, l_columns)
1226
1219
 
1227
-
1228
1220
  chart.Bind(
1229
1221
  wx.EVT_LIST_BEGIN_LABEL_EDIT,
1230
1222
  on_chart_start(l_columns, attr, chart, obj),
1231
1223
  )
1232
1224
 
1233
-
1234
1225
  chart.Bind(
1235
1226
  wx.EVT_LIST_END_LABEL_EDIT,
1236
1227
  on_chart_stop(l_columns, attr, chart, obj),
@@ -1326,7 +1317,6 @@ class ChoicePropertyPanel(ScrolledPanel):
1326
1317
  control.SetMaxSize(dip_size(self, ctrl_width, -1))
1327
1318
  control_sizer.Add(control, 1, wx.EXPAND, 0)
1328
1319
 
1329
-
1330
1320
  control.SetActionRoutine(
1331
1321
  on_generic_text(attr, control, obj, data_type, additional_signal)
1332
1322
  )
@@ -243,9 +243,7 @@ class DevicePanel(wx.Panel):
243
243
  sizer_1.Add(sizer_3, 1, wx.EXPAND, 0)
244
244
  # All devices
245
245
  self.devices = []
246
- # Active item in list
247
- self.current_item = 0
248
-
246
+
249
247
  self.button_create_device = wxButton(self, wx.ID_ANY, _("Create New Device"))
250
248
  sizer_3.Add(self.button_create_device, 0, 0, 0)
251
249
 
@@ -305,12 +303,16 @@ class DevicePanel(wx.Panel):
305
303
  self.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.on_end_edit, self.devices_list)
306
304
  # end wxGlade
307
305
 
306
+ @property
307
+ def current_item(self):
308
+ if self.devices_list.GetSelectedItemCount() > 0:
309
+ return self.devices_list.GetFirstSelected()
310
+ return -1
311
+
308
312
  def pane_show(self, *args):
309
313
  self.refresh_device_tree()
310
314
  if len(self.devices) > 0:
311
315
  self.devices_list.Select(0, 1)
312
- else:
313
- self.current_item = -1
314
316
  self.enable_controls()
315
317
 
316
318
  def pane_hide(self, *args):
@@ -453,17 +455,14 @@ class DevicePanel(wx.Panel):
453
455
  return label
454
456
 
455
457
  def on_item_selected(self, event):
456
- if event is None:
457
- self.current_item = self.devices_list.GetFirstSelected()
458
- else:
459
- self.current_item = event.Index
458
+ if event:
460
459
  event.Skip()
461
460
  self.enable_controls()
462
461
 
463
462
  def on_item_deselected(self, event):
464
- self.current_item = -1
463
+ if event:
464
+ event.Skip()
465
465
  self.enable_controls()
466
- event.Skip()
467
466
 
468
467
  def enable_controls(self):
469
468
  if self.current_item < 0:
@@ -522,8 +521,9 @@ class DevicePanel(wx.Panel):
522
521
 
523
522
  def on_tree_popup_rename(self, service):
524
523
  def renameit(event=None):
524
+ lbl = str(service.label or "")
525
525
  with wx.TextEntryDialog(
526
- None, _("What do you call this device?"), _("Device Label"), ""
526
+ None, _("What do you call this device?"), _("Device Label"), lbl
527
527
  ) as dlg:
528
528
  dlg.SetValue(service.label)
529
529
  if dlg.ShowModal() == wx.ID_OK:
@@ -608,21 +608,32 @@ class DevicePanel(wx.Panel):
608
608
  self.duplicate_device(service)
609
609
 
610
610
  def on_button_activate_device(self, event): # wxGlade: DevicePanel.<event_handler>
611
+ def reselect(idx):
612
+ def handler():
613
+ if idx >= 0:
614
+ self.devices_list.Select(idx, 1)
615
+ self.devices_list.Focus(idx)
616
+
617
+ return handler
618
+
619
+ idx = self.current_item
620
+ if idx < 0:
621
+ return
611
622
  service = self.get_selected_device()
612
623
  if service is not None:
613
624
  service.kernel.activate_service_path("device", service.path)
614
625
  self.recolor_device_items()
626
+ wx.CallLater(750, reselect(idx))
615
627
 
616
628
  def on_button_config_device(self, event): # wxGlade: DevicePanel.<event_handler>
617
- service = self.get_selected_device()
618
- if service is not None:
619
- service("window toggle Configuration\n")
629
+ self.context("window toggle Configuration\n")
620
630
 
621
631
  def on_button_rename_device(self, event): # wxGlade: DevicePanel.<event_handler>
622
632
  service = self.get_selected_device()
623
633
  if service is not None:
634
+ label = str(service.label or "")
624
635
  with wx.TextEntryDialog(
625
- None, _("What do you call this device?"), _("Device Label"), ""
636
+ None, _("What do you call this device?"), _("Device Label"), label
626
637
  ) as dlg:
627
638
  dlg.SetValue(service.label)
628
639
  if dlg.ShowModal() == wx.ID_OK:
meerk40t/gui/icons.py CHANGED
@@ -3497,3 +3497,18 @@ icon_z_home = VectorIcon(
3497
3497
  "M 37.5,25 h 25 l -25, 25 h 25",
3498
3498
  ),
3499
3499
  )
3500
+
3501
+ icon_coffee = VectorIcon(
3502
+ fill=(),
3503
+ stroke=(
3504
+ "M4 20H10.9433M10.9433 20H11.0567M10.9433 20C10.9622 20.0002 10.9811 20.0002 11 20.0002C11.0189 20.0002 11.0378 20.0002 11.0567 20M10.9433 20C7.1034 19.9695 4 16.8468 4 12.9998V8.92285C4 8.41305 4.41305 8 4.92285 8H17.0767C17.5865 8 18 8.41305 18 8.92285V9M11.0567 20H18M11.0567 20C14.8966 19.9695 18 16.8468 18 12.9998M18 9H19.5C20.8807 9 22 10.1193 22 11.5C22 12.8807 20.8807 14 19.5 14H18V12.9998M18 9V12.9998M15 3L14 5M12 3L11 5M9 3L8 5",
3505
+ ),
3506
+ )
3507
+
3508
+ icon_sleep = VectorIcon(
3509
+ fill=(
3510
+ "M7.953,13 L1.027,13 C0.65,13 0.311,12.769 0.16,12.411 C0.009,12.053 0.076,11.636 0.328,11.348 L5.859,4.973 L0.988,4.973 C0.465,4.973 -2.27373675e-13,4.572 -2.27373675e-13,4.028 C-2.27373675e-13,3.483 0.425,3.043 0.947,3.043 L7.973,3.043 C8.347,3.043 8.689,3.273 8.839,3.632 C8.989,3.991 8.924,4.406 8.671,4.695 L3.179,11.082 L7.952,11.082 C8.475,11.082 8.899,11.471 8.899,12.015 C8.9,12.559 8.477,13 7.953,13 L7.953,13 Z",
3511
+ "M15.755,7.935 L9.701,7.935 C9.447,7.935 9.219,7.783 9.115,7.544 C9.011,7.307 9.053,7.027 9.222,6.833 L14.338,0.964 L9.338,0.964 C8.984,0.964 9.02,0.034 9.373,0.034 L15.471,0.034 C15.724,0.034 15.917,0.151 16.021,0.388 C16.126,0.625 16.083,0.905 15.914,1.1 L10.88,6.963 L15.755,6.963 C16.109,6.964 16.109,7.935 15.755,7.935 L15.755,7.935 Z",
3512
+ ),
3513
+ stroke=(),
3514
+ )