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
@@ -70,6 +70,12 @@ class LihuiyuControllerPanel(ScrolledPanel):
70
70
  self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY
71
71
  )
72
72
  self.button_clear_stats = wxButton(self, wx.ID_ANY, _("Reset\nstatistics"))
73
+ self.logged_packets = []
74
+ self.text_status_log = TextCtrl(
75
+ self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_READONLY
76
+ )
77
+ self.button_clear_status_log = wxButton(self, wx.ID_ANY, _("Clear Status-Log"))
78
+ # self.button_clear_status_log.function = lambda: self.context("clear\n")
73
79
 
74
80
  self.__set_properties()
75
81
  self.__do_layout()
@@ -84,10 +90,15 @@ class LihuiyuControllerPanel(ScrolledPanel):
84
90
  self.Bind(
85
91
  wx.EVT_CHECKBOX, self.on_check_show_usb_log, self.checkbox_show_usb_log
86
92
  )
93
+
87
94
  # Test the color combos...
88
95
  # self.Bind(wx.EVT_RIGHT_DOWN, self.debug_colors)
89
96
  # self._debug_counter = 0
97
+ def clear_log(event=None):
98
+ self.logged_packets.clear()
99
+ self.text_status_log.SetValue("")
90
100
 
101
+ self.button_clear_status_log.Bind(wx.EVT_BUTTON, clear_log)
91
102
  self.last_control_state = None
92
103
  self.retries = 0
93
104
  self._buffer = ""
@@ -190,11 +201,12 @@ class LihuiyuControllerPanel(ScrolledPanel):
190
201
  def __do_layout(self):
191
202
  sizer_main = wx.BoxSizer(wx.HORIZONTAL)
192
203
  sizer_main_vertical = wx.BoxSizer(wx.VERTICAL)
193
- sizer_show_usb_log = wx.BoxSizer(wx.HORIZONTAL)
204
+ sizer_show_usb_log = wx.BoxSizer(wx.VERTICAL)
194
205
  packet_count = StaticBoxSizer(self, wx.ID_ANY, _("Packet Info"), wx.VERTICAL)
195
- byte_data_status = StaticBoxSizer(
196
- self, wx.ID_ANY, _("Byte Data Status"), wx.HORIZONTAL
206
+ byte_data_sizer = StaticBoxSizer(
207
+ self, wx.ID_ANY, _("Byte Data Status"), wx.VERTICAL
197
208
  )
209
+ byte_data_status = wx.BoxSizer(wx.HORIZONTAL)
198
210
  byte5sizer = wx.BoxSizer(wx.VERTICAL)
199
211
  byte4sizer = wx.BoxSizer(wx.VERTICAL)
200
212
  byte3sizer = wx.BoxSizer(wx.VERTICAL)
@@ -240,38 +252,43 @@ class LihuiyuControllerPanel(ScrolledPanel):
240
252
  packet_count.Add(sizer_statistics, 1, wx.EXPAND, 0)
241
253
  packet_info.Add(self.text_packet_info, 11, wx.EXPAND, 0)
242
254
  packet_count.Add(packet_info, 0, wx.EXPAND, 0)
243
- byte0sizer.Add(self.text_byte_0, 0, 0, 0)
255
+ byte0sizer.Add(self.text_byte_0, 0, wx.EXPAND, 0)
244
256
  label_1 = wxStaticText(self, wx.ID_ANY, _("Byte 0"))
245
- byte0sizer.Add(label_1, 0, 0, 0)
257
+ byte0sizer.Add(label_1, 0, wx.EXPAND, 0)
246
258
  byte_data_status.Add(byte0sizer, 1, wx.EXPAND, 0)
247
- byte1sizer.Add(self.text_byte_1, 0, 0, 0)
259
+ byte1sizer.Add(self.text_byte_1, 0, wx.EXPAND, 0)
248
260
  label_2 = wxStaticText(self, wx.ID_ANY, _("Byte 1"))
249
- byte1sizer.Add(label_2, 0, 0, 0)
250
- byte1sizer.Add(self.text_desc, 0, 0, 0)
261
+ byte1sizer.Add(label_2, 0, wx.EXPAND, 0)
262
+ byte1sizer.Add(self.text_desc, 0, wx.EXPAND, 0)
251
263
  byte_data_status.Add(byte1sizer, 1, wx.EXPAND, 0)
252
- byte2sizer.Add(self.text_byte_2, 0, 0, 0)
264
+ byte2sizer.Add(self.text_byte_2, 0, wx.EXPAND, 0)
253
265
  label_3 = wxStaticText(self, wx.ID_ANY, _("Byte 2"))
254
- byte2sizer.Add(label_3, 0, 0, 0)
266
+ byte2sizer.Add(label_3, 0, wx.EXPAND, 0)
255
267
  byte_data_status.Add(byte2sizer, 1, wx.EXPAND, 0)
256
- byte3sizer.Add(self.text_byte_3, 0, 0, 0)
268
+ byte3sizer.Add(self.text_byte_3, 0, wx.EXPAND, 0)
257
269
  label_4 = wxStaticText(self, wx.ID_ANY, _("Byte 3"))
258
- byte3sizer.Add(label_4, 0, 0, 0)
270
+ byte3sizer.Add(label_4, 0, wx.EXPAND, 0)
259
271
  byte_data_status.Add(byte3sizer, 1, wx.EXPAND, 0)
260
- byte4sizer.Add(self.text_byte_4, 0, 0, 0)
272
+ byte4sizer.Add(self.text_byte_4, 0, wx.EXPAND, 0)
261
273
  label_5 = wxStaticText(self, wx.ID_ANY, _("Byte 4"))
262
- byte4sizer.Add(label_5, 0, 0, 0)
274
+ byte4sizer.Add(label_5, 0, wx.EXPAND, 0)
263
275
  byte_data_status.Add(byte4sizer, 1, wx.EXPAND, 0)
264
- byte5sizer.Add(self.text_byte_5, 0, 0, 0)
276
+ byte5sizer.Add(self.text_byte_5, 0, wx.EXPAND, 0)
265
277
  label_18 = wxStaticText(self, wx.ID_ANY, _("Byte 5"))
266
- byte5sizer.Add(label_18, 0, 0, 0)
278
+ byte5sizer.Add(label_18, 0, wx.EXPAND, 0)
267
279
  byte_data_status.Add(byte5sizer, 1, wx.EXPAND, 0)
268
- packet_count.Add(byte_data_status, 0, wx.EXPAND, 0)
269
- sizer_main_vertical.Add(packet_count, 0, 0, 0)
270
- label_6 = wxStaticText(self, wx.ID_ANY, "")
271
- sizer_show_usb_log.Add(label_6, 10, wx.EXPAND, 0)
272
- sizer_show_usb_log.Add(self.checkbox_show_usb_log, 0, 0, 0)
273
- sizer_main_vertical.Add(sizer_show_usb_log, 1, wx.EXPAND, 0)
274
- sizer_main.Add(sizer_main_vertical, 1, 0, 0)
280
+
281
+ byte_data_sizer.Add(byte_data_status, 0, wx.EXPAND, 0)
282
+ byte_data_sizer.Add(self.text_status_log, 1, wx.EXPAND, 0)
283
+ byte_data_sizer.Add(self.button_clear_status_log, 0, wx.EXPAND, 0)
284
+
285
+ sizer_main_vertical.Add(packet_count, 0, wx.EXPAND, 0)
286
+ sizer_main_vertical.Add(byte_data_sizer, 1, wx.EXPAND, 0)
287
+ # label_6 = wxStaticText(self, wx.ID_ANY, "")
288
+ # sizer_show_usb_log.Add(label_6, 10, wx.EXPAND, 0)
289
+ sizer_show_usb_log.Add(self.checkbox_show_usb_log, 0, wx.ALIGN_RIGHT, 0)
290
+ sizer_main_vertical.Add(sizer_show_usb_log, 0, wx.EXPAND, 0)
291
+ sizer_main.Add(sizer_main_vertical, 1, wx.EXPAND, 0)
275
292
  sizer_main.Add(self.text_usb_log, 1, wx.EXPAND, 0)
276
293
  self.SetSizer(sizer_main)
277
294
  sizer_main.Fit(self)
@@ -403,18 +420,42 @@ class LihuiyuControllerPanel(ScrolledPanel):
403
420
  self.text_byte_4.SetValue(str(status_data[4]))
404
421
  self.text_byte_5.SetValue(str(status_data[5]))
405
422
  self.text_desc.SetValue(code_string)
423
+ self.add_to_status_log(f"{status_data} - {code_string}")
406
424
  self.packet_count_text.SetValue(str(self.context.packet_count))
407
425
  self.rejected_packet_count_text.SetValue(str(self.context.rejected_count))
408
426
  except RuntimeError:
409
427
  # This should be handled when the controller window is closed.
410
428
  pass
411
429
 
430
+ def add_to_status_log(self, string_data):
431
+ """Add a string to the status log."""
432
+ if len(self.logged_packets) == 0:
433
+ self.logged_packets.append((1, string_data))
434
+ else:
435
+ count, oldstring_data = self.logged_packets[-1]
436
+ if oldstring_data == string_data:
437
+ # If the last entry is the same, just increase the count.
438
+ self.logged_packets[-1] = (count + 1, oldstring_data)
439
+ else:
440
+ # Otherwise, add a new entry.
441
+ if len(self.logged_packets) > 750:
442
+ self.logged_packets = self.logged_packets[-750:]
443
+ self.logged_packets.append((1, string_data))
444
+ text_data = []
445
+ for count, data in self.logged_packets:
446
+ if count > 1:
447
+ text_data.append(f"{data} (x{count})")
448
+ else:
449
+ text_data.append(data)
450
+ self.text_status_log.SetValue("\n".join(text_data))
451
+
412
452
  @signal_listener("pipe;packet_text")
413
453
  def update_packet_text(self, origin, string_data):
414
454
  if origin != self.context._path:
415
455
  return
416
456
  if string_data is not None and len(string_data) != 0:
417
457
  self.text_packet_info.SetValue(str(string_data))
458
+ self.add_to_status_log(f">> {string_data}")
418
459
 
419
460
  @signal_listener("pipe;usb_status")
420
461
  def on_connection_status_change(self, origin, status):
@@ -526,7 +567,11 @@ class LihuiyuControllerPanel(ScrolledPanel):
526
567
  except NotImplementedError:
527
568
  dlg = wx.MessageDialog(
528
569
  None,
529
- _("Connection Refused. See USB Log for detailed information.") + "\n" + _("You may run an incompatible version of libusb, have you tried the 32-bit version?"),
570
+ _("Connection Refused. See USB Log for detailed information.")
571
+ + "\n"
572
+ + _(
573
+ "You may run an incompatible version of libusb, have you tried the 32-bit version?"
574
+ ),
530
575
  _("Manual Connection"),
531
576
  wx.OK | wx.ICON_WARNING,
532
577
  )
@@ -468,3 +468,9 @@ class LihuiyuDriverGui(MWindow):
468
468
  @staticmethod
469
469
  def helptext():
470
470
  return _("Display the device configuration window")
471
+
472
+ @signal_listener("activate;device")
473
+ def on_device_changes(self, *args):
474
+ # Device activated, make sure we are still fine...
475
+ if self.context.device.name != 'LihuiyuDevice':
476
+ wx.CallAfter(self.Close)
@@ -6,7 +6,6 @@ LaserSpeed
6
6
  This is the standard library for converting to and from speed code information for LHYMICRO-GL.
7
7
  """
8
8
 
9
-
10
9
  from math import floor
11
10
 
12
11
 
@@ -55,6 +54,7 @@ class LaserSpeed:
55
54
  fix_speeds=False,
56
55
  fix_lows=False,
57
56
  fix_limit=False,
57
+ power_value=None,
58
58
  ):
59
59
  self.speed = speed
60
60
  self.board = board
@@ -67,7 +67,7 @@ class LaserSpeed:
67
67
  self.fix_speeds = fix_speeds
68
68
  self.fix_lows = fix_lows
69
69
  self.fix_limit = fix_limit
70
-
70
+ self.power_value = power_value
71
71
  if isinstance(speed, str):
72
72
  # this is a speedcode value.
73
73
  (
@@ -114,6 +114,8 @@ class LaserSpeed:
114
114
  parts.append(f"fix_limit={str(self.fix_limit)}")
115
115
  if not self.raster_horizontal:
116
116
  parts.append(f"raster_horizontal={str(self.raster_horizontal)}")
117
+ if self.power_value:
118
+ parts.append(f"power_value={str(self.power_value)}")
117
119
  return f"LaserSpeed({', '.join(parts)})"
118
120
 
119
121
  @property
@@ -129,6 +131,7 @@ class LaserSpeed:
129
131
  fix_speeds=self.fix_speeds,
130
132
  fix_lows=self.fix_lows,
131
133
  raster_horizontal=self.raster_horizontal,
134
+ power_value=self.power_value,
132
135
  )
133
136
 
134
137
 
@@ -163,6 +166,7 @@ def get_code_from_speed(
163
166
  fix_speeds=False,
164
167
  fix_lows=False,
165
168
  raster_horizontal=True,
169
+ power_value=None,
166
170
  ):
167
171
  """
168
172
  Get a speedcode from a given speed. The raster step appends the 'G' value and uses speed ranges.
@@ -204,21 +208,24 @@ def get_code_from_speed(
204
208
  # produced a negative speed value, go ahead and set that to 0
205
209
  speed_value = 0
206
210
  encoded_speed = encode_16bit(speed_value)
207
-
211
+ if power_value is None or power_value >= 1000 or power_value < 0:
212
+ power_suffix = ""
213
+ else:
214
+ power_suffix = f"W{int(power_value):03d}"
208
215
  if raster_step != 0:
209
216
  # There is no C suffix notation for raster step.
210
217
  if isinstance(raster_step, tuple):
211
- return f"V{encoded_speed}{acceleration:1d}G{abs(raster_step[0]):03d}G{abs(raster_step[1]):03d}"
218
+ return f"V{encoded_speed}{acceleration:1d}G{abs(raster_step[0]):03d}G{abs(raster_step[1]):03d}{power_suffix}"
212
219
  else:
213
- return f"V{encoded_speed}{acceleration:1d}G{abs(raster_step):03d}"
220
+ return f"V{encoded_speed}{acceleration:1d}G{abs(raster_step):03d}{power_suffix}"
214
221
 
215
222
  if d_ratio == 0 or board in ("A", "B", "M"):
216
223
  # We do not need the diagonal code.
217
224
  if raster_step == 0:
218
225
  if suffix_c:
219
- return f"CV{encoded_speed}1C"
226
+ return f"CV{encoded_speed}1C{power_suffix}"
220
227
  else:
221
- return f"CV{encoded_speed}{acceleration:1d}"
228
+ return f"CV{encoded_speed}{acceleration:1d}{power_suffix}"
222
229
  else:
223
230
  step_value = min(int(floor(mm_per_second) + 1), 128)
224
231
  frequency_kHz = float(mm_per_second) / 25.4
@@ -235,11 +242,11 @@ def get_code_from_speed(
235
242
  d_value = 0
236
243
  encoded_diagonal = encode_16bit(d_value)
237
244
  if suffix_c:
238
- return f"CV{encoded_speed}1{step_value:03d}{encoded_diagonal}C"
239
- else:
240
245
  return (
241
- f"CV{encoded_speed}{acceleration:1d}{step_value:03d}{encoded_diagonal}"
246
+ f"CV{encoded_speed}1{step_value:03d}{encoded_diagonal}C{power_suffix}"
242
247
  )
248
+ else:
249
+ return f"CV{encoded_speed}{acceleration:1d}{step_value:03d}{encoded_diagonal}{power_suffix}"
243
250
 
244
251
 
245
252
  def parse_speed_code(speed_code):
@@ -30,6 +30,7 @@ class LihuiyuParser:
30
30
 
31
31
  self.x = 0.0
32
32
  self.y = 0.0
33
+ self.power = 1000.0
33
34
  self.number_value = ""
34
35
  self.distance_x = 0
35
36
  self.distance_y = 0
@@ -132,6 +133,7 @@ class LihuiyuParser:
132
133
  self.number_value += c
133
134
  if len(self.number_value) >= 3:
134
135
  self.append_distance(int(self.number_value))
136
+
135
137
  self.number_value = ""
136
138
 
137
139
  def speedcode_b1_consumer(self, c):
@@ -201,6 +203,17 @@ class LihuiyuParser:
201
203
  self.number_value = ""
202
204
  self.number_consumer = self.speedcode_mult_consumer
203
205
 
206
+ def power_consumer(self, c):
207
+ self.number_value += c
208
+ if len(self.number_value) >= 3:
209
+ if self.channel:
210
+ self.channel(f"Set Power level = {self.number_value}")
211
+ self.power = int(self.number_value)
212
+ if self.power == 0:
213
+ self.power = 1000
214
+ self.number_value = ""
215
+ self.number_consumer = self.distance_consumer
216
+
204
217
  def append_distance(self, amount):
205
218
  if self.x_on:
206
219
  self.distance_x += amount
@@ -402,3 +415,13 @@ class LihuiyuParser:
402
415
  self.channel(
403
416
  f"Diagonal {'Top' if self.top else 'Bottom'} {'Left' if self.left else 'Right'}"
404
417
  )
418
+ elif c == "W":
419
+ """
420
+ W: Set Laser Power(W000 .. W999)
421
+ Example: IV……W500 (Set Laser Power is 50.0%)
422
+ Note: only M3 and fireware up 2024.01.18g support set laser power
423
+ Earlier M3 only is M2+
424
+ """
425
+ if self.channel:
426
+ self.channel("Set Laser Power")
427
+ self.number_consumer = self.power_consumer
meerk40t/main.py CHANGED
@@ -11,7 +11,7 @@ import os.path
11
11
  import sys
12
12
 
13
13
  APPLICATION_NAME = "MeerK40t"
14
- APPLICATION_VERSION = "0.9.7051"
14
+ APPLICATION_VERSION = "0.9.7900"
15
15
 
16
16
  if not getattr(sys, "frozen", False):
17
17
  # If .git directory does not exist we are running from a package like pypi
@@ -7,6 +7,7 @@ from meerk40t.device.gui.effectspanel import EffectsPanel
7
7
  from meerk40t.gui.choicepropertypanel import ChoicePropertyPanel
8
8
  from meerk40t.gui.icons import icons8_administrative_tools
9
9
  from meerk40t.gui.mwindow import MWindow
10
+ from meerk40t.kernel import signal_listener
10
11
 
11
12
  _ = wx.GetTranslation
12
13
 
@@ -85,3 +86,9 @@ class MoshiDriverGui(MWindow):
85
86
  @staticmethod
86
87
  def helptext():
87
88
  return _("Display the device configuration window")
89
+
90
+ @signal_listener("activate;device")
91
+ def on_device_changes(self, *args):
92
+ # Device activated, make sure we are still fine...
93
+ if self.context.device.name != 'MoshiDevice':
94
+ wx.CallAfter(self.Close)
@@ -739,9 +739,10 @@ class NewlyController:
739
739
  self(f"WU{int(round(w_position))}")
740
740
  self.close_job()
741
741
 
742
- def pulse(self, pulse_time_ms):
742
+ def pulse(self, pulse_time_ms, power=None):
743
743
  self.realtime_job()
744
- self.dwell(pulse_time_ms)
744
+ settings = None if power is None else {"power": power}
745
+ self.dwell(pulse_time_ms, settings=settings)
745
746
  self.close_job()
746
747
 
747
748
  def home(self):
meerk40t/newly/device.py CHANGED
@@ -644,15 +644,28 @@ class NewlyDevice(Service, Status):
644
644
  action="store_true",
645
645
  help=_("override one second laser fire pulse duration"),
646
646
  )
647
+ @self.console_option("power", "p", type=str, help=_("Power level"))
647
648
  @self.console_argument("time", type=float, help=_("laser fire pulse duration"))
648
649
  @self.console_command(
649
650
  "pulse",
650
651
  help=_("pulse <time>: Pulse the laser in place."),
651
652
  )
652
- def pulse(command, channel, _, time=None, idonotlovemyhouse=False, **kwgs):
653
+ def pulse(
654
+ command, channel, _, time=None, power=None, idonotlovemyhouse=False, **kwgs
655
+ ):
653
656
  if time is None:
654
657
  channel(_("Must specify a pulse time in milliseconds."))
655
658
  return
659
+ if power:
660
+ try:
661
+ if power.endswith("%"):
662
+ power = float(power[:-1]) * 10
663
+ else:
664
+ power = float(power)
665
+ except ValueError:
666
+ channel(_("Invalid power value: {power}").format(power=power))
667
+ return
668
+
656
669
  if time > 1000.0:
657
670
  channel(
658
671
  _(
@@ -665,7 +678,7 @@ class NewlyDevice(Service, Status):
665
678
  except IndexError:
666
679
  return
667
680
  if self.spooler.is_idle:
668
- self.spooler.command("pulse", time)
681
+ self.spooler.command("pulse", time, power)
669
682
  channel(_("Pulse laser for {time} milliseconds").format(time=time))
670
683
  else:
671
684
  channel(_("Pulse laser failed: Busy"))
@@ -813,6 +826,14 @@ class NewlyDevice(Service, Status):
813
826
  name = self.label.replace(" ", "-")
814
827
  return name.replace("/", "-")
815
828
 
829
+ @property
830
+ def supports_pwm(self):
831
+ """
832
+ Returns whether this device supports PWM.
833
+ :return: True if the device supports PWM, False otherwise.
834
+ """
835
+ return self.pwm_enabled
836
+
816
837
  def service_attach(self, *args, **kwargs):
817
838
  self.realize()
818
839
 
meerk40t/newly/driver.py CHANGED
@@ -39,7 +39,12 @@ class NewlyDriver:
39
39
  self._queue_total = 0
40
40
 
41
41
  self.plot_planner = PlotPlanner(
42
- dict(), single=True, ppi=False, shift=False, group=True, require_uniform_movement = False,
42
+ dict(),
43
+ single=True,
44
+ ppi=False,
45
+ shift=False,
46
+ group=True,
47
+ require_uniform_movement=False,
43
48
  )
44
49
  self._aborting = False
45
50
  self._list_bits = None
@@ -525,8 +530,8 @@ class NewlyDriver:
525
530
  """
526
531
  pass
527
532
 
528
- def pulse(self, pulse_time):
529
- self.connection.pulse(pulse_time)
533
+ def pulse(self, pulse_time, power=None):
534
+ self.connection.pulse(pulse_time, power=power)
530
535
 
531
536
  def dwell(self, time_in_ms):
532
537
  """
@@ -7,6 +7,7 @@ from meerk40t.device.gui.effectspanel import EffectsPanel
7
7
  from meerk40t.gui.choicepropertypanel import ChoicePropertyPanel
8
8
  from meerk40t.gui.icons import icons8_administrative_tools
9
9
  from meerk40t.gui.mwindow import MWindow
10
+ from meerk40t.kernel import signal_listener
10
11
 
11
12
  _ = wx.GetTranslation
12
13
 
@@ -114,3 +115,9 @@ class NewlyConfiguration(MWindow):
114
115
  @staticmethod
115
116
  def submenu():
116
117
  return "Device-Settings", "Configuration"
118
+
119
+ @signal_listener("activate;device")
120
+ def on_device_changes(self, *args):
121
+ # Device activated, make sure we are still fine...
122
+ if self.context.device.name != 'newly':
123
+ wx.CallAfter(self.Close)
@@ -8,6 +8,7 @@ from meerk40t.gui.choicepropertypanel import ChoicePropertyPanel
8
8
  from meerk40t.gui.icons import icons8_administrative_tools
9
9
  from meerk40t.gui.mwindow import MWindow
10
10
  from meerk40t.gui.wxutils import ScrolledPanel, StaticBoxSizer, TextCtrl, dip_size
11
+ from meerk40t.kernel import signal_listener
11
12
 
12
13
  _ = wx.GetTranslation
13
14
 
@@ -237,3 +238,9 @@ class RuidaConfiguration(MWindow):
237
238
  @staticmethod
238
239
  def helptext():
239
240
  return _("Display the device configuration window")
241
+
242
+ @signal_listener("activate;device")
243
+ def on_device_changes(self, *args):
244
+ # Device activated, make sure we are still fine...
245
+ if self.context.device.name != 'RuidaDevice':
246
+ wx.CallAfter(self.Close)