meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7050__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 (85) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +38 -13
  3. meerk40t/balormk/cylindermod.py +1 -0
  4. meerk40t/balormk/device.py +13 -9
  5. meerk40t/balormk/driver.py +9 -2
  6. meerk40t/balormk/galvo_commands.py +3 -1
  7. meerk40t/balormk/gui/gui.py +6 -0
  8. meerk40t/balormk/livelightjob.py +338 -321
  9. meerk40t/balormk/mock_connection.py +4 -3
  10. meerk40t/balormk/usb_connection.py +11 -2
  11. meerk40t/camera/camera.py +19 -14
  12. meerk40t/camera/gui/camerapanel.py +6 -0
  13. meerk40t/core/cutplan.py +101 -78
  14. meerk40t/core/elements/element_treeops.py +435 -140
  15. meerk40t/core/elements/elements.py +100 -9
  16. meerk40t/core/elements/shapes.py +259 -72
  17. meerk40t/core/elements/tree_commands.py +10 -5
  18. meerk40t/core/node/blobnode.py +19 -4
  19. meerk40t/core/node/elem_ellipse.py +18 -8
  20. meerk40t/core/node/elem_image.py +51 -19
  21. meerk40t/core/node/elem_line.py +18 -8
  22. meerk40t/core/node/elem_path.py +18 -8
  23. meerk40t/core/node/elem_point.py +10 -4
  24. meerk40t/core/node/elem_polyline.py +19 -11
  25. meerk40t/core/node/elem_rect.py +18 -8
  26. meerk40t/core/node/elem_text.py +11 -5
  27. meerk40t/core/node/filenode.py +2 -8
  28. meerk40t/core/node/groupnode.py +11 -11
  29. meerk40t/core/node/image_processed.py +11 -5
  30. meerk40t/core/node/image_raster.py +11 -5
  31. meerk40t/core/node/node.py +64 -16
  32. meerk40t/core/node/refnode.py +2 -1
  33. meerk40t/core/planner.py +25 -11
  34. meerk40t/core/svg_io.py +91 -34
  35. meerk40t/device/dummydevice.py +7 -1
  36. meerk40t/extra/vtracer.py +222 -0
  37. meerk40t/grbl/device.py +96 -9
  38. meerk40t/grbl/driver.py +15 -5
  39. meerk40t/gui/about.py +20 -0
  40. meerk40t/gui/devicepanel.py +20 -16
  41. meerk40t/gui/gui_mixins.py +4 -0
  42. meerk40t/gui/icons.py +330 -253
  43. meerk40t/gui/laserpanel.py +27 -3
  44. meerk40t/gui/laserrender.py +41 -21
  45. meerk40t/gui/magnetoptions.py +158 -65
  46. meerk40t/gui/materialtest.py +569 -310
  47. meerk40t/gui/navigationpanels.py +229 -24
  48. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  49. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  50. meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
  51. meerk40t/gui/ribbon.py +6 -1
  52. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  53. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  54. meerk40t/gui/simulation.py +75 -77
  55. meerk40t/gui/spoolerpanel.py +27 -7
  56. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  57. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  58. meerk40t/gui/tips.py +15 -1
  59. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  60. meerk40t/gui/wxmmain.py +242 -114
  61. meerk40t/gui/wxmscene.py +107 -24
  62. meerk40t/gui/wxmtree.py +4 -2
  63. meerk40t/gui/wxutils.py +286 -15
  64. meerk40t/image/imagetools.py +129 -65
  65. meerk40t/internal_plugins.py +4 -0
  66. meerk40t/kernel/kernel.py +67 -18
  67. meerk40t/kernel/settings.py +28 -9
  68. meerk40t/lihuiyu/device.py +24 -12
  69. meerk40t/main.py +14 -9
  70. meerk40t/moshi/device.py +20 -6
  71. meerk40t/network/console_server.py +22 -6
  72. meerk40t/newly/device.py +10 -3
  73. meerk40t/newly/gui/gui.py +10 -0
  74. meerk40t/ruida/device.py +22 -2
  75. meerk40t/ruida/loader.py +9 -4
  76. meerk40t/ruida/rdjob.py +48 -8
  77. meerk40t/tools/geomstr.py +240 -123
  78. meerk40t/tools/rasterplotter.py +185 -94
  79. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
  80. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
  81. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
  82. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
  83. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
  84. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
  85. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  import math
2
2
  import platform
3
- from time import perf_counter, sleep
3
+ from time import perf_counter
4
4
 
5
5
  import wx
6
6
 
@@ -280,7 +280,6 @@ class OperationsPanel(wx.Panel):
280
280
  def on_text_operation_param(self, event):
281
281
  content = self.text_operation_param.GetValue()
282
282
  idx = self.list_operations.GetFirstSelected()
283
- flag = False
284
283
  if idx < 0:
285
284
  return
286
285
  op = self.cutplan.plan[idx]
@@ -325,7 +324,7 @@ class OperationsPanel(wx.Panel):
325
324
  content = op.command
326
325
  flag = True
327
326
  elif isinstance(op, GotoOperation):
328
- content = str(op.x) + "," + str(op.y)
327
+ content = f"{str(op.x)},{str(op.y)}"
329
328
  flag = True
330
329
  elif isinstance(op, WaitOperation):
331
330
  content = str(op.wait)
@@ -485,9 +484,7 @@ class CutcodePanel(wx.Panel):
485
484
 
486
485
  self.cutcode = cutcode
487
486
  self.plan_name = plan_name
488
- self.list_cutcode = wxListBox(
489
- self, wx.ID_ANY, choices=[], style=wx.LB_MULTIPLE
490
- )
487
+ self.list_cutcode = wxListBox(self, wx.ID_ANY, choices=[], style=wx.LB_MULTIPLE)
491
488
  self.last_selected = []
492
489
  self.display_highlighted_only = False
493
490
  # self.text_operation_param = TextCtrl(
@@ -901,15 +898,9 @@ class SimulationPanel(wx.Panel, Job):
901
898
 
902
899
  self.slider_progress = wx.Slider(self, wx.ID_ANY, self.max, 0, self.max)
903
900
  self.slider_progress.SetFocus()
904
- self.text_distance_laser = TextCtrl(
905
- self, wx.ID_ANY, "", style=wx.TE_READONLY
906
- )
907
- self.text_distance_travel = TextCtrl(
908
- self, wx.ID_ANY, "", style=wx.TE_READONLY
909
- )
910
- self.text_distance_total = TextCtrl(
911
- self, wx.ID_ANY, "", style=wx.TE_READONLY
912
- )
901
+ self.text_distance_laser = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
902
+ self.text_distance_travel = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
903
+ self.text_distance_total = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
913
904
  self.text_time_laser = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
914
905
  self.text_time_travel = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
915
906
  self.text_time_extra = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
@@ -923,18 +914,10 @@ class SimulationPanel(wx.Panel, Job):
923
914
  self.text_distance_total_step = TextCtrl(
924
915
  self, wx.ID_ANY, "", style=wx.TE_READONLY
925
916
  )
926
- self.text_time_laser_step = TextCtrl(
927
- self, wx.ID_ANY, "", style=wx.TE_READONLY
928
- )
929
- self.text_time_travel_step = TextCtrl(
930
- self, wx.ID_ANY, "", style=wx.TE_READONLY
931
- )
932
- self.text_time_extra_step = TextCtrl(
933
- self, wx.ID_ANY, "", style=wx.TE_READONLY
934
- )
935
- self.text_time_total_step = TextCtrl(
936
- self, wx.ID_ANY, "", style=wx.TE_READONLY
937
- )
917
+ self.text_time_laser_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
918
+ self.text_time_travel_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
919
+ self.text_time_extra_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
920
+ self.text_time_total_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
938
921
  self.button_play = wxButton(self, wx.ID_ANY, "")
939
922
  self.button_play.SetToolTip(_("Start the simulation replay"))
940
923
  self.slider_playbackspeed = wx.Slider(self, wx.ID_ANY, 180, 0, 310)
@@ -1257,12 +1240,16 @@ class SimulationPanel(wx.Panel, Job):
1257
1240
  try:
1258
1241
  if newvalue:
1259
1242
  # Slided in ->
1260
- self.hscene_sizer.Show(sizer=self.voption_sizer, show=False, recursive=True)
1243
+ self.hscene_sizer.Show(
1244
+ sizer=self.voption_sizer, show=False, recursive=True
1245
+ )
1261
1246
  self.voption_sizer.Layout()
1262
1247
  self.btn_slide_options.SetLabel("<")
1263
1248
  else:
1264
1249
  # Slided out ->
1265
- self.hscene_sizer.Show(sizer=self.voption_sizer, show=True, recursive=True)
1250
+ self.hscene_sizer.Show(
1251
+ sizer=self.voption_sizer, show=True, recursive=True
1252
+ )
1266
1253
  self.voption_sizer.Layout()
1267
1254
  self.btn_slide_options.SetLabel(">")
1268
1255
  self.hscene_sizer.Layout()
@@ -1316,7 +1303,10 @@ class SimulationPanel(wx.Panel, Job):
1316
1303
  if self.laserspot_display:
1317
1304
  spot_value = getattr(self.context.device, "laserspot", "0.3mm")
1318
1305
  try:
1319
- scale = 0.5 * (self.context.device.view.native_scale_x + self.context.device.view.native_scale_y)
1306
+ scale = 0.5 * (
1307
+ self.context.device.view.native_scale_x
1308
+ + self.context.device.view.native_scale_y
1309
+ )
1320
1310
  spotwidth_in_scene = float(Length(spot_value))
1321
1311
  spot_width = spotwidth_in_scene / scale
1322
1312
  # print (f"Scale for device: {scale}, spot in scene: {spot_value} = {spotwidth_in_scene} -> {spot_width}")
@@ -1560,10 +1550,7 @@ class SimulationPanel(wx.Panel, Job):
1560
1550
  self.slider_progress.SetValue(self.max)
1561
1551
  value = self.slider_playbackspeed.GetValue()
1562
1552
  value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
1563
- if self.radio_cut.GetValue():
1564
- factor = 0.1 # steps
1565
- else:
1566
- factor = 1 # seconds
1553
+ factor = 0.1 if self.radio_cut.GetValue() else 1 # steps / seconds
1567
1554
  self.interval = factor * 100.0 / float(value)
1568
1555
 
1569
1556
  def _refresh_simulated_plan(self):
@@ -1620,12 +1607,11 @@ class SimulationPanel(wx.Panel, Job):
1620
1607
 
1621
1608
  self.update_job.cancel()
1622
1609
  self.context.schedule(self.update_job)
1623
-
1610
+ self.reload_statistics()
1624
1611
  self._startup()
1625
1612
  self.request_refresh()
1626
1613
  self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
1627
1614
 
1628
-
1629
1615
  @signal_listener("device;modified")
1630
1616
  @signal_listener("plan")
1631
1617
  def on_plan_change(self, origin, plan_name=None, status=None):
@@ -1693,7 +1679,7 @@ class SimulationPanel(wx.Panel, Job):
1693
1679
  self.button_spool.Enable(False)
1694
1680
  except RuntimeError:
1695
1681
  # Control no longer existant
1696
- return
1682
+ return
1697
1683
  msg = self.button_spool.GetLabel()
1698
1684
  self.button_spool.SetLabel(_("Calculating"))
1699
1685
  for cut in self.cutcode:
@@ -1701,9 +1687,15 @@ class SimulationPanel(wx.Panel, Job):
1701
1687
  if hasattr(cut, "_plotcache") and cut._plotcache is not None:
1702
1688
  continue
1703
1689
  if isinstance(cut, RasterCut):
1704
- cut._plotcache = list(cut.plot.plot())
1690
+ try:
1691
+ cut._plotcache = list(cut.plot.plot())
1692
+ except MemoryError:
1693
+ cut._plotcache = None
1705
1694
  elif isinstance(cut, PlotCut):
1706
- cut._plotcache = list(cut.plot)
1695
+ try:
1696
+ cut._plotcache = list(cut.plot)
1697
+ except MemoryError:
1698
+ cut._plotcache = None
1707
1699
  self.context.signal("refresh_scene", self.widget_scene.name)
1708
1700
  self.reload_statistics()
1709
1701
  try:
@@ -1963,10 +1955,7 @@ class SimulationPanel(wx.Panel, Job):
1963
1955
 
1964
1956
  value = self.slider_playbackspeed.GetValue()
1965
1957
  value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
1966
- if self.radio_cut.GetValue():
1967
- factor = 0.1 # steps
1968
- else:
1969
- factor = 1 # seconds
1958
+ factor = 0.1 if self.radio_cut.GetValue() else 1 # steps / seconds
1970
1959
  self.interval = factor * 100.0 / float(value)
1971
1960
 
1972
1961
  self.text_playback_speed.SetValue(f"{value}%")
@@ -1991,7 +1980,7 @@ class SimulationWidget(Widget):
1991
1980
  self.matrix.post_cat(~scene.context.device.view.matrix)
1992
1981
  self.last_msg = None
1993
1982
  self.raster_as_image = True
1994
- self.laserspot_width = None # 1 Pixel
1983
+ self.laserspot_width = None # 1 Pixel
1995
1984
 
1996
1985
  def process_draw(self, gc: wx.GraphicsContext):
1997
1986
  if self.sim.progress < 0:
@@ -2005,7 +1994,9 @@ class SimulationWidget(Widget):
2005
1994
  sim_cut = self.sim.cutcode[:idx]
2006
1995
  else:
2007
1996
  sim_cut = self.sim.cutcode
2008
- self.renderer.draw_cutcode(sim_cut, gc, 0, 0, self.raster_as_image, laserspot_width=spot_width)
1997
+ self.renderer.draw_cutcode(
1998
+ sim_cut, gc, 0, 0, self.raster_as_image, laserspot_width=spot_width
1999
+ )
2009
2000
  if residual <= 0:
2010
2001
  return
2011
2002
  # We draw interpolated lines to acknowledge we are in the middle of a cut operation
@@ -2022,7 +2013,9 @@ class SimulationWidget(Widget):
2022
2013
  image = cut.image
2023
2014
  gc.PushState()
2024
2015
  matrix = Matrix.scale(cut.step_x, cut.step_y)
2025
- matrix.post_translate(cut.offset_x + x, cut.offset_y + y) # Adjust image xy
2016
+ matrix.post_translate(
2017
+ cut.offset_x + x, cut.offset_y + y
2018
+ ) # Adjust image xy
2026
2019
  gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
2027
2020
  try:
2028
2021
  cache = cut._cache
@@ -2043,31 +2036,23 @@ class SimulationWidget(Widget):
2043
2036
  cut._cache_id = id(image)
2044
2037
  # Set draw - constraint
2045
2038
  if cut.horizontal:
2039
+ # mode = "T2B"
2040
+ clip_w = cut._cache_width
2041
+ clip_h = int(residual * cut._cache_height)
2046
2042
  if cut.start_minimum_y:
2047
- # mode = "T2B"
2048
- clip_w = cut._cache_width
2049
- clip_h = int(residual * cut._cache_height)
2050
- clip_x = 0
2051
2043
  clip_y = 0
2052
2044
  else:
2053
- # mode = "B2T"
2054
- clip_w = cut._cache_width
2055
- clip_h = int(residual * cut._cache_height)
2056
- clip_x = 0
2057
2045
  clip_y = cut._cache_height - clip_h
2046
+ clip_x = 0
2058
2047
  else:
2048
+ # mode = "L2R"
2049
+ clip_w = int(residual * cut._cache_width)
2050
+ clip_h = cut._cache_height
2051
+ clip_y = 0
2059
2052
  if cut.start_minimum_x:
2060
- # mode = "L2R"
2061
- clip_w = int(residual * cut._cache_width)
2062
- clip_h = cut._cache_height
2063
2053
  clip_x = 0
2064
- clip_y = 0
2065
2054
  else:
2066
- # mode = "R2L"
2067
- clip_w = int(residual * cut._cache_width)
2068
- clip_h = cut._cache_height
2069
2055
  clip_x = cut._cache_width - clip_w
2070
- clip_y = 0
2071
2056
 
2072
2057
  # msg = f"Mode: {mode}, Horiz: {cut.horizontal}, from left: {cut.start_on_left}, from top: {cut.start_on_top}"
2073
2058
  # if msg != self.last_msg:
@@ -2094,8 +2079,16 @@ class SimulationWidget(Widget):
2094
2079
  gc.PopState()
2095
2080
  else:
2096
2081
  # We draw the cutcode up to a certain percentage
2097
- simcut = (self.sim.cutcode[idx], )
2098
- self.renderer.draw_cutcode(simcut, gc, 0, 0, self.raster_as_image, residual=residual, laserspot_width=spot_width)
2082
+ simcut = (self.sim.cutcode[idx],)
2083
+ self.renderer.draw_cutcode(
2084
+ simcut,
2085
+ gc,
2086
+ 0,
2087
+ 0,
2088
+ self.raster_as_image,
2089
+ residual=residual,
2090
+ laserspot_width=spot_width,
2091
+ )
2099
2092
 
2100
2093
  return
2101
2094
  # # We draw a rectangle covering the raster area
@@ -2163,9 +2156,9 @@ class SimulationTravelWidget(Widget):
2163
2156
  self.initvars()
2164
2157
 
2165
2158
  def initvars(self):
2166
- self.starts = list()
2167
- self.ends = list()
2168
- self.pos = list()
2159
+ self.starts = []
2160
+ self.ends = []
2161
+ self.pos = []
2169
2162
  self.starts.append(wx.Point2D(0, 0))
2170
2163
  self.ends.append(wx.Point2D(0, 0))
2171
2164
  prev = None
@@ -2202,8 +2195,8 @@ class SimulationTravelWidget(Widget):
2202
2195
  self.ends.append(wx.Point2D(m1.real, m1.imag))
2203
2196
  else:
2204
2197
  end = wx.Point2D(*curr.start)
2205
- self.starts = list()
2206
- self.ends = list()
2198
+ self.starts = []
2199
+ self.ends = []
2207
2200
  self.starts.append(wx.Point2D(0, 0))
2208
2201
  self.ends.append(end)
2209
2202
  self.pos.append(len(self.starts))
@@ -2297,17 +2290,20 @@ class SimReticleWidget(Widget):
2297
2290
  y = 0
2298
2291
  if (
2299
2292
  # self.sim.progress > 0 and
2300
- self.sim.cutcode is not None
2301
- and len(self.sim.cutcode)
2293
+ self.sim.cutcode is not None and len(self.sim.cutcode)
2302
2294
  ):
2303
2295
  idx, residual = self.sim.progress_to_idx(self.sim.progress)
2296
+ if idx >= len(self.sim.cutcode):
2297
+ idx = len(self.sim.cutcode) - 1
2298
+ self.sim.progress = 0
2304
2299
  dx = 0
2305
2300
  dy = 0
2306
2301
  if self.sim.progress != self.sim.max:
2307
- if idx > 0:
2308
- pos = self.sim.cutcode[idx - 1].end
2309
- else:
2310
- pos = self.sim.cutcode[idx].start
2302
+ pos = (
2303
+ self.sim.cutcode[idx - 1].end
2304
+ if idx > 0
2305
+ else self.sim.cutcode[idx].start
2306
+ )
2311
2307
  if residual > 0:
2312
2308
  # We could still be traversing or already burning...
2313
2309
  # We have two time stamps one after travel,
@@ -2399,7 +2395,7 @@ class Simulation(MWindow):
2399
2395
  busy.start()
2400
2396
 
2401
2397
  kernel.console(
2402
- f"planz copy preprocess validate blob{optpart}\nwindow toggle Simulation z\n"
2398
+ f"planz clear copy preprocess validate blob{optpart}\nwindow toggle Simulation z\n"
2403
2399
  )
2404
2400
  busy.end()
2405
2401
 
@@ -2415,7 +2411,9 @@ class Simulation(MWindow):
2415
2411
  {
2416
2412
  "label": _("Simulate"),
2417
2413
  "icon": icons8_laser_beam_hazard,
2418
- "tip": _("Simulate the current laser job") + "\n" + _("(Right click: no optimisation)"),
2414
+ "tip": _("Simulate the current laser job")
2415
+ + "\n"
2416
+ + _("(Right click: no optimisation)"),
2419
2417
  "action": open_simulator,
2420
2418
  "action_right": open_simulator_simple,
2421
2419
  "rule_enabled": lambda cond: kernel.elements.have_burnable_elements(),
@@ -103,6 +103,8 @@ class SpoolerPanel(wx.Panel):
103
103
  self.context.setting(int, "spooler_sash_position", 0)
104
104
  self.context.setting(bool, "spool_history_clear_on_start", False)
105
105
  self.context.setting(bool, "spool_ignore_helper_jobs", True)
106
+ self.context.setting(bool, "silent_mode", False)
107
+ self.context(f".silent {'on' if self.context.silent_mode else 'off'}\n")
106
108
 
107
109
  self.splitter = wx.SplitterWindow(self, id=wx.ID_ANY, style=wx.SP_LIVE_UPDATE)
108
110
  sty = wx.BORDER_SUNKEN
@@ -131,11 +133,20 @@ class SpoolerPanel(wx.Panel):
131
133
  )
132
134
  )
133
135
  self.button_stop.SetBitmapFocus(
134
- icons8_emergency_stop_button.GetBitmap(resize=0.5 * get_default_icon_size(self.context))
136
+ icons8_emergency_stop_button.GetBitmap(
137
+ resize=0.5 * get_default_icon_size(self.context)
138
+ )
135
139
  )
136
140
  self.button_stop.SetBackgroundColour(self.context.themes.get("stop_bg"))
137
141
  self.button_stop.SetForegroundColour(self.context.themes.get("stop_fg"))
138
142
  self.button_stop.SetFocusColour(self.context.themes.get("stop_fg_focus"))
143
+ self.check_silent = wx.CheckBox(self.win_top, wx.ID_ANY)
144
+ self.check_silent.SetValue(self.context.silent_mode)
145
+ self.check_silent.SetToolTip(
146
+ _(
147
+ "If checked, the spooler will not emit any sound signals while processing jobs"
148
+ )
149
+ )
139
150
 
140
151
  self.list_job_spool = wxListCtrl(
141
152
  self.win_top,
@@ -145,9 +156,7 @@ class SpoolerPanel(wx.Panel):
145
156
  list_name="list_spoolerjobs",
146
157
  )
147
158
 
148
- self.info_label = wxStaticText(
149
- self.win_bottom, wx.ID_ANY, _("Completed jobs:")
150
- )
159
+ self.info_label = wxStaticText(self.win_bottom, wx.ID_ANY, _("Completed jobs:"))
151
160
  self.button_clear_history = wxButton(
152
161
  self.win_bottom, wx.ID_ANY, _("Clear History")
153
162
  )
@@ -184,7 +193,7 @@ class SpoolerPanel(wx.Panel):
184
193
  self.Bind(
185
194
  wx.EVT_LIST_ITEM_RIGHT_CLICK, self.on_item_rightclick, self.list_job_spool
186
195
  )
187
- # end wxGlade
196
+ self.Bind(wx.EVT_CHECKBOX, self.on_check_silent, self.check_silent)
188
197
  self._last_invokation = 0
189
198
  self.dirty = False
190
199
  self.update_buffer_size = False
@@ -298,6 +307,7 @@ class SpoolerPanel(wx.Panel):
298
307
  sizer_combo_cmds.Add(self.combo_device, 1, wx.ALIGN_CENTER_VERTICAL, 0)
299
308
  sizer_combo_cmds.Add(self.button_pause, 0, wx.EXPAND, 0)
300
309
  sizer_combo_cmds.Add(self.button_stop, 0, wx.EXPAND, 0)
310
+ sizer_combo_cmds.Add(self.check_silent, 0, wx.ALIGN_CENTER_VERTICAL, 0)
301
311
 
302
312
  sizer_top.Add(sizer_combo_cmds, 0, wx.EXPAND, 0)
303
313
  sizer_top.Add(self.list_job_spool, 4, wx.EXPAND, 0)
@@ -318,6 +328,10 @@ class SpoolerPanel(wx.Panel):
318
328
  self.Layout()
319
329
  # end wxGlade
320
330
 
331
+ def on_check_silent(self, event):
332
+ self.context.silent_mode = self.check_silent.GetValue()
333
+ self.context(f".silent {'on' if self.context.silent_mode else 'off'}\n")
334
+
321
335
  def on_sash_changed(self, event):
322
336
  position = self.splitter.GetSashPosition()
323
337
  self.context.spooler_sash_position = position
@@ -760,7 +774,10 @@ class SpoolerPanel(wx.Panel):
760
774
  info_s = f"{spool_obj.steps_done}/{spool_obj.steps_total}"
761
775
  if hasattr(spooler, "driver"):
762
776
  if hasattr(spooler.driver, "get_internal_queue_status"):
763
- internal_current, internal_total = spooler.driver.get_internal_queue_status()
777
+ (
778
+ internal_current,
779
+ internal_total,
780
+ ) = spooler.driver.get_internal_queue_status()
764
781
  if internal_current != 0:
765
782
  info_s += f" ({internal_current}/{internal_total})"
766
783
  except AttributeError:
@@ -1113,7 +1130,10 @@ class SpoolerPanel(wx.Panel):
1113
1130
  info_s = f"{spool_obj.steps_done}/{spool_obj.steps_total}"
1114
1131
  if hasattr(spooler, "driver"):
1115
1132
  if hasattr(spooler.driver, "get_internal_queue_status"):
1116
- internal_current, internal_total = spooler.driver.get_internal_queue_status()
1133
+ (
1134
+ internal_current,
1135
+ internal_total,
1136
+ ) = spooler.driver.get_internal_queue_status()
1117
1137
  if internal_current != 0:
1118
1138
  info_s += f" ({internal_current}/{internal_total})"
1119
1139
  except AttributeError:
@@ -6,7 +6,8 @@ from meerk40t.core.node.op_image import ImageOpNode
6
6
  from meerk40t.core.node.op_raster import RasterOpNode
7
7
  from meerk40t.gui.icons import EmptyIcon, icon_library
8
8
  from meerk40t.gui.laserrender import swizzlecolor
9
- from meerk40t.gui.wxutils import wxStaticBitmap, dip_size
9
+ from meerk40t.gui.wxutils import dip_size, wxStaticBitmap
10
+
10
11
  from .statusbarwidget import StatusBarWidget
11
12
 
12
13
  _ = wx.GetTranslation
@@ -28,24 +29,40 @@ class DefaultOperationWidget(StatusBarWidget):
28
29
  self.first_to_show = 0
29
30
 
30
31
  def node_label(self, node):
31
- if isinstance(node, CutOpNode):
32
- slabel = "Cut ({percent}, {speed}mm/s)"
33
- elif isinstance(node, EngraveOpNode):
34
- slabel = "Engrave ({percent}, {speed}mm/s)"
35
- elif isinstance(node, RasterOpNode):
36
- slabel = "Raster ({percent}, {speed}mm/s)"
37
- elif isinstance(node, ImageOpNode):
38
- slabel = "Image ({percent}, {speed}mm/s)"
32
+ percent = ""
33
+ if hasattr(node, "power"):
34
+ if getattr(self.context.device, "use_percent_for_power_display", False):
35
+ percent = f"{node.power / 10.0:.1f}%"
36
+ else:
37
+ percent = f"{node.power:.0f}ppi"
38
+ speed = ""
39
+ if hasattr(node, "speed"):
40
+ if getattr(self.context.device, "use_mm_min_for_speed_display", False):
41
+ speed = f"{node.speed * 60:.0f}mm/min"
42
+ else:
43
+ speed = f"{node.speed:.0f}mm/s"
44
+
45
+ op_map = {
46
+ CutOpNode: _("Cut ({percent}, {speed})"),
47
+ EngraveOpNode: _("Engrave ({percent}, {speed})"),
48
+ RasterOpNode: _("Raster ({percent}, {speed})"),
49
+ ImageOpNode: _("Image ({percent}, {speed})"),
50
+ }
51
+
52
+ for op_type, template in op_map.items():
53
+ if isinstance(node, op_type):
54
+ slabel = template.format(percent=percent, speed=speed)
55
+ break
39
56
  else:
40
57
  slabel = ""
41
- slabel = (
58
+
59
+ return (
42
60
  _("Assign the selection to:")
43
61
  + "\n"
44
62
  + slabel
45
63
  + "\n"
46
64
  + _("Right click for options")
47
65
  )
48
- return slabel
49
66
 
50
67
  def GenerateControls(self, parent, panelidx, identifier, context):
51
68
  def size_it(ctrl, dimen_x, dimen_y):
@@ -337,13 +354,52 @@ class DefaultOperationWidget(StatusBarWidget):
337
354
 
338
355
  menu.Destroy()
339
356
 
357
+ def _should_activate(self, idx, x, gap, residual):
358
+ # If no assigned operation, always inactive.
359
+ if self.assign_operations[idx] is None or idx < self.first_to_show:
360
+ return False, x, residual
361
+
362
+ btn_width = self.buttonsize_x
363
+ # When this is the last button:
364
+ if idx == len(self.assign_buttons) - 1 and not residual:
365
+ if x + btn_width > self.width:
366
+ return False, x, residual
367
+ else:
368
+ return True, x + gap + btn_width, False
369
+ # For non-last buttons:
370
+ if x + 2 * btn_width + gap > self.width:
371
+ return False, x, True
372
+ return True, x + gap + btn_width, residual
373
+
374
+ def _update_button_states(self, x, residual, gap):
375
+ """
376
+ Loops through all buttons (assign_buttons) and decides whether to activate or deactivate each one.
377
+ Conditions for each button:
378
+
379
+ - If the corresponding operation (self.assign_operations[idx]) is None, deactivate the button.
380
+ - Otherwise:
381
+ - Check if the button fits within the available width (self.width).
382
+ - If it doesn't fit, mark it as "residual" (indicating more buttons exist off-screen) and deactivate it.
383
+ - If it fits, activate it and update the horizontal position
384
+ """
385
+ for idx, btn in enumerate(self.assign_buttons):
386
+ active, x, residual = self._should_activate(idx, x, gap, residual)
387
+ self.SetActive(btn, active)
388
+ return residual
389
+
340
390
  def Show(self, showit=True):
341
- # Callback function that decides whether to show an element or not
391
+ """
392
+ Controls the visibility and layout of the operation assignment buttons in the status bar.
393
+ Updates which buttons are active and visible based on the current page and available width.
394
+
395
+ Args:
396
+ showit (bool): Whether to show the operation assignment controls.
397
+ """
342
398
  if showit:
399
+ # Determine how many buttons can fit horizontally on the screen
343
400
  self.page_size = int(
344
401
  (self.width - 2 * self.buttonsize_x) / self.buttonsize_x
345
402
  )
346
- # print(f"Page-Size: {self.page_size}, width={self.width}")
347
403
  x = 0
348
404
  gap = 0
349
405
  if self.first_to_show > 0:
@@ -351,36 +407,10 @@ class DefaultOperationWidget(StatusBarWidget):
351
407
  x = self.buttonsize_x + gap
352
408
  else:
353
409
  self.SetActive(self.btn_prev, False)
354
-
355
410
  residual = False
356
- for idx, btn in enumerate(self.assign_buttons):
357
- w = self.buttonsize_x
358
- btnflag = False
359
- if not residual:
360
- if self.assign_operations[idx] is None:
361
- self.SetActive(btn, False)
362
- else:
363
- if idx < self.first_to_show:
364
- btnflag = False
365
- elif idx == len(self.assign_buttons) - 1:
366
- # The last
367
- if x + w > self.width:
368
- residual = True
369
- btnflag = False
370
- else:
371
- btnflag = True
372
- x += gap + w
373
- else:
374
- if x + w + gap + self.buttonsize_x > self.width:
375
- residual = True
376
- btnflag = False
377
- else:
378
- btnflag = True
379
- x += gap + w
380
- self.SetActive(btn, btnflag)
411
+ residual = self._update_button_states(x, residual, gap)
381
412
  self.SetActive(self.btn_next, residual)
382
413
  self.SetActive(self.btn_matman, not residual)
383
-
384
414
  else:
385
415
  self.SetActive(self.btn_prev, False)
386
416
  for btn in self.assign_buttons:
@@ -392,16 +422,19 @@ class DefaultOperationWidget(StatusBarWidget):
392
422
 
393
423
  def on_prev(self, event):
394
424
  self.first_to_show -= self.page_size
395
- if self.first_to_show < 0:
396
- self.first_to_show = 0
425
+ self.first_to_show = max(self.first_to_show, 0)
397
426
  self.Show(True)
398
427
 
399
428
  def on_next(self, event):
400
- self.first_to_show += self.page_size
429
+ if (
430
+ self.first_to_show == 0
431
+ ): # We did not have a previous button, so we displayed one more than normal
432
+ self.first_to_show += self.page_size + 1
433
+ else:
434
+ self.first_to_show += self.page_size
401
435
  if self.first_to_show + self.page_size >= len(self.assign_buttons):
402
436
  self.first_to_show = len(self.assign_buttons) - self.page_size
403
- if self.first_to_show < 0:
404
- self.first_to_show = 0
437
+ self.first_to_show = max(self.first_to_show, 0)
405
438
  self.Show(True)
406
439
 
407
440
  def execute_on(self, targetop, use_parent):
@@ -409,9 +442,12 @@ class DefaultOperationWidget(StatusBarWidget):
409
442
  data = list(self.context.elements.elems(emphasized=True))
410
443
  for node in data:
411
444
  add_node = node
412
- if use_parent:
413
- if node.parent is not None and node.parent.type.startswith("effect"):
414
- add_node = node.parent
445
+ if (
446
+ use_parent
447
+ and node.parent is not None
448
+ and node.parent.type.startswith("effect")
449
+ ):
450
+ add_node = node.parent
415
451
  if add_node not in targetdata:
416
452
  targetdata.append(add_node)
417
453
 
@@ -61,7 +61,7 @@ class SimpleInfoWidget(StatusBarWidget):
61
61
  self.btn_next.Bind(wx.EVT_LEFT_DOWN, self.on_button_next)
62
62
  self.btn_next.Bind(wx.EVT_RIGHT_DOWN, self.on_button_prev)
63
63
 
64
- self.Add(self.info_text, 5, wx.EXPAND, 0)
64
+ self.Add(self.info_text, 5, 0, 0)
65
65
  self.Add(self.progress_bar, 1, wx.EXPAND, 0)
66
66
  self.Add(self.btn_next, 0, wx.EXPAND, 0)
67
67
  self.SetActive(self.btn_next, False)
@@ -201,7 +201,7 @@ class InformationWidget(SimpleInfoWidget):
201
201
  while new_height > 1024 or new_width > 1024:
202
202
  new_height //= 2
203
203
  new_width //= 2
204
-
204
+
205
205
  all_pixel = new_height * new_width
206
206
  if all_pixel > 0:
207
207
  try:
meerk40t/gui/tips.py CHANGED
@@ -22,7 +22,14 @@ from .icons import (
22
22
  icons8_manager,
23
23
  )
24
24
  from .mwindow import MWindow
25
- from .wxutils import TextCtrl, dip_size, wxButton, wxCheckBox, wxStaticBitmap, wxStaticText
25
+ from .wxutils import (
26
+ TextCtrl,
27
+ dip_size,
28
+ wxButton,
29
+ wxCheckBox,
30
+ wxStaticBitmap,
31
+ wxStaticText,
32
+ )
26
33
 
27
34
  _ = wx.GetTranslation
28
35
 
@@ -98,6 +105,9 @@ class TipPanel(wx.Panel):
98
105
  sizer_main.Add(button_sizer, 0, wx.EXPAND, 0)
99
106
 
100
107
  self.check_startup = wxCheckBox(self, wx.ID_ANY, _("Show tips at startup"))
108
+ self.check_startup.SetFont(
109
+ wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
110
+ )
101
111
  self.check_startup.SetToolTip(
102
112
  _(
103
113
  "Show tips at program start.\n"
@@ -234,6 +244,10 @@ class TipPanel(wx.Panel):
234
244
  self.image_tip.Show(True)
235
245
  return True
236
246
 
247
+ img_size = self.image_tip.GetSize()
248
+ if img_size[0] == 0 or img_size[1] == 0:
249
+ # Invalid display area
250
+ return False
237
251
  # self.image_tip.SetBitmap(wx.NullBitmap)
238
252
  self.image_tip.Show(False)
239
253
  self.tip_image = path