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
@@ -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",
@@ -470,6 +495,19 @@ class GRBLDevice(Service, Status):
470
495
  "section": "_20_" + _("Maximum speeds"),
471
496
  "subsection": "_10_",
472
497
  },
498
+ {
499
+ "attr": "rapid_speed",
500
+ "object": self,
501
+ "default": 600,
502
+ "type": float,
503
+ "label": _("Travel speed"),
504
+ "trailer": "mm/s",
505
+ "tip": _(
506
+ "What is the travel speed for your device to move from point to another."
507
+ ),
508
+ "section": "_25_" + _("Travel"),
509
+ "subsection": "_10_",
510
+ },
473
511
  {
474
512
  "attr": "limit_buffer",
475
513
  "object": self,
@@ -635,6 +673,56 @@ class GRBLDevice(Service, Status):
635
673
  if self.permit_serial:
636
674
  self._register_console_serial()
637
675
 
676
+ @self.console_command(
677
+ "z_home",
678
+ help=_("Homes the z-Axis"),
679
+ input_type=None,
680
+ )
681
+ def command_zhome(command, channel, _, data=None, remainder=None, **kwgs):
682
+ if not self.supports_z_axis:
683
+ channel(_("This device does not support a z-axis."))
684
+ return
685
+ zhome = self.z_home_command
686
+ if not zhome:
687
+ channel(_("There is no homing sequence defined."))
688
+ return
689
+ channel(_("Z-Homing..."))
690
+ self.driver(zhome + self.driver.line_end)
691
+
692
+ @self.console_argument("step", type=Length, help=_("Amount to move the z-axis"))
693
+ @self.console_command(
694
+ "z_move",
695
+ help=_("Moves the z-Axis by the given amount"),
696
+ input_type=None,
697
+ )
698
+ def command_zmove_rel(command, channel, _, data=None, step=None, **kwgs):
699
+ if not self.supports_z_axis:
700
+ channel(_("This device does not support a z-axis."))
701
+ return
702
+ if step is None:
703
+ channel(_("No z-movement defined"))
704
+ return
705
+ # relative movement in mm
706
+ gcode = f"G91 G21 Z{step.mm:.3f}"
707
+ self.driver(gcode + self.driver.line_end)
708
+
709
+ @self.console_argument("step", type=Length, help=_("New z-axis position"))
710
+ @self.console_command(
711
+ "z_move_to",
712
+ help=_("Moves the z-Axis to the given position"),
713
+ input_type=None,
714
+ )
715
+ def command_zmove_abs(command, channel, _, data=None, step=None, **kwgs):
716
+ if not self.supports_z_axis:
717
+ channel(_("This device does not support a z-axis."))
718
+ return
719
+ if step is None:
720
+ channel(_("No z-movement defined"))
721
+ return
722
+ # absolute movement in mm
723
+ gcode = f"G91 G20 Z{step.mm:.3f}"
724
+ self.driver(gcode + self.driver.line_end)
725
+
638
726
  @self.console_command(
639
727
  ("gcode", "grbl"),
640
728
  help=_("Send raw gcode to the device"),
@@ -825,7 +913,8 @@ class GRBLDevice(Service, Status):
825
913
  # self.redlight_preferred = True
826
914
  # self.driver.set("power", int(self.red_dot_level / 100 * 1000))
827
915
  self.driver._clean()
828
- self.driver.laser_on(power=int(self.red_dot_level), speed=1000)
916
+ rapid_speed = self.setting(float, "rapid_speed", 600.0)
917
+ self.driver.laser_on(power=int(self.red_dot_level), speed=rapid_speed)
829
918
  # By default, any move is a G0 move which will not activate the laser,
830
919
  # so we need to switch to G1 mode:
831
920
  self.driver.move_mode = 1
@@ -901,9 +990,7 @@ class GRBLDevice(Service, Status):
901
990
  channel(_("Interpreter cannot be attached to any device."))
902
991
  return
903
992
 
904
- @self.console_argument(
905
- "index", type=int, help=_("macro to run (1-5).")
906
- )
993
+ @self.console_argument("index", type=int, help=_("macro to run (1-5)."))
907
994
  @self.console_command(
908
995
  "macro",
909
996
  help=_("Send a predefined macro to the device."),
@@ -916,11 +1003,11 @@ class GRBLDevice(Service, Status):
916
1003
  macrotext = self.setting(str, f"macro_{idx}", "")
917
1004
  channel(f"Content of macro {idx + 1}:")
918
1005
  for no, line in enumerate(macrotext.splitlines()):
919
- channel (f"{no:2d}: {line}")
1006
+ channel(f"{no:2d}: {line}")
920
1007
  return
921
1008
  err = True
922
1009
  try:
923
- macro_index = int(index) -1
1010
+ macro_index = int(index) - 1
924
1011
  if 0 <= macro_index <= 4:
925
1012
  err = False
926
1013
  except ValueError:
@@ -1081,4 +1168,4 @@ class GRBLDevice(Service, Status):
1081
1168
  def get_raster_instructions(self):
1082
1169
  return {
1083
1170
  "gantry": True,
1084
- }
1171
+ }
meerk40t/grbl/driver.py CHANGED
@@ -47,7 +47,12 @@ class GRBLDriver(Parameters):
47
47
  self.stepper_step_size = UNITS_PER_MIL
48
48
 
49
49
  self.plot_planner = PlotPlanner(
50
- self.settings, single=True, ppi=False, shift=False, group=True, require_uniform_movement = False,
50
+ self.settings,
51
+ single=True,
52
+ ppi=False,
53
+ shift=False,
54
+ group=True,
55
+ require_uniform_movement=False,
51
56
  )
52
57
  self.queue = []
53
58
  self._queue_current = 0
@@ -407,10 +412,10 @@ class GRBLDriver(Parameters):
407
412
  for q in self.queue:
408
413
  # Are there any custom commands to be executed?
409
414
  # Usecase (as described in issue https://github.com/meerk40t/meerk40t/issues/2764 ):
410
- # Switch between M3 and M4 mode for cut / raster
415
+ # Switch between M3 and M4 mode for cut / raster
411
416
  # M3=used to cut as gantry acceleration doesn't matter on a cut.
412
- # M4=used for Raster/Engrave operations, as grblHAL will
413
- # adjust power based on gantry speed including acceleration.
417
+ # M4=used for Raster/Engrave operations, as grblHAL will
418
+ # adjust power based on gantry speed including acceleration.
414
419
 
415
420
  cmd_string = q.settings.get("custom_commands", "")
416
421
  if cmd_string:
@@ -437,7 +442,7 @@ class GRBLDriver(Parameters):
437
442
  self.on_value = 1.0
438
443
  # Do we have a custom z-Value?
439
444
  # NB: zaxis is not a property inside Parameters like power/or speed
440
- # so we need to deal with it more directly
445
+ # so we need to deal with it more directly
441
446
  # (e.g. self.power is the equivalent to self.settings.["power"]))
442
447
  qzaxis = q.settings.get("zaxis", self.zaxis)
443
448
  if qzaxis != self.zaxis:
@@ -534,6 +539,7 @@ class GRBLDriver(Parameters):
534
539
  PLOT_RAPID | PLOT_JOG
535
540
  ): # Plot planner requests position change.
536
541
  # self.move_mode = 0
542
+ self.rapid_mode()
537
543
  self._move(x, y)
538
544
  continue
539
545
  # if on == 0:
@@ -609,6 +615,10 @@ class GRBLDriver(Parameters):
609
615
  @param values:
610
616
  @return:
611
617
  """
618
+ speedvalue = self.service.setting(float, "rapid_speed", 600.0)
619
+ if self.speed != speedvalue:
620
+ self.speed = speedvalue
621
+ self.speed_dirty = True
612
622
 
613
623
  def finished_mode(self, *values):
614
624
  """
meerk40t/gui/about.py CHANGED
@@ -1790,6 +1790,25 @@ class ComponentPanel(ScrolledPanel):
1790
1790
  entry[2] = status
1791
1791
  self.content.append(entry)
1792
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")
1805
+ except ImportError:
1806
+ info = "??"
1807
+ status = _("Missing")
1808
+ entry[1] = info
1809
+ entry[2] = status
1810
+ self.content.append(entry)
1811
+
1793
1812
  def get_ezdxf():
1794
1813
  entry = ["ezdxf", "", "", "https://ezdxf.readthedocs.io/en/stable/"]
1795
1814
  try:
@@ -1959,6 +1978,7 @@ class ComponentPanel(ScrolledPanel):
1959
1978
  get_numpy()
1960
1979
  get_pillow()
1961
1980
  get_potrace()
1981
+ get_vtrace()
1962
1982
  get_ezdxf()
1963
1983
  get_pyusb()
1964
1984
  get_pyserial()
@@ -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)