meerk40t 0.9.7020__py2.py3-none-any.whl → 0.9.7040__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 (98) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +28 -11
  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/cutcode/cutcode.py +1 -1
  14. meerk40t/core/cutplan.py +169 -43
  15. meerk40t/core/elements/element_treeops.py +444 -147
  16. meerk40t/core/elements/elements.py +100 -9
  17. meerk40t/core/elements/grid.py +8 -1
  18. meerk40t/core/elements/offset_mk.py +2 -1
  19. meerk40t/core/elements/shapes.py +618 -279
  20. meerk40t/core/elements/tree_commands.py +10 -5
  21. meerk40t/core/node/elem_ellipse.py +18 -8
  22. meerk40t/core/node/elem_image.py +51 -19
  23. meerk40t/core/node/elem_line.py +18 -8
  24. meerk40t/core/node/elem_path.py +18 -8
  25. meerk40t/core/node/elem_point.py +10 -4
  26. meerk40t/core/node/elem_polyline.py +19 -11
  27. meerk40t/core/node/elem_rect.py +18 -8
  28. meerk40t/core/node/elem_text.py +11 -5
  29. meerk40t/core/node/filenode.py +2 -8
  30. meerk40t/core/node/groupnode.py +11 -11
  31. meerk40t/core/node/image_processed.py +11 -5
  32. meerk40t/core/node/image_raster.py +11 -5
  33. meerk40t/core/node/node.py +70 -19
  34. meerk40t/core/node/refnode.py +2 -1
  35. meerk40t/core/planner.py +23 -0
  36. meerk40t/core/svg_io.py +91 -34
  37. meerk40t/core/undos.py +1 -1
  38. meerk40t/core/wordlist.py +1 -0
  39. meerk40t/device/dummydevice.py +7 -1
  40. meerk40t/dxf/dxf_io.py +6 -0
  41. meerk40t/extra/mk_potrace.py +1959 -0
  42. meerk40t/extra/param_functions.py +1 -1
  43. meerk40t/extra/potrace.py +14 -10
  44. meerk40t/extra/vtracer.py +222 -0
  45. meerk40t/grbl/device.py +81 -8
  46. meerk40t/grbl/interpreter.py +1 -1
  47. meerk40t/gui/about.py +21 -3
  48. meerk40t/gui/basicops.py +3 -3
  49. meerk40t/gui/choicepropertypanel.py +1 -4
  50. meerk40t/gui/devicepanel.py +20 -16
  51. meerk40t/gui/gui_mixins.py +8 -1
  52. meerk40t/gui/icons.py +330 -253
  53. meerk40t/gui/laserpanel.py +8 -3
  54. meerk40t/gui/laserrender.py +41 -21
  55. meerk40t/gui/magnetoptions.py +158 -65
  56. meerk40t/gui/materialtest.py +229 -39
  57. meerk40t/gui/navigationpanels.py +229 -24
  58. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  59. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  60. meerk40t/gui/ribbon.py +6 -1
  61. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  62. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  63. meerk40t/gui/simulation.py +75 -77
  64. meerk40t/gui/spoolerpanel.py +6 -9
  65. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  66. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  67. meerk40t/gui/themes.py +7 -1
  68. meerk40t/gui/tips.py +15 -1
  69. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  70. meerk40t/gui/wxmeerk40t.py +26 -0
  71. meerk40t/gui/wxmmain.py +242 -114
  72. meerk40t/gui/wxmscene.py +180 -4
  73. meerk40t/gui/wxmtree.py +4 -2
  74. meerk40t/gui/wxutils.py +60 -15
  75. meerk40t/image/imagetools.py +130 -66
  76. meerk40t/internal_plugins.py +4 -0
  77. meerk40t/kernel/kernel.py +49 -22
  78. meerk40t/kernel/settings.py +29 -8
  79. meerk40t/lihuiyu/device.py +30 -12
  80. meerk40t/main.py +22 -5
  81. meerk40t/moshi/device.py +20 -6
  82. meerk40t/network/console_server.py +22 -6
  83. meerk40t/newly/device.py +10 -3
  84. meerk40t/newly/gui/gui.py +10 -0
  85. meerk40t/ruida/device.py +22 -2
  86. meerk40t/ruida/gui/gui.py +6 -6
  87. meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
  88. meerk40t/ruida/loader.py +6 -3
  89. meerk40t/ruida/rdjob.py +3 -3
  90. meerk40t/tools/geomstr.py +195 -39
  91. meerk40t/tools/rasterplotter.py +179 -93
  92. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
  93. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +98 -96
  94. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +1 -1
  95. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
  96. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
  97. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
  98. {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/zip-safe +0 -0
@@ -435,7 +435,7 @@ def plugin(kernel, lifecycle):
435
435
  @self.console_argument("inversions", nargs="*", type=int)
436
436
  @context.console_command(
437
437
  "ffractal",
438
- help=_("ffractal iterations"),
438
+ help=_("fractal iterations"),
439
439
  output_type="geometry",
440
440
  hidden=True,
441
441
  )
meerk40t/extra/potrace.py CHANGED
@@ -48,21 +48,25 @@ potracer: https://github.com/tatarize/potrace
48
48
 
49
49
  def plugin(kernel, lifecycle=None):
50
50
  if lifecycle == "invalidate":
51
+ invalid = False
52
+ return invalid
53
+
54
+ if lifecycle == "register":
55
+ _ = kernel.translation
56
+ import numpy
57
+ valid = False
51
58
  try:
52
- import numpy
53
59
  import potrace
54
- if not hasattr(potrace, "Bitmap"):
60
+ if hasattr(potrace, "Bitmap"):
61
+ valid = True
62
+ else:
55
63
  # This is a strange variant, that we do not support!
56
64
  print ("The version of potrace that is installed on your machine is incompatible. Please report this to the developers.")
57
- return True
58
65
  except ImportError:
59
- # print("Potrace plugin could not load because potracer/pypotrace is not installed.")
60
- return True
61
-
62
- if lifecycle == "register":
63
- _ = kernel.translation
64
- import numpy
65
- import potrace
66
+ valid = False
67
+
68
+ if not valid:
69
+ from . import mk_potrace as potrace
66
70
 
67
71
  def make_vector(
68
72
  image,
@@ -0,0 +1,222 @@
1
+ """
2
+ Vtracer https://github.com/visioncortex/vtracer
3
+ visioncortex VTracer is an open source software to convert raster images (like jpg & png)
4
+ into vector graphics (svg). It can vectorize graphics and photographs and trace the curves
5
+ to output compact vector files.
6
+ Comparing to [Potrace](http://potrace.sourceforge.net/) which only accept binarized
7
+ inputs (Black & White pixmap), VTracer has an image processing pipeline which
8
+ can handle colored high resolution scans.
9
+ Comparing to Adobe Illustrator's [Image Trace](https://helpx.adobe.com/illustrator/using/image-trace.html),
10
+ VTracer's output is much more compact (less shapes) as we adopt a stacking strategy
11
+ and avoid producing shapes with holes.
12
+ A technical description of the algorithm is on [visioncortex.org/vtracer-docs](//www.visioncortex.org/vtracer-docs).
13
+ To use it we need to have the python interface in place: pip install vtracer
14
+ """
15
+
16
+ """
17
+ Not needed left here for reference purposes
18
+
19
+ def simplified_load(source : str, bbox : tuple) -> list:
20
+ from meerk40t.tools.geomstr import Geomstr
21
+ from meerk40t.svgelements import Matrix, Color
22
+ from meerk40t.core.node.elem_path import PathNode
23
+ from time import perf_counter
24
+ t0 = perf_counter()
25
+ time_geom = 0
26
+ time_trans = 0
27
+ time_read = 0
28
+ time_bbox = 0
29
+ result_list = []
30
+ p_pattern = '<path d="'
31
+ t_pattern = 'transform="'
32
+ f_pattern = 'fill="'
33
+ black = Color("black")
34
+ content = []
35
+ min_x = float("inf")
36
+ min_y = float("inf")
37
+ max_x = -float("inf")
38
+ max_y = -float("inf")
39
+ with open(source, "r") as svg_file:
40
+ tt = perf_counter()
41
+ all_lines = svg_file.readlines()
42
+ time_read = perf_counter() - tt
43
+ for line in all_lines:
44
+ idx_start = line.find(p_pattern, 0)
45
+ if idx_start < 0:
46
+ continue
47
+ idx_end = line.find('"', idx_start + len(p_pattern))
48
+ d_str = line[idx_start + len(p_pattern):idx_end]
49
+ # print (d_str)
50
+ tt = perf_counter()
51
+ geom = Geomstr.svg(d_str)
52
+ time_geom += perf_counter() - tt
53
+ if geom.index == 0:
54
+ # print (f"Strange, empty from '{d_str}' ({line})")
55
+ continue
56
+ fill_value = None
57
+ mat_start = line.find(t_pattern, idx_end)
58
+ if mat_start >= 0:
59
+ tt = perf_counter()
60
+ mat_end = line.find('"', mat_start + len(t_pattern))
61
+ mat_str = line[mat_start + len(t_pattern):mat_end]
62
+ matrix = Matrix(mat_str)
63
+ geom.transform(matrix)
64
+ time_trans += perf_counter() - tt
65
+ fill_start = line.find(f_pattern, idx_end)
66
+ if fill_start >= 0:
67
+ fill_end = line.find('"', fill_start + len(f_pattern))
68
+ fill_str = line[fill_start + len(f_pattern):fill_end]
69
+ # if fill_str == "#ffffff":
70
+ # continue
71
+
72
+ else:
73
+ fill_str = ""
74
+ content.append((geom, fill_str))
75
+ tt = perf_counter()
76
+ g_bb = geom.bbox()
77
+ min_x = min(min_x, g_bb[0])
78
+ min_y = min(min_y, g_bb[1])
79
+ max_x = max(max_x, g_bb[2])
80
+ max_y = max(max_y, g_bb[3])
81
+ time_bbox += perf_counter() - tt
82
+ t1 = perf_counter()
83
+ if content:
84
+ sx = (bbox[2] - bbox[0]) / (max_x - min_x)
85
+ sy = (bbox[3] - bbox[1]) / (max_y - min_y)
86
+ tx = bbox[0] - min_x
87
+ ty = bbox[1] - min_y
88
+ components = (sx, 0, 0, sy, tx, ty)
89
+ matrix = Matrix(components)
90
+ else:
91
+ matrix = None
92
+ for geom, fill_str in content:
93
+ if matrix:
94
+ tt = perf_counter()
95
+ geom.transform(matrix)
96
+ time_trans += perf_counter() - tt
97
+ node = PathNode(geometry = geom, stroke=black, stroke_width = 500)
98
+ if fill_str:
99
+ fill_value = Color(fill_str)
100
+ node.fill = fill_value
101
+ result_list.append(node)
102
+ t2 = perf_counter()
103
+ # print (f"Loading and geometry creation: {t1-t0:.2f}sec, node creation: {t2-t1:.2f} sec, total: {t2-t0:.2f}sec")
104
+ # print (f"Pure creation: {time_geom:.2f}sec, transform {time_trans:.2f}sec, reading {time_read:.2f}sec, bbox: {time_bbox:.2f}sec")
105
+ return result_list
106
+ """
107
+
108
+
109
+ def plugin(kernel, lifecycle=None):
110
+ if lifecycle == "invalidate":
111
+ try:
112
+ import vtracer
113
+ except ImportError:
114
+ # print("vtracer plugin could not load because vtracer is not installed.")
115
+ return True
116
+
117
+ if lifecycle == "register":
118
+ _ = kernel.translation
119
+
120
+ @kernel.console_command(
121
+ "vtracer",
122
+ help=_("return paths around image"),
123
+ input_type=("image", "elements", None),
124
+ output_type="elements",
125
+ )
126
+ def do_vtracer(
127
+ channel,
128
+ data=None,
129
+ **kwargs,
130
+ ):
131
+ try:
132
+ import os
133
+ from time import perf_counter
134
+
135
+ from vtracer import convert_image_to_svg_py
136
+
137
+ from meerk40t.core.units import Length
138
+ from meerk40t.kernel import get_safe_path
139
+ except ImportError:
140
+ channel("vtracer isn't installed, use 'pip install vtracer'")
141
+ return None
142
+ elements = kernel.root.elements
143
+ if data is None:
144
+ data = list(elements.elems(emphasized=True))
145
+ if not data:
146
+ channel(_("Nothing selected"))
147
+ return
148
+ images = [node for node in data if hasattr(node, "image")]
149
+ if not images:
150
+ channel(_("No images selected"))
151
+ return
152
+
153
+ safe_dir = os.path.realpath(get_safe_path(kernel.name))
154
+ input_file = os.path.join(safe_dir, "_vtrace_input.png")
155
+ output_file = os.path.join(safe_dir, "_vtrace_output.svg")
156
+ t_start = perf_counter()
157
+ t_convert = t_load = 0
158
+ # _("Vectorizing image")
159
+ with elements.undoscope("Vectorizing image"):
160
+ for node in images:
161
+ # kernel.root.signal("freeze_tree", True)
162
+ _start = perf_counter()
163
+ bb = node.bounds
164
+ im_wd = bb[2] - bb[0]
165
+ im_ht = bb[3] - bb[1]
166
+ im_x = bb[0]
167
+ im_y = bb[1]
168
+ flag = node.prevent_crop
169
+ if not flag:
170
+ node.prevent_crop = True
171
+ node.update(None)
172
+ bb2 = node.bounds
173
+ dx = bb2[0] - bb[0]
174
+ dy = bb2[1] - bb[1]
175
+ bb = (bb[0] - dx, bb[1] - dy, bb[2] - dx, bb[3] - dy)
176
+ node.prevent_crop = flag
177
+ node.update(None)
178
+ image = node.image
179
+
180
+ image.save(input_file)
181
+ convert_image_to_svg_py(
182
+ image_path=input_file, out_path=output_file, colormode="binary"
183
+ )
184
+ t_convert += perf_counter() - _start
185
+
186
+ _start = perf_counter()
187
+ # print (f"Vectorization took {t1-t0:.1f}sec, now loading file, executing {cmd}")
188
+ elements.suppress_updates = True
189
+ cmd = (
190
+ f'xload "{output_file}"'
191
+ + f" {Length(im_x).length_mm}"
192
+ + f" {Length(im_y).length_mm}"
193
+ f" {Length(im_wd).length_mm}" + f" {Length(im_ht).length_mm}"
194
+ )
195
+ kernel.root(f"{cmd}\n")
196
+ # elements.suppress_updates = True
197
+ # nodes = simplified_load(output_file, bb)
198
+ # elem = kernel.elements.elem_branch.add(type="group", label=f"VTrace ({node.display_label()})")
199
+ # for e in nodes:
200
+ # elem.add_node(e)
201
+ # elements.suppress_updates = False
202
+ t_load += perf_counter() - _start
203
+ try:
204
+ os.remove(input_file)
205
+ os.remove(output_file)
206
+ except (PermissionError, OSError):
207
+ pass
208
+ except Exception as e:
209
+ channel(f"Could not remove temporary files: {e}")
210
+ # kernel.root.signal("freeze_tree", False)
211
+ t_end = perf_counter()
212
+ channel(
213
+ _(
214
+ "Time needed for vectorisation: {time_total}sec (analysis: {time_convert}sec, loading: {time_load}sec)"
215
+ ).format(
216
+ time_total=round(t_end - t_start, 1),
217
+ time_convert=round(t_convert, 1),
218
+ time_load=round(t_load, 1),
219
+ )
220
+ )
221
+ kernel.root.signal("refresh_scene", "Scene")
222
+ return "elements", None
meerk40t/grbl/device.py CHANGED
@@ -7,6 +7,7 @@ Registers relevant commands and options.
7
7
 
8
8
  from time import sleep
9
9
 
10
+ from meerk40t.device.devicechoices import get_effect_choices
10
11
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
11
12
 
12
13
  from ..core.laserjob import LaserJob
@@ -16,7 +17,6 @@ from ..core.view import View
16
17
  from ..device.mixins import Status
17
18
  from .controller import GrblController
18
19
  from .driver import GRBLDriver
19
- from meerk40t.device.devicechoices import get_effect_choices
20
20
 
21
21
 
22
22
  class GRBLDevice(Service, Status):
@@ -181,6 +181,30 @@ class GRBLDevice(Service, Status):
181
181
  "tip": _("Override native home location"),
182
182
  "subsection": "_60_Home position",
183
183
  },
184
+ {
185
+ "attr": "supports_z_axis",
186
+ "object": self,
187
+ "default": False,
188
+ "type": bool,
189
+ "label": _("Supports Z-axis"),
190
+ "tip": _("Does this device have a Z-axis?"),
191
+ "subsection": "_70_Z-Axis support",
192
+ },
193
+ {
194
+ "attr": "z_home_command",
195
+ "object": self,
196
+ "default": "$HZ",
197
+ "type": str,
198
+ "style": "combosmall",
199
+ "choices": [
200
+ "$HZ",
201
+ "G28 Z",
202
+ ],
203
+ "exclusive": False,
204
+ "label": _("Z-Homing"),
205
+ "tip": _("Which command triggers the z-homing sequence"),
206
+ "subsection": "_70_Z-Axis support",
207
+ },
184
208
  {
185
209
  "attr": "signal_updates",
186
210
  "object": self,
@@ -258,6 +282,7 @@ class GRBLDevice(Service, Status):
258
282
  choice_dict["display"] = ["pyserial-not-installed"]
259
283
 
260
284
  from platform import system
285
+
261
286
  is_linux = system() == "Linux"
262
287
  choices = [
263
288
  {
@@ -271,7 +296,7 @@ class GRBLDevice(Service, Status):
271
296
  "section": "_10_Serial Interface",
272
297
  "subsection": "_00_",
273
298
  "dynamic": update,
274
- "exclusive": not is_linux,
299
+ "exclusive": not is_linux,
275
300
  },
276
301
  {
277
302
  "attr": "baud_rate",
@@ -635,6 +660,56 @@ class GRBLDevice(Service, Status):
635
660
  if self.permit_serial:
636
661
  self._register_console_serial()
637
662
 
663
+ @self.console_command(
664
+ "z_home",
665
+ help=_("Homes the z-Axis"),
666
+ input_type=None,
667
+ )
668
+ def command_zhome(command, channel, _, data=None, remainder=None, **kwgs):
669
+ if not self.supports_z_axis:
670
+ channel(_("This device does not support a z-axis."))
671
+ return
672
+ zhome = self.z_home_command
673
+ if not zhome:
674
+ channel(_("There is no homing sequence defined."))
675
+ return
676
+ channel(_("Z-Homing..."))
677
+ self.driver(zhome + self.driver.line_end)
678
+
679
+ @self.console_argument("step", type=Length, help=_("Amount to move the z-axis"))
680
+ @self.console_command(
681
+ "z_move",
682
+ help=_("Moves the z-Axis by the given amount"),
683
+ input_type=None,
684
+ )
685
+ def command_zmove_rel(command, channel, _, data=None, step=None, **kwgs):
686
+ if not self.supports_z_axis:
687
+ channel(_("This device does not support a z-axis."))
688
+ return
689
+ if step is None:
690
+ channel(_("No z-movement defined"))
691
+ return
692
+ # relative movement in mm
693
+ gcode = f"G91 G21 Z{step.mm:.3f}"
694
+ self.driver(gcode + self.driver.line_end)
695
+
696
+ @self.console_argument("step", type=Length, help=_("New z-axis position"))
697
+ @self.console_command(
698
+ "z_move_to",
699
+ help=_("Moves the z-Axis to the given position"),
700
+ input_type=None,
701
+ )
702
+ def command_zmove_abs(command, channel, _, data=None, step=None, **kwgs):
703
+ if not self.supports_z_axis:
704
+ channel(_("This device does not support a z-axis."))
705
+ return
706
+ if step is None:
707
+ channel(_("No z-movement defined"))
708
+ return
709
+ # absolute movement in mm
710
+ gcode = f"G91 G20 Z{step.mm:.3f}"
711
+ self.driver(gcode + self.driver.line_end)
712
+
638
713
  @self.console_command(
639
714
  ("gcode", "grbl"),
640
715
  help=_("Send raw gcode to the device"),
@@ -901,9 +976,7 @@ class GRBLDevice(Service, Status):
901
976
  channel(_("Interpreter cannot be attached to any device."))
902
977
  return
903
978
 
904
- @self.console_argument(
905
- "index", type=int, help=_("macro to run (1-5).")
906
- )
979
+ @self.console_argument("index", type=int, help=_("macro to run (1-5)."))
907
980
  @self.console_command(
908
981
  "macro",
909
982
  help=_("Send a predefined macro to the device."),
@@ -916,11 +989,11 @@ class GRBLDevice(Service, Status):
916
989
  macrotext = self.setting(str, f"macro_{idx}", "")
917
990
  channel(f"Content of macro {idx + 1}:")
918
991
  for no, line in enumerate(macrotext.splitlines()):
919
- channel (f"{no:2d}: {line}")
992
+ channel(f"{no:2d}: {line}")
920
993
  return
921
994
  err = True
922
995
  try:
923
- macro_index = int(index) -1
996
+ macro_index = int(index) - 1
924
997
  if 0 <= macro_index <= 4:
925
998
  err = False
926
999
  except ValueError:
@@ -1081,4 +1154,4 @@ class GRBLDevice(Service, Status):
1081
1154
  def get_raster_instructions(self):
1082
1155
  return {
1083
1156
  "gantry": True,
1084
- }
1157
+ }
@@ -12,7 +12,7 @@ from meerk40t.kernel import Module
12
12
  class GRBLInterpreter(Module):
13
13
  def __init__(self, service, path):
14
14
  Module.__init__(self, service, path)
15
- self.emulator = GRBLEmulator(self, service.space.display.matrix())
15
+ self.emulator = GRBLEmulator(self, service.space.display.matrix)
16
16
  self._attached_device = None
17
17
 
18
18
  def __repr__(self):
meerk40t/gui/about.py CHANGED
@@ -1770,6 +1770,8 @@ class ComponentPanel(ScrolledPanel):
1770
1770
 
1771
1771
  def get_potrace():
1772
1772
  entry = ["potracer", "", "", "https://pypi.org/project/potracer/"]
1773
+ status = _("Present (slow)")
1774
+ info = "0.05 (internal)"
1773
1775
  try:
1774
1776
  import potrace
1775
1777
 
@@ -1780,11 +1782,26 @@ class ComponentPanel(ScrolledPanel):
1780
1782
  entry[0] = "pypotrace"
1781
1783
  entry[3] = "https://pypi.org/project/pypotrace/"
1782
1784
  info = potrace.potracelib_version()
1783
- else:
1784
- status = _("Present (slow)")
1785
- info = "??"
1786
1785
  if not hasattr(potrace, "Bitmap"):
1787
1786
  status = _("Faulty, please report")
1787
+ except ImportError:
1788
+ pass
1789
+ entry[1] = info
1790
+ entry[2] = status
1791
+ self.content.append(entry)
1792
+
1793
+ def get_vtrace():
1794
+ entry = ["vtracer", "", "", "https://pypi.org/project/vtracer/"]
1795
+ try:
1796
+ import vtracer
1797
+
1798
+ # for e in vars(vtracer):
1799
+ # print (f"var {e} - {getattr(vtracer, e)}")
1800
+ try:
1801
+ info = vtracer.__version__
1802
+ except AttributeError:
1803
+ info = "??"
1804
+ status = _("Present")
1788
1805
  except ImportError:
1789
1806
  info = "??"
1790
1807
  status = _("Missing")
@@ -1961,6 +1978,7 @@ class ComponentPanel(ScrolledPanel):
1961
1978
  get_numpy()
1962
1979
  get_pillow()
1963
1980
  get_potrace()
1981
+ get_vtrace()
1964
1982
  get_ezdxf()
1965
1983
  get_pyusb()
1966
1984
  get_pyserial()
meerk40t/gui/basicops.py CHANGED
@@ -459,21 +459,21 @@ class BasicOpPanel(wx.Panel):
459
459
  self.context.themes.set_window_colors(header)
460
460
  header.SetMinSize(dip_size(self, 20, -1))
461
461
  header.SetMaxSize(dip_size(self, 20, -1))
462
- header.SetToolTip(_("Active"))
462
+ header.SetToolTip(_("A: Active = toggle whether the elements assigned to this operation will be burned or not"))
463
463
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
464
464
 
465
465
  header = wxStaticText(self.op_panel, wx.ID_ANY, label="S")
466
466
  self.context.themes.set_window_colors(header)
467
467
  header.SetMinSize(dip_size(self, 20, -1))
468
468
  header.SetMaxSize(dip_size(self, 20, -1))
469
- header.SetToolTip(_("Show"))
469
+ header.SetToolTip(_("S: Show = if inactive then you can suppress the drawing of the assigned elements"))
470
470
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
471
471
 
472
472
  header = wxStaticText(self.op_panel, wx.ID_ANY, label="C")
473
473
  self.context.themes.set_window_colors(header)
474
474
  header.SetMinSize(dip_size(self, 20, -1))
475
475
  header.SetMaxSize(dip_size(self, 20, -1))
476
- header.SetToolTip(_("Coolant"))
476
+ header.SetToolTip(_("C: Coolant = determines whether coolant remains / will be turned on / turned off at start of this operation"))
477
477
  info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
478
478
 
479
479
  unit = " [%]" if self.use_percent else ""
@@ -1117,10 +1117,7 @@ class ChoicePropertyPanel(ScrolledPanel):
1117
1117
  control.SetValue(bool((data >> b) & 1))
1118
1118
  if mask:
1119
1119
  control.Enable(bool((mask_bits >> b) & 1))
1120
- control.Bind(
1121
- wx.EVT_CHECKBOX,
1122
- on_checkbox_check(attr, control, obj, b, additional_signal),
1123
- )
1120
+ control.Bind(wx.EVT_CHECKBOX, on_checkbox_bitcheck(attr, control, obj, b, additional_signal), )
1124
1121
 
1125
1122
  # mask bit
1126
1123
  if mask:
@@ -5,12 +5,12 @@ from meerk40t.gui.icons import icons8_manager
5
5
  from meerk40t.gui.mwindow import MWindow
6
6
  from meerk40t.gui.wxutils import (
7
7
  StaticBoxSizer,
8
+ TextCtrl,
8
9
  dip_size,
9
10
  wxButton,
10
11
  wxListCtrl,
11
12
  wxStaticText,
12
13
  wxTreeCtrl,
13
- TextCtrl,
14
14
  )
15
15
  from meerk40t.kernel import lookup_listener, signal_listener
16
16
 
@@ -71,15 +71,15 @@ class SelectDevice(wx.Dialog):
71
71
  )
72
72
  # Used for proper sorting in the device add menu.
73
73
  self.sort_family_name = {
74
- _("K-Series CO2-Laser"): 99,
75
- _("Ortur Diode-Laser"): 98,
76
- _("Longer Diode-Laser"): 97,
77
- _("Newly CO2-Laser"): 96,
78
- _("Generic UV-Laser"): 95,
79
- _("Generic CO2-Laser"): 94,
80
- _("Generic Fibre-Laser"): 93,
81
- _("Generic Diode-Laser"): 92,
82
- _("Generic"): 91,
74
+ _("K-Series CO2-Laser"): 99,
75
+ _("Ortur Diode-Laser"): 98,
76
+ _("Longer Diode-Laser"): 97,
77
+ _("Newly CO2-Laser"): 96,
78
+ _("Generic UV-Laser"): 95,
79
+ _("Generic CO2-Laser"): 94,
80
+ _("Generic Fibre-Laser"): 93,
81
+ _("Generic Diode-Laser"): 92,
82
+ _("Generic"): 91,
83
83
  }
84
84
  sizer_main.Add(self.tree_devices, 3, wx.EXPAND, 0)
85
85
  self.no_msg = (
@@ -125,7 +125,6 @@ class SelectDevice(wx.Dialog):
125
125
  self.Layout()
126
126
  self.populate_tree()
127
127
 
128
-
129
128
  def populate_tree(self):
130
129
  tree = self.tree_devices
131
130
  tree.DeleteAllItems()
@@ -223,18 +222,20 @@ class DevicePanel(wx.Panel):
223
222
  | wx.LC_SINGLE_SEL
224
223
  | wx.LC_SORT_ASCENDING,
225
224
  context=self.context,
226
- list_name="list_devices"
225
+ list_name="list_devices",
227
226
  )
228
227
  self.list_columns = {
229
228
  "device": 0,
230
229
  "driver": 1,
231
230
  "family": 2,
232
231
  "status": 3,
232
+ "location": 4,
233
233
  }
234
234
  self.devices_list.InsertColumn(self.list_columns["device"], _("Device"))
235
235
  self.devices_list.InsertColumn(self.list_columns["driver"], _("Driver"))
236
236
  self.devices_list.InsertColumn(self.list_columns["family"], _("Type"))
237
237
  self.devices_list.InsertColumn(self.list_columns["status"], _("Status"))
238
+ self.devices_list.InsertColumn(self.list_columns["location"], _("Interface"))
238
239
  self.devices_list.resize_columns()
239
240
  sizer_1.Add(self.devices_list, 7, wx.EXPAND, 0)
240
241
 
@@ -287,9 +288,7 @@ class DevicePanel(wx.Panel):
287
288
  self.Bind(
288
289
  wx.EVT_BUTTON, self.on_button_create_device, self.button_create_device
289
290
  )
290
- self.Bind(
291
- wx.EVT_BUTTON, self.on_button_copy_device, self.button_copy_device
292
- )
291
+ self.Bind(wx.EVT_BUTTON, self.on_button_copy_device, self.button_copy_device)
293
292
  self.Bind(
294
293
  wx.EVT_BUTTON, self.on_button_remove_device, self.button_remove_device
295
294
  )
@@ -317,7 +316,6 @@ class DevicePanel(wx.Panel):
317
316
  def pane_hide(self, *args):
318
317
  pass
319
318
 
320
-
321
319
  def on_start_edit(self, event):
322
320
  event.Allow()
323
321
 
@@ -410,11 +408,17 @@ class DevicePanel(wx.Panel):
410
408
  except AttributeError:
411
409
  pass
412
410
 
411
+ try:
412
+ loc_info = device.location()
413
+ except AttributeError:
414
+ loc_info = "undefined"
415
+
413
416
  self.devices_list.SetItem(index, self.list_columns["driver"], type_info)
414
417
  self.devices_list.SetItem(index, self.list_columns["family"], family_info)
415
418
  self.devices_list.SetItem(
416
419
  index, self.list_columns["status"], _(active_status)
417
420
  )
421
+ self.devices_list.SetItem(index, self.list_columns["location"], loc_info)
418
422
  self.devices_list.SetItemData(index, dev_index)
419
423
  if self.context.device is device:
420
424
  self.devices_list.SetItemTextColour(index, wx.RED)
@@ -63,6 +63,8 @@ class FormatPainter:
63
63
  ("linecap", True),
64
64
  ("linejoin", True),
65
65
  ("fillrule", True),
66
+ ("stroke_dash", True),
67
+ ("mktablength", False),
66
68
  # Image attributes
67
69
  ("dpi", False),
68
70
  ("operations", False),
@@ -193,6 +195,8 @@ class FormatPainter:
193
195
  flag_changed = True
194
196
 
195
197
  if flag_changed:
198
+ if hasattr(node, "empty_cache"):
199
+ node.empty_cache()
196
200
  nodes_changed.append(node)
197
201
  if node.type == "elem image":
198
202
  nodes_images.append(node)
@@ -584,7 +588,10 @@ class Warnings:
584
588
  image_node = node.node if hasattr(node, "node") else node
585
589
  if getattr(image_node, "hidden", False):
586
590
  continue
587
- opdpi = op.dpi if useop else image_node.dpi
591
+ if hasattr(image_node, "dpi"):
592
+ opdpi = op.dpi if useop else image_node.dpi
593
+ else:
594
+ opdpi = op.dpi
588
595
  step_x, step_y = self.context.device.view.dpi_to_steps(opdpi)
589
596
  step_x *= self.context.device.view.native_scale_x
590
597
  step_y *= self.context.device.view.native_scale_y