meerk40t 0.9.7010__py2.py3-none-any.whl → 0.9.7030__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 (77) hide show
  1. meerk40t/balormk/galvo_commands.py +1 -2
  2. meerk40t/core/cutcode/cutcode.py +1 -1
  3. meerk40t/core/cutplan.py +70 -2
  4. meerk40t/core/elements/branches.py +18 -4
  5. meerk40t/core/elements/element_treeops.py +43 -7
  6. meerk40t/core/elements/elements.py +49 -63
  7. meerk40t/core/elements/grid.py +8 -1
  8. meerk40t/core/elements/offset_clpr.py +4 -3
  9. meerk40t/core/elements/offset_mk.py +2 -1
  10. meerk40t/core/elements/shapes.py +379 -260
  11. meerk40t/core/elements/testcases.py +105 -0
  12. meerk40t/core/node/node.py +6 -3
  13. meerk40t/core/node/op_cut.py +9 -8
  14. meerk40t/core/node/op_dots.py +8 -8
  15. meerk40t/core/node/op_engrave.py +7 -7
  16. meerk40t/core/node/op_raster.py +8 -8
  17. meerk40t/core/planner.py +23 -0
  18. meerk40t/core/undos.py +1 -1
  19. meerk40t/core/wordlist.py +1 -0
  20. meerk40t/dxf/dxf_io.py +6 -0
  21. meerk40t/extra/encode_detect.py +8 -2
  22. meerk40t/extra/hershey.py +2 -3
  23. meerk40t/extra/inkscape.py +3 -5
  24. meerk40t/extra/mk_potrace.py +1959 -0
  25. meerk40t/extra/outerworld.py +2 -3
  26. meerk40t/extra/param_functions.py +2 -2
  27. meerk40t/extra/potrace.py +14 -10
  28. meerk40t/grbl/device.py +4 -1
  29. meerk40t/grbl/gui/grblcontroller.py +2 -2
  30. meerk40t/grbl/interpreter.py +1 -1
  31. meerk40t/gui/about.py +3 -5
  32. meerk40t/gui/basicops.py +3 -3
  33. meerk40t/gui/busy.py +75 -13
  34. meerk40t/gui/choicepropertypanel.py +365 -379
  35. meerk40t/gui/consolepanel.py +3 -3
  36. meerk40t/gui/gui_mixins.py +4 -1
  37. meerk40t/gui/hersheymanager.py +13 -3
  38. meerk40t/gui/laserpanel.py +12 -7
  39. meerk40t/gui/materialmanager.py +33 -6
  40. meerk40t/gui/plugin.py +9 -3
  41. meerk40t/gui/propertypanels/operationpropertymain.py +1 -1
  42. meerk40t/gui/ribbon.py +4 -1
  43. meerk40t/gui/scene/widget.py +1 -1
  44. meerk40t/gui/scenewidgets/rectselectwidget.py +19 -16
  45. meerk40t/gui/scenewidgets/selectionwidget.py +26 -20
  46. meerk40t/gui/simpleui.py +13 -8
  47. meerk40t/gui/simulation.py +22 -2
  48. meerk40t/gui/spoolerpanel.py +8 -11
  49. meerk40t/gui/themes.py +7 -1
  50. meerk40t/gui/tips.py +2 -3
  51. meerk40t/gui/toolwidgets/toolmeasure.py +4 -1
  52. meerk40t/gui/wxmeerk40t.py +32 -3
  53. meerk40t/gui/wxmmain.py +72 -6
  54. meerk40t/gui/wxmscene.py +95 -6
  55. meerk40t/gui/wxmtree.py +17 -11
  56. meerk40t/gui/wxutils.py +1 -1
  57. meerk40t/image/imagetools.py +21 -6
  58. meerk40t/kernel/kernel.py +31 -6
  59. meerk40t/kernel/settings.py +2 -0
  60. meerk40t/lihuiyu/device.py +9 -3
  61. meerk40t/main.py +22 -5
  62. meerk40t/network/console_server.py +52 -14
  63. meerk40t/network/web_server.py +15 -1
  64. meerk40t/ruida/device.py +5 -1
  65. meerk40t/ruida/gui/gui.py +6 -6
  66. meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
  67. meerk40t/ruida/rdjob.py +3 -3
  68. meerk40t/tools/geomstr.py +88 -0
  69. meerk40t/tools/polybool.py +2 -1
  70. meerk40t/tools/shxparser.py +92 -34
  71. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/METADATA +1 -1
  72. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/RECORD +77 -75
  73. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/WHEEL +1 -1
  74. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/LICENSE +0 -0
  75. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/entry_points.txt +0 -0
  76. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/top_level.txt +0 -0
  77. {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/zip-safe +0 -0
@@ -6,7 +6,7 @@ from wx import aui
6
6
 
7
7
  from meerk40t.gui.icons import STD_ICON_SIZE, icons8_console
8
8
  from meerk40t.gui.mwindow import MWindow
9
- from meerk40t.kernel import get_safe_path, signal_listener
9
+ from meerk40t.kernel import signal_listener
10
10
  from meerk40t.gui.wxutils import TextCtrl
11
11
 
12
12
  try:
@@ -78,7 +78,7 @@ def register_panel_console(window, context):
78
78
  aui.AuiPaneInfo()
79
79
  .Bottom()
80
80
  .Layer(2)
81
- .MinSize(600, 100)
81
+ .MinSize(100, 100)
82
82
  .FloatingSize(600, 230)
83
83
  .Caption(_("Console"))
84
84
  .Name("console")
@@ -600,7 +600,7 @@ class ConsolePanel(wx.ScrolledWindow):
600
600
  event.Skip(False)
601
601
 
602
602
  def history_filename(self):
603
- safe_dir = os.path.realpath(get_safe_path(self.context.kernel.name))
603
+ safe_dir = self.context.kernel.os_information["WORKDIR"]
604
604
  fname = os.path.join(safe_dir, "cmdhistory.log")
605
605
  is_there = os.path.exists(fname)
606
606
  return fname, is_there
@@ -584,7 +584,10 @@ class Warnings:
584
584
  image_node = node.node if hasattr(node, "node") else node
585
585
  if getattr(image_node, "hidden", False):
586
586
  continue
587
- opdpi = op.dpi if useop else image_node.dpi
587
+ if hasattr(image_node, "dpi"):
588
+ opdpi = op.dpi if useop else image_node.dpi
589
+ else:
590
+ opdpi = op.dpi
588
591
  step_x, step_y = self.context.device.view.dpi_to_steps(opdpi)
589
592
  step_x *= self.context.device.view.native_scale_x
590
593
  step_y *= self.context.device.view.native_scale_y
@@ -283,9 +283,19 @@ class FontGlyphPicker(wx.Dialog):
283
283
  return
284
284
  as_stroke = getattr(cfont, "STROKE_BASED", False)
285
285
  for c in cfont.glyphs:
286
- if ord(c) == 65535:
286
+ if isinstance(c, str):
287
+ if len(c) > 1:
288
+ # print (f"Strange: {c}, use {idx} instead")
289
+ continue
290
+ if ord(c) == 65535:
291
+ continue
292
+ cstr = str(c)
293
+ elif isinstance(c, int):
294
+ if c == 65535:
295
+ continue
296
+ cstr = chr(c)
297
+ else:
287
298
  continue
288
- cstr = str(c)
289
299
  hexa = cstr.encode("utf-8")
290
300
  item = self.list_glyphs.InsertItem(self.list_glyphs.ItemCount, hexa)
291
301
  self.list_glyphs.SetItem(item, 1, str(ord(cstr)))
@@ -294,7 +304,7 @@ class FontGlyphPicker(wx.Dialog):
294
304
  try:
295
305
  cfont.render(
296
306
  path,
297
- c,
307
+ cstr,
298
308
  True,
299
309
  12.0,
300
310
  1.0,
@@ -18,7 +18,7 @@ from meerk40t.gui.icons import (
18
18
  icons8_pentagon,
19
19
  icons8_save,
20
20
  )
21
- from meerk40t.gui.navigationpanels import Drag, Jog, MovePanel
21
+ from meerk40t.gui.navigationpanels import Drag, Jog, MovePanel, JogDistancePanel
22
22
  from meerk40t.gui.wxutils import (
23
23
  HoverButton,
24
24
  ScrolledPanel,
@@ -47,10 +47,14 @@ def register_panel_laser(window, context):
47
47
  jog_drag.SetupScrolling()
48
48
  jog_panel = Jog(jog_drag, wx.ID_ANY, context=context)
49
49
  drag_panel = Drag(jog_drag, wx.ID_ANY, context=context)
50
- main_sizer = wx.BoxSizer(wx.HORIZONTAL)
50
+ distance_panel = JogDistancePanel(jog_drag, wx.ID_ANY, context=context)
51
+ main_sizer = wx.BoxSizer(wx.VERTICAL)
52
+ sub_sizer = wx.BoxSizer(wx.HORIZONTAL)
51
53
  # main_sizer.AddStretchSpacer()
52
- main_sizer.Add(jog_panel, 1, wx.ALIGN_CENTER_VERTICAL, 0)
53
- main_sizer.Add(drag_panel, 1, wx.ALIGN_CENTER_VERTICAL, 0)
54
+ sub_sizer.Add(jog_panel, 1, wx.ALIGN_CENTER_VERTICAL, 0)
55
+ sub_sizer.Add(drag_panel, 1, wx.ALIGN_CENTER_VERTICAL, 0)
56
+ main_sizer.Add(sub_sizer, 1, wx.EXPAND, 0)
57
+ main_sizer.Add(distance_panel, 0, wx.EXPAND, 0)
54
58
  # main_sizer.AddStretchSpacer()
55
59
  jog_drag.SetSizer(main_sizer)
56
60
  jog_drag.Layout()
@@ -97,7 +101,7 @@ def register_panel_laser(window, context):
97
101
  page = notebook.GetCurrentPage()
98
102
  if page is None:
99
103
  return
100
- pages = [jog_panel, drag_panel] if page is jog_drag else [page]
104
+ pages = [jog_panel, drag_panel, distance_panel] if page is jog_drag else [page]
101
105
  for p in pages:
102
106
  if hasattr(p, "pane_show"):
103
107
  p.pane_show()
@@ -127,8 +131,9 @@ def register_panel_laser(window, context):
127
131
  else:
128
132
  panel_size = (wb_size[0] / 2, wb_size[1])
129
133
 
130
- jog_panel.set_icons(dimension=panel_size)
131
- drag_panel.set_icons(dimension=panel_size)
134
+ for panel in (jog_panel, drag_panel, distance_panel):
135
+ if hasattr(panel, "set_icons"):
136
+ panel.set_icons(dimension=panel_size)
132
137
 
133
138
  jog_drag.Bind(wx.EVT_SIZE, on_resize)
134
139
 
@@ -36,7 +36,6 @@ from meerk40t.gui.wxutils import (
36
36
  wxStaticText,
37
37
  wxTreeCtrl,
38
38
  )
39
- from meerk40t.kernel.kernel import get_safe_path
40
39
  from meerk40t.kernel.settings import Settings
41
40
  from meerk40t.svgelements import Color
42
41
 
@@ -389,6 +388,9 @@ class MaterialPanel(ScrolledPanel):
389
388
  self.list_preview.AppendColumn(
390
389
  _("Frequency") + " [kHz]", format=wx.LIST_FORMAT_LEFT, width=50
391
390
  )
391
+ self.list_preview.AppendColumn(
392
+ _("Passes"), format=wx.LIST_FORMAT_LEFT, width=50
393
+ )
392
394
  self.list_preview.resize_columns()
393
395
  self.list_preview.SetToolTip(_("Click to select / Right click for actions"))
394
396
  self.opinfo = {
@@ -919,7 +921,7 @@ class MaterialPanel(ScrolledPanel):
919
921
  if len(oplist) == 0:
920
922
  return
921
923
  opinfo["author"] = last_author
922
- directory = get_safe_path(self.context.kernel.name)
924
+ directory = self.context.kernel.os_information["WORKDIR"]
923
925
  local_file = os.path.join(directory, "op_export.cfg")
924
926
  if os.path.exists(local_file):
925
927
  try:
@@ -1317,6 +1319,9 @@ class MaterialPanel(ScrolledPanel):
1317
1319
  self.op_data.write_persistent(
1318
1320
  section_name, "passes", numeric_value
1319
1321
  )
1322
+ self.op_data.write_persistent(
1323
+ section_name, "passes_custom", True
1324
+ )
1320
1325
  elif param == "speed":
1321
1326
  if numeric_value != 0:
1322
1327
  speedval = numeric_value
@@ -1592,6 +1597,9 @@ class MaterialPanel(ScrolledPanel):
1592
1597
  self.op_data.write_persistent(
1593
1598
  section_name, "passes", numeric_value
1594
1599
  )
1600
+ self.op_data.write_persistent(
1601
+ section_name, "passes_custom", True
1602
+ )
1595
1603
  elif param == "markspeed":
1596
1604
  if numeric_value != 0:
1597
1605
  speedval = numeric_value
@@ -2066,6 +2074,7 @@ class MaterialPanel(ScrolledPanel):
2066
2074
  oplabel = self.op_data.read_persistent(str, subsection, "label", "")
2067
2075
  speed = self.op_data.read_persistent(str, subsection, "speed", "")
2068
2076
  power = self.op_data.read_persistent(str, subsection, "power", "")
2077
+ passes = self.op_data.read_persistent(str, subsection, "passes", "")
2069
2078
  frequency = self.op_data.read_persistent(
2070
2079
  str, subsection, "frequency", ""
2071
2080
  )
@@ -2074,6 +2083,8 @@ class MaterialPanel(ScrolledPanel):
2074
2083
  command = self.op_data.read_persistent(str, subsection, "command", "")
2075
2084
  if power == "" and optype.startswith("op "):
2076
2085
  power = "1000"
2086
+ if passes == "" and optype.startswith("op "):
2087
+ passes = "1"
2077
2088
  list_id = self.list_preview.InsertItem(
2078
2089
  self.list_preview.GetItemCount(), f"#{idx}"
2079
2090
  )
@@ -2093,6 +2104,7 @@ class MaterialPanel(ScrolledPanel):
2093
2104
  self.list_preview.SetItem(list_id, 4, power)
2094
2105
  self.list_preview.SetItem(list_id, 5, speed)
2095
2106
  self.list_preview.SetItem(list_id, 6, frequency)
2107
+ self.list_preview.SetItem(list_id, 7, passes)
2096
2108
  key = get_key(optype, opc)
2097
2109
  if key in icon_dict:
2098
2110
  imgid = icon_dict[key]
@@ -2576,6 +2588,7 @@ class MaterialPanel(ScrolledPanel):
2576
2588
  "power": "1000",
2577
2589
  "label": "Raster ({percent}, {speed}mm/s)",
2578
2590
  "color": "#000000",
2591
+ "passes": "1",
2579
2592
  }
2580
2593
  if self.is_balor:
2581
2594
  op_dict["frequency"] = "35"
@@ -2675,15 +2688,16 @@ class MaterialPanel(ScrolledPanel):
2675
2688
  # 4 "Power"
2676
2689
  # 5 "Speed"
2677
2690
  # 6 "Frequency"
2691
+ # 7 "Passes"
2678
2692
  if self.is_balor:
2679
2693
  p1 = 0.15
2680
2694
  p2 = 0.35
2681
- p3 = (1.0 - p1 - p2) / 4
2695
+ p3 = (1.0 - p1 - p2) / 5
2682
2696
  p4 = p3
2683
2697
  else:
2684
2698
  p1 = 0.15
2685
2699
  p2 = 0.40
2686
- p3 = (1.0 - p1 - p2) / 3
2700
+ p3 = (1.0 - p1 - p2) / 4
2687
2701
  p4 = 0
2688
2702
  self.list_preview.SetColumnWidth(0, int(p3 * remaining))
2689
2703
  self.list_preview.SetColumnWidth(1, int(p1 * remaining))
@@ -2692,6 +2706,7 @@ class MaterialPanel(ScrolledPanel):
2692
2706
  self.list_preview.SetColumnWidth(4, int(p3 * remaining))
2693
2707
  self.list_preview.SetColumnWidth(5, int(p3 * remaining))
2694
2708
  self.list_preview.SetColumnWidth(6, int(p4 * remaining))
2709
+ self.list_preview.SetColumnWidth(7, int(p3 * remaining))
2695
2710
 
2696
2711
  def before_operation_update(self, event):
2697
2712
  list_id = event.GetIndex() # Get the current row
@@ -2708,7 +2723,7 @@ class MaterialPanel(ScrolledPanel):
2708
2723
  ok = False
2709
2724
  except (AttributeError, KeyError):
2710
2725
  ok = False
2711
- if col_id not in range(2, 7):
2726
+ if col_id not in range(2, 7 + 1):
2712
2727
  ok = False
2713
2728
  if col_id == 6 and not self.is_balor:
2714
2729
  ok = False
@@ -2724,7 +2739,7 @@ class MaterialPanel(ScrolledPanel):
2724
2739
  index = self.list_preview.GetItemData(list_id)
2725
2740
  key = self.get_nth_operation(index)
2726
2741
 
2727
- if list_id >= 0 and col_id in range(2, 7):
2742
+ if list_id >= 0 and col_id in range(2, 7 + 1):
2728
2743
  if col_id == 2:
2729
2744
  # id
2730
2745
  self.op_data.write_persistent(key, "id", new_data)
@@ -2761,6 +2776,18 @@ class MaterialPanel(ScrolledPanel):
2761
2776
  except ValueError:
2762
2777
  event.Veto()
2763
2778
  return
2779
+ elif col_id == 7:
2780
+ # Passes
2781
+ try:
2782
+ new_data = int(new_data)
2783
+ if new_data < 1:
2784
+ new_data = 1
2785
+ self.op_data.write_persistent(key, "passes_custom", bool(new_data != 1))
2786
+ self.op_data.write_persistent(key, "passes", new_data)
2787
+ new_data = f"{new_data}"
2788
+ except ValueError:
2789
+ event.Veto()
2790
+ return
2764
2791
  # Set the new data in the listctrl
2765
2792
  self.op_data.write_configuration()
2766
2793
  self.list_preview.SetItem(list_id, col_id, new_data)
meerk40t/gui/plugin.py CHANGED
@@ -327,13 +327,19 @@ and a wxpython version <= 4.1.1."""
327
327
 
328
328
  kernel.yesno = yesno_popup
329
329
 
330
- from meerk40t.gui.busy import BusyInfo
330
+ from meerk40t.gui.busy import SimpleBusyInfo, BusyInfo
331
331
 
332
- kargs = {}
332
+ kargs = {"kernel": kernel,}
333
333
  if kernel.themes.dark:
334
334
  kargs["bgcolor"] = kernel.themes.get("win_bg")
335
335
  kargs["fgcolor"] = kernel.themes.get("win_fg")
336
- kernel.busyinfo = BusyInfo(**kargs)
336
+ if kernel.os_information["OS_NAME"] != "Linux":
337
+ # The Linux implementation of wxWidgets
338
+ # cannot properly update controls (n idea why,
339
+ # any hint to circumvent this would be welcome)
340
+ kernel.busyinfo = BusyInfo(**kargs)
341
+ else:
342
+ kernel.busyinfo = SimpleBusyInfo(**kargs)
337
343
 
338
344
  @kernel.console_argument("message")
339
345
  @kernel.console_command("notify", hidden=True)
@@ -487,7 +487,7 @@ class SpeedPpiPanel(wx.Panel):
487
487
  self.update_power_speed_properties()
488
488
  except RuntimeError:
489
489
  # Pane was already destroyed
490
- pass
490
+ return
491
491
  self.set_widgets(self.operation)
492
492
 
493
493
  def update_power_speed_properties(self):
meerk40t/gui/ribbon.py CHANGED
@@ -563,7 +563,10 @@ class Button:
563
563
  """
564
564
 
565
565
  def toggle_click(origin, *args):
566
- set_value = getattr(self.object, self.toggle_attr) if self.toggle_attr else not self.toggle
566
+ # Whats the value to set?
567
+ set_value = args[0] if args else not self.toggle
568
+ # But if we have a toggle_attr then this has precedence
569
+ set_value = getattr(self.object, self.toggle_attr) if self.toggle_attr else set_value
567
570
  self.set_button_toggle(set_value)
568
571
 
569
572
  self.context.listen(signal, toggle_click)
@@ -103,7 +103,7 @@ class Widget(list):
103
103
 
104
104
  try:
105
105
  self.process_draw(gc)
106
- except Exception as e:
106
+ except OSError as e:
107
107
  print (f"Could not process draw [{e}]")
108
108
  for i in range(len(self) - 1, -1, -1):
109
109
  widget = self[i]
@@ -257,22 +257,25 @@ class RectSelectWidget(Widget):
257
257
  """
258
258
  Calculates the shortest distance between two arrays of 2-dimensional points.
259
259
  """
260
- # Calculate the Euclidean distance between each point in p1 and p2
261
- if tuplemode:
262
- # For an array of tuples:
263
- dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
264
- else:
265
- # For an array of complex numbers
266
- dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
267
-
268
- # Find the minimum distance and its corresponding indices
269
- min_dist = np.min(dist)
270
- if np.isnan(min_dist):
271
- return None, 0, 0
272
- min_indices = np.argwhere(dist == min_dist)
273
-
274
- # Return the coordinates of the two points
275
- return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
260
+ try:
261
+ # Calculate the Euclidean distance between each point in p1 and p2
262
+ if tuplemode:
263
+ # For an array of tuples:
264
+ dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
265
+ else:
266
+ # For an array of complex numbers
267
+ dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
268
+
269
+ # Find the minimum distance and its corresponding indices
270
+ min_dist = np.min(dist)
271
+ if np.isnan(min_dist):
272
+ return None, 0, 0
273
+ min_indices = np.argwhere(dist == min_dist)
274
+
275
+ # Return the coordinates of the two points
276
+ return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
277
+ except Exception: # out of memory eg
278
+ return None, None, None
276
279
 
277
280
  b = self.scene.context.elements._emphasized_bounds
278
281
  if b is None:
@@ -31,6 +31,7 @@ from meerk40t.gui.scene.scene import (
31
31
  from meerk40t.gui.scene.sceneconst import HITCHAIN_HIT_AND_DELEGATE
32
32
  from meerk40t.gui.scene.widget import Widget
33
33
  from meerk40t.gui.wxutils import (
34
+ dip_size,
34
35
  StaticBoxSizer,
35
36
  create_menu_for_node,
36
37
  get_gc_full_scale,
@@ -268,7 +269,7 @@ class BorderWidget(Widget):
268
269
  if self.show_rb:
269
270
  gc.StrokeLine(sx * center_x, sy * self.bottom, sx * center_x, sy * bed_h)
270
271
  gc.StrokeLine(sx * self.right, sy * center_y, sx * bed_w, sy * center_y)
271
-
272
+
272
273
  mypen.SetStyle(wx.PENSTYLE_DOT)
273
274
  gc.SetPen(mypen)
274
275
  gc.StrokeLine(sx * self.left, sy * self.top, sx * self.right, sy * self.top)
@@ -335,7 +336,7 @@ class BorderWidget(Widget):
335
336
  s_txt = str(Length(amount=rpos, digits=2, preferred_units=units))
336
337
  (t_width, t_height) = gc.GetTextExtent(s_txt)
337
338
  pos = self.right + rpos / 2.0 - t_width / 2
338
- if pos - t_width - distance <= self.right:
339
+ if pos - distance <= self.right:
339
340
  pos = self.right + distance
340
341
  gc.DrawText(s_txt, pos, center_y)
341
342
 
@@ -1610,23 +1611,26 @@ class MoveWidget(Widget):
1610
1611
  """
1611
1612
  Calculates the shortest distance between two arrays of 2-dimensional points.
1612
1613
  """
1613
- # Calculate the Euclidean distance between each point in p1 and p2
1614
- if tuplemode:
1615
- # For an array of tuples:
1616
- dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
1617
- else:
1618
- # For an array of complex numbers
1619
- dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
1620
- # Find the minimum distance and its corresponding indices
1621
- min_dist = np.min(dist)
1622
- if np.isnan(min_dist):
1623
- # print (f"Encountered the infamous bug: {p1} {p2}")
1624
- # Still need an example when that happens
1625
- return None, 0, 0
1626
- min_indices = np.argwhere(dist == min_dist)
1627
-
1628
- # Return the coordinates of the two points
1629
- return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
1614
+ try:
1615
+ # Calculate the Euclidean distance between each point in p1 and p2
1616
+ if tuplemode:
1617
+ # For an array of tuples:
1618
+ dist = np.sqrt(np.sum((p1[:, np.newaxis] - p2) ** 2, axis=2))
1619
+ else:
1620
+ # For an array of complex numbers
1621
+ dist = np.abs(p1[:, np.newaxis] - p2[np.newaxis, :])
1622
+ # Find the minimum distance and its corresponding indices
1623
+ min_dist = np.min(dist)
1624
+ if np.isnan(min_dist):
1625
+ # print (f"Encountered the infamous bug: {p1} {p2}")
1626
+ # Still need an example when that happens
1627
+ return None, 0, 0
1628
+ min_indices = np.argwhere(dist == min_dist)
1629
+
1630
+ # Return the coordinates of the two points
1631
+ return min_dist, p1[min_indices[0][0]], p2[min_indices[0][1]]
1632
+ except Exception: # out of memory eg
1633
+ return None, None, None
1630
1634
 
1631
1635
  b = elements._emphasized_bounds
1632
1636
  if b is None:
@@ -2390,6 +2394,8 @@ class SelectionWidget(Widget):
2390
2394
  self.handle_pen.SetStyle(wx.PENSTYLE_SOLID)
2391
2395
  # want to have sharp edges
2392
2396
  self.handle_pen.SetJoin(wx.JOIN_MITER)
2397
+ fact = dip_size(self.scene.pane, 100, 100)
2398
+ self.font_size_factor = (fact[0] + fact[1]) / 100 * 0.5
2393
2399
 
2394
2400
  self.gc = None
2395
2401
  self.reset_variables()
@@ -2797,7 +2803,7 @@ class SelectionWidget(Widget):
2797
2803
  try:
2798
2804
  factor = math.sqrt(abs(matrix.determinant))
2799
2805
  self.line_width = 2.0 / factor
2800
- self.font_size = 14.0 / factor
2806
+ self.font_size = 12.0 / factor * self.font_size_factor
2801
2807
  except ZeroDivisionError:
2802
2808
  matrix.reset()
2803
2809
  return
meerk40t/gui/simpleui.py CHANGED
@@ -13,7 +13,7 @@ from wx import aui
13
13
  from ..core.exceptions import BadFileError
14
14
  from .icons import get_default_icon_size, icons8_computer_support, icons8_opened_folder
15
15
  from .mwindow import MWindow
16
- from .navigationpanels import Drag, Jog
16
+ from .navigationpanels import Drag, Jog, JogDistancePanel
17
17
  from .wxutils import StaticBoxSizer, TextCtrl, wxButton, wxStaticText
18
18
 
19
19
  _ = wx.GetTranslation
@@ -26,19 +26,24 @@ class JogMovePanel(wx.Panel):
26
26
  wx.Panel.__init__(self, *args, **kwds)
27
27
  self.context = context
28
28
  self.context.themes.set_window_colors(self)
29
-
29
+ distance_panel = JogDistancePanel(self, wx.ID_ANY, context=context)
30
30
  jog_panel = Jog(self, wx.ID_ANY, context=context)
31
31
  drag_panel = Drag(self, wx.ID_ANY, context=context)
32
- self.panels = [jog_panel, drag_panel]
33
- self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
34
- self.main_sizer.Add(jog_panel, 1, wx.EXPAND, 0)
35
- self.main_sizer.Add(drag_panel, 1, wx.EXPAND, 0)
32
+ self.panels = [distance_panel, jog_panel, drag_panel]
33
+ self.main_sizer = wx.BoxSizer(wx.VERTICAL)
34
+ self.jog_sizer = wx.BoxSizer(wx.HORIZONTAL)
35
+ self.jog_sizer.Add(jog_panel, 1, wx.EXPAND, 0)
36
+ self.jog_sizer.Add(drag_panel, 1, wx.EXPAND, 0)
37
+ self.main_sizer.Add(distance_panel, 0, wx.EXPAND, 0)
38
+ self.main_sizer.Add(self.jog_sizer, 1, wx.EXPAND, 0)
36
39
  self.SetSizer(self.main_sizer)
37
40
  self.Layout()
38
41
  self.Bind(wx.EVT_SIZE, self.on_resize)
42
+ # Force initial resizing
43
+ wx.CallAfter(self.on_resize)
39
44
 
40
- def on_resize(self, event):
41
- wb_size = event.GetSize()
45
+ def on_resize(self, event=None):
46
+ wb_size = self.jog_sizer.GetSize()
42
47
  panel_size = (wb_size[0] / 2, wb_size[1])
43
48
  for p in self.panels:
44
49
  if hasattr(p, "set_icons"):
@@ -823,7 +823,7 @@ class SimulationPanel(wx.Panel, Job):
823
823
  job_name="cache_updater",
824
824
  interval=0.25,
825
825
  times=1,
826
- run_main=False,
826
+ run_main=True,
827
827
  )
828
828
 
829
829
  self.job_name = "simulate"
@@ -1573,6 +1573,8 @@ class SimulationPanel(wx.Panel, Job):
1573
1573
  self._stop()
1574
1574
  return
1575
1575
  # Refresh cutcode
1576
+ self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
1577
+
1576
1578
  if self.plan_name:
1577
1579
  self.cutplan = self.context.planner.get_or_make_plan(self.plan_name)
1578
1580
  else:
@@ -1621,6 +1623,8 @@ class SimulationPanel(wx.Panel, Job):
1621
1623
 
1622
1624
  self._startup()
1623
1625
  self.request_refresh()
1626
+ self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
1627
+
1624
1628
 
1625
1629
  @signal_listener("device;modified")
1626
1630
  @signal_listener("plan")
@@ -1685,7 +1689,11 @@ class SimulationPanel(wx.Panel, Job):
1685
1689
  self.options_optimize.Enable(False)
1686
1690
 
1687
1691
  def cache_updater(self):
1688
- self.button_spool.Enable(False)
1692
+ try:
1693
+ self.button_spool.Enable(False)
1694
+ except RuntimeError:
1695
+ # Control no longer existant
1696
+ return
1689
1697
  msg = self.button_spool.GetLabel()
1690
1698
  self.button_spool.SetLabel(_("Calculating"))
1691
1699
  for cut in self.cutcode:
@@ -1837,6 +1845,15 @@ class SimulationPanel(wx.Panel, Job):
1837
1845
  event.Skip()
1838
1846
 
1839
1847
  def on_redo_it(self, event):
1848
+ # Dont occupy gui event handling too long
1849
+ wx.CallAfter(self.redo_action)
1850
+
1851
+ def redo_action(self):
1852
+ self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
1853
+ self.btn_redo_it.SetLabel(_("Preparing simulation..."))
1854
+ self.btn_redo_it.Enable(False)
1855
+ self.btn_redo_it.Refresh()
1856
+ self.btn_redo_it.Update()
1840
1857
  busy = self.context.kernel.busyinfo
1841
1858
  busy.start(msg=_("Preparing simulation..."))
1842
1859
 
@@ -1853,6 +1870,9 @@ class SimulationPanel(wx.Panel, Job):
1853
1870
  )
1854
1871
  busy.end()
1855
1872
  self._refresh_simulated_plan()
1873
+ self.btn_redo_it.Enable(True)
1874
+ self.btn_redo_it.SetLabel(_("Recalculate"))
1875
+ self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
1856
1876
 
1857
1877
  def pane_show(self):
1858
1878
  self.Layout()
@@ -23,7 +23,7 @@ from meerk40t.gui.wxutils import (
23
23
  wxListCtrl,
24
24
  wxStaticText,
25
25
  )
26
- from meerk40t.kernel import Job, get_safe_path, signal_listener
26
+ from meerk40t.kernel import Job, signal_listener
27
27
 
28
28
  _ = wx.GetTranslation
29
29
 
@@ -330,7 +330,7 @@ class SpoolerPanel(wx.Panel):
330
330
  self.current_item = event.Index
331
331
 
332
332
  def write_csv(self):
333
- filename = Path(get_safe_path(self.context.kernel.name, create=True)).joinpath(
333
+ filename = Path(self.context.kernel.os_information["WORKDIR"]).joinpath(
334
334
  "history.csv"
335
335
  )
336
336
  if self.filter_device:
@@ -673,15 +673,6 @@ class SpoolerPanel(wx.Panel):
673
673
 
674
674
  return routine
675
675
 
676
- def pane_show(self, *args):
677
- self.shown = True
678
- self.context.schedule(self.timerjob)
679
- self.refresh_spooler_list()
680
-
681
- def pane_hide(self, *args):
682
- self.context.unschedule(self.timerjob)
683
- self.shown = False
684
-
685
676
  @staticmethod
686
677
  def _name_str(named_obj):
687
678
  try:
@@ -1171,10 +1162,16 @@ class SpoolerPanel(wx.Panel):
1171
1162
  self.on_device_update(None)
1172
1163
 
1173
1164
  def pane_show(self):
1165
+ self.shown = True
1174
1166
  self.list_job_history.load_column_widths()
1175
1167
  self.list_job_spool.load_column_widths()
1168
+ self.context.schedule(self.timerjob)
1169
+ self.refresh_spooler_list()
1176
1170
 
1177
1171
  def pane_hide(self):
1172
+ self.context.unschedule(self.timerjob)
1173
+ self.shown = False
1174
+
1178
1175
  self.list_job_history.save_column_widths()
1179
1176
  self.list_job_spool.save_column_widths()
1180
1177
 
meerk40t/gui/themes.py CHANGED
@@ -31,6 +31,9 @@ def color_distance(c1:wx.Colour, c2:wx.Colour) -> bool:
31
31
  # print (f"Distance from {c1.GetAsString()} to {c2.GetAsString()} = {sqrt(sq_dist)}")
32
32
  return sqrt(sq_dist)
33
33
 
34
+ def inverted_color(c1:wx.Colour) -> wx.Colour:
35
+ return wx.Colour(255-c1.red, 255-c1.green, 255-c1.blue, c1.alpha)
36
+
34
37
  def is_a_bright_color(c1):
35
38
  return color_distance(c1, wx.BLACK) > color_distance(c1, wx.WHITE)
36
39
 
@@ -132,6 +135,10 @@ class Themes(Service):
132
135
  tp["inactive_fg"] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT)
133
136
  # for key, col in tp.items():
134
137
  # print (f'tp["{key}"] = wx.Colour({col.red}, {col.green}, {col.blue}, {col.alpha})')
138
+ for p1, p2 in (("label_bg", "label_fg"), ("button_bg", "button_fg"), ("text_bg", "text_fg"), ("list_bg", "list_fg")):
139
+ inv_col = inverted_color(tp[p2])
140
+ if color_distance(tp[p2], tp[p1]) < color_distance(inv_col, tp[p1]):
141
+ tp[p2] = inv_col
135
142
  if system() != "Darwin":
136
143
  # Alas, Darwin does not properly support overloading of colors...
137
144
  if not self.dark and is_a_dark_color(tp["win_bg"]):
@@ -166,7 +173,6 @@ class Themes(Service):
166
173
  tp["highlight"] = wx.Colour(0, 0, 255)
167
174
  tp["inactive_bg"] = wx.Colour(46, 46, 46)
168
175
  tp["inactive_fg"] = base_fg
169
-
170
176
  tp["pause_bg"] = (
171
177
  wx.Colour(87, 87, 0) if self._dark else wx.Colour(200, 200, 0)
172
178
  )
meerk40t/gui/tips.py CHANGED
@@ -11,7 +11,6 @@ import webbrowser
11
11
 
12
12
  import wx
13
13
 
14
- from ..kernel import get_safe_path
15
14
  from .icons import (
16
15
  icon_outline,
17
16
  icon_youtube,
@@ -43,7 +42,7 @@ class TipPanel(wx.Panel):
43
42
  self.tip_image = ""
44
43
  self.tips = []
45
44
 
46
- safe_dir = os.path.realpath(get_safe_path(self.context.kernel.name))
45
+ safe_dir = self.context.kernel.os_information["WORKDIR"]
47
46
  self.local_file = os.path.join(safe_dir, "tips.txt")
48
47
 
49
48
  self.setup_tips()
@@ -538,7 +537,7 @@ class TipPanel(wx.Panel):
538
537
  Check for existence of a subdirectory to store images
539
538
  and create it if not found
540
539
  """
541
- safe_dir = os.path.realpath(get_safe_path(self.context.kernel.name))
540
+ safe_dir = self.context.kernel.os_information["WORKDIR"]
542
541
  cache_dir = os.path.join(safe_dir, "tip_images")
543
542
  if not os.path.exists(cache_dir):
544
543
  try: