meerk40t 0.9.7030__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.
- meerk40t/balormk/clone_loader.py +3 -2
- meerk40t/balormk/controller.py +28 -11
- meerk40t/balormk/cylindermod.py +1 -0
- meerk40t/balormk/device.py +13 -9
- meerk40t/balormk/driver.py +9 -2
- meerk40t/balormk/galvo_commands.py +3 -1
- meerk40t/balormk/gui/gui.py +6 -0
- meerk40t/balormk/livelightjob.py +338 -321
- meerk40t/balormk/mock_connection.py +4 -3
- meerk40t/balormk/usb_connection.py +11 -2
- meerk40t/camera/camera.py +19 -14
- meerk40t/camera/gui/camerapanel.py +6 -0
- meerk40t/core/cutplan.py +109 -51
- meerk40t/core/elements/element_treeops.py +435 -140
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/shapes.py +259 -39
- meerk40t/core/elements/tree_commands.py +10 -5
- meerk40t/core/node/elem_ellipse.py +18 -8
- meerk40t/core/node/elem_image.py +51 -19
- meerk40t/core/node/elem_line.py +18 -8
- meerk40t/core/node/elem_path.py +18 -8
- meerk40t/core/node/elem_point.py +10 -4
- meerk40t/core/node/elem_polyline.py +19 -11
- meerk40t/core/node/elem_rect.py +18 -8
- meerk40t/core/node/elem_text.py +11 -5
- meerk40t/core/node/filenode.py +2 -8
- meerk40t/core/node/groupnode.py +11 -11
- meerk40t/core/node/image_processed.py +11 -5
- meerk40t/core/node/image_raster.py +11 -5
- meerk40t/core/node/node.py +64 -16
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/svg_io.py +91 -34
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +81 -8
- meerk40t/gui/about.py +20 -0
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +4 -0
- meerk40t/gui/icons.py +330 -253
- meerk40t/gui/laserpanel.py +8 -3
- meerk40t/gui/laserrender.py +41 -21
- meerk40t/gui/magnetoptions.py +158 -65
- meerk40t/gui/materialtest.py +229 -39
- meerk40t/gui/navigationpanels.py +229 -24
- meerk40t/gui/propertypanels/hatchproperty.py +2 -0
- meerk40t/gui/propertypanels/imageproperty.py +160 -106
- meerk40t/gui/ribbon.py +6 -1
- meerk40t/gui/scenewidgets/gridwidget.py +29 -32
- meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
- meerk40t/gui/simulation.py +75 -77
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +107 -24
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +60 -15
- meerk40t/image/imagetools.py +129 -65
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +39 -18
- meerk40t/kernel/settings.py +28 -9
- meerk40t/lihuiyu/device.py +24 -12
- meerk40t/main.py +1 -1
- meerk40t/moshi/device.py +20 -6
- meerk40t/network/console_server.py +22 -6
- meerk40t/newly/device.py +10 -3
- meerk40t/newly/gui/gui.py +10 -0
- meerk40t/ruida/device.py +22 -2
- meerk40t/ruida/loader.py +6 -3
- meerk40t/tools/geomstr.py +193 -125
- meerk40t/tools/rasterplotter.py +179 -93
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +79 -78
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.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",
|
@@ -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
|
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
|
+
}
|
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()
|
meerk40t/gui/devicepanel.py
CHANGED
@@ -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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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)
|
meerk40t/gui/gui_mixins.py
CHANGED
@@ -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)
|