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.
- 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/cutcode/cutcode.py +1 -1
- meerk40t/core/cutplan.py +169 -43
- meerk40t/core/elements/element_treeops.py +444 -147
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/grid.py +8 -1
- meerk40t/core/elements/offset_mk.py +2 -1
- meerk40t/core/elements/shapes.py +618 -279
- 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 +70 -19
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/planner.py +23 -0
- meerk40t/core/svg_io.py +91 -34
- meerk40t/core/undos.py +1 -1
- meerk40t/core/wordlist.py +1 -0
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/dxf/dxf_io.py +6 -0
- meerk40t/extra/mk_potrace.py +1959 -0
- meerk40t/extra/param_functions.py +1 -1
- meerk40t/extra/potrace.py +14 -10
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +81 -8
- meerk40t/grbl/interpreter.py +1 -1
- meerk40t/gui/about.py +21 -3
- meerk40t/gui/basicops.py +3 -3
- meerk40t/gui/choicepropertypanel.py +1 -4
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +8 -1
- 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/spoolerpanel.py +6 -9
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/themes.py +7 -1
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmeerk40t.py +26 -0
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +180 -4
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +60 -15
- meerk40t/image/imagetools.py +130 -66
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +49 -22
- meerk40t/kernel/settings.py +29 -8
- meerk40t/lihuiyu/device.py +30 -12
- meerk40t/main.py +22 -5
- 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/gui/gui.py +6 -6
- meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
- meerk40t/ruida/loader.py +6 -3
- meerk40t/ruida/rdjob.py +3 -3
- meerk40t/tools/geomstr.py +195 -39
- meerk40t/tools/rasterplotter.py +179 -93
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +98 -96
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7020.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
- {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=_("
|
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
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
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/grbl/interpreter.py
CHANGED
@@ -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:
|
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)
|
@@ -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
|
-
|
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
|