meerk40t 0.9.7010__py2.py3-none-any.whl → 0.9.7030__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/balormk/galvo_commands.py +1 -2
- meerk40t/core/cutcode/cutcode.py +1 -1
- meerk40t/core/cutplan.py +70 -2
- meerk40t/core/elements/branches.py +18 -4
- meerk40t/core/elements/element_treeops.py +43 -7
- meerk40t/core/elements/elements.py +49 -63
- meerk40t/core/elements/grid.py +8 -1
- meerk40t/core/elements/offset_clpr.py +4 -3
- meerk40t/core/elements/offset_mk.py +2 -1
- meerk40t/core/elements/shapes.py +379 -260
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/node/node.py +6 -3
- meerk40t/core/node/op_cut.py +9 -8
- meerk40t/core/node/op_dots.py +8 -8
- meerk40t/core/node/op_engrave.py +7 -7
- meerk40t/core/node/op_raster.py +8 -8
- meerk40t/core/planner.py +23 -0
- meerk40t/core/undos.py +1 -1
- meerk40t/core/wordlist.py +1 -0
- meerk40t/dxf/dxf_io.py +6 -0
- meerk40t/extra/encode_detect.py +8 -2
- meerk40t/extra/hershey.py +2 -3
- meerk40t/extra/inkscape.py +3 -5
- meerk40t/extra/mk_potrace.py +1959 -0
- meerk40t/extra/outerworld.py +2 -3
- meerk40t/extra/param_functions.py +2 -2
- meerk40t/extra/potrace.py +14 -10
- meerk40t/grbl/device.py +4 -1
- meerk40t/grbl/gui/grblcontroller.py +2 -2
- meerk40t/grbl/interpreter.py +1 -1
- meerk40t/gui/about.py +3 -5
- meerk40t/gui/basicops.py +3 -3
- meerk40t/gui/busy.py +75 -13
- meerk40t/gui/choicepropertypanel.py +365 -379
- meerk40t/gui/consolepanel.py +3 -3
- meerk40t/gui/gui_mixins.py +4 -1
- meerk40t/gui/hersheymanager.py +13 -3
- meerk40t/gui/laserpanel.py +12 -7
- meerk40t/gui/materialmanager.py +33 -6
- meerk40t/gui/plugin.py +9 -3
- meerk40t/gui/propertypanels/operationpropertymain.py +1 -1
- meerk40t/gui/ribbon.py +4 -1
- meerk40t/gui/scene/widget.py +1 -1
- meerk40t/gui/scenewidgets/rectselectwidget.py +19 -16
- meerk40t/gui/scenewidgets/selectionwidget.py +26 -20
- meerk40t/gui/simpleui.py +13 -8
- meerk40t/gui/simulation.py +22 -2
- meerk40t/gui/spoolerpanel.py +8 -11
- meerk40t/gui/themes.py +7 -1
- meerk40t/gui/tips.py +2 -3
- meerk40t/gui/toolwidgets/toolmeasure.py +4 -1
- meerk40t/gui/wxmeerk40t.py +32 -3
- meerk40t/gui/wxmmain.py +72 -6
- meerk40t/gui/wxmscene.py +95 -6
- meerk40t/gui/wxmtree.py +17 -11
- meerk40t/gui/wxutils.py +1 -1
- meerk40t/image/imagetools.py +21 -6
- meerk40t/kernel/kernel.py +31 -6
- meerk40t/kernel/settings.py +2 -0
- meerk40t/lihuiyu/device.py +9 -3
- meerk40t/main.py +22 -5
- meerk40t/network/console_server.py +52 -14
- meerk40t/network/web_server.py +15 -1
- meerk40t/ruida/device.py +5 -1
- meerk40t/ruida/gui/gui.py +6 -6
- meerk40t/ruida/gui/ruidaoperationproperties.py +1 -10
- meerk40t/ruida/rdjob.py +3 -3
- meerk40t/tools/geomstr.py +88 -0
- meerk40t/tools/polybool.py +2 -1
- meerk40t/tools/shxparser.py +92 -34
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/RECORD +77 -75
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7030.dist-info}/zip-safe +0 -0
@@ -3,7 +3,7 @@ from math import atan2, cos, sin, sqrt, tau
|
|
3
3
|
import wx
|
4
4
|
|
5
5
|
from meerk40t.core.units import Length
|
6
|
-
from meerk40t.gui.wxutils import get_matrix_scale, get_gc_scale
|
6
|
+
from meerk40t.gui.wxutils import get_matrix_scale, get_gc_scale, dip_size
|
7
7
|
|
8
8
|
from .toolpointlistbuilder import PointListTool
|
9
9
|
|
@@ -24,6 +24,8 @@ class MeasureTool(PointListTool):
|
|
24
24
|
self.line_pen.SetColour(self.scene.colors.color_measure_line)
|
25
25
|
self.line_pen.SetStyle(wx.PENSTYLE_DOT)
|
26
26
|
self.line_pen.SetWidth(1000)
|
27
|
+
fact = dip_size(self.scene.pane, 100, 100)
|
28
|
+
self.font_size_factor = (fact[0] + fact[1]) / 100 * 0.5
|
27
29
|
|
28
30
|
def create_node(self):
|
29
31
|
# No need to create anything
|
@@ -43,6 +45,7 @@ class MeasureTool(PointListTool):
|
|
43
45
|
font_size = 5000
|
44
46
|
if font_size > 1e8:
|
45
47
|
font_size = 5000
|
48
|
+
font_size *= self.font_size_factor
|
46
49
|
# print ("Fontsize=%.3f, " % self.font_size)
|
47
50
|
if font_size < 1.0:
|
48
51
|
font_size = 1.0 # Mac does not allow values lower than 1.
|
meerk40t/gui/wxmeerk40t.py
CHANGED
@@ -24,6 +24,7 @@ from meerk40t.gui.wxmscene import SceneWindow
|
|
24
24
|
from meerk40t.gui.wxutils import TextCtrl, wxButton, wxStaticText
|
25
25
|
from meerk40t.kernel import CommandSyntaxError, Module, get_safe_path
|
26
26
|
from meerk40t.kernel.kernel import Job
|
27
|
+
from meerk40t.core.units import Length
|
27
28
|
|
28
29
|
from ..main import APPLICATION_NAME, APPLICATION_VERSION
|
29
30
|
from ..tools.kerftest import KerfTool
|
@@ -500,7 +501,8 @@ class wxMeerK40t(wx.App, Module):
|
|
500
501
|
|
501
502
|
def OnInit(self):
|
502
503
|
self.name = f"MeerK40t-{wx.GetUserId()}"
|
503
|
-
|
504
|
+
mkdir = self.context.kernel.os_information["OS_TEMPDIR"]
|
505
|
+
self.instance = wx.SingleInstanceChecker(self.name, path=mkdir)
|
504
506
|
self.context.setting(bool, "single_instance_only", True)
|
505
507
|
if self.context.kernel._was_restarted:
|
506
508
|
return True
|
@@ -560,15 +562,17 @@ class wxMeerK40t(wx.App, Module):
|
|
560
562
|
def MacOpenFile(self, filename):
|
561
563
|
try:
|
562
564
|
if self.context is not None:
|
563
|
-
self.context.
|
565
|
+
channel = self.context.kernel.channel("console")
|
566
|
+
self.context.elements.load(os.path.realpath(filename), svg_ppi=self.context.elements.svg_ppi, channel=channel)
|
564
567
|
except AttributeError:
|
565
568
|
pass
|
566
569
|
|
567
570
|
def MacOpenFiles(self, filenames):
|
568
571
|
try:
|
569
572
|
if self.context is not None:
|
573
|
+
channel = self.context.kernel.channel("console")
|
570
574
|
for filename in filenames:
|
571
|
-
self.context.elements.load(os.path.realpath(filename))
|
575
|
+
self.context.elements.load(os.path.realpath(filename), svg_ppi=self.context.elements.svg_ppi, channel=channel)
|
572
576
|
except AttributeError:
|
573
577
|
pass
|
574
578
|
|
@@ -832,6 +836,31 @@ class wxMeerK40t(wx.App, Module):
|
|
832
836
|
dialog.cancel_it()
|
833
837
|
dialog.Destroy()
|
834
838
|
|
839
|
+
@kernel.console_argument("info", type=str, help=_("Unit to translate"))
|
840
|
+
@kernel.console_command("unit", help=_("Translate units"))
|
841
|
+
def show_unit_info(command, channel, _, info=None, **kwargs):
|
842
|
+
if info is None:
|
843
|
+
channel(_("You need to provide a value to translate"))
|
844
|
+
device = kernel.root.device
|
845
|
+
try:
|
846
|
+
valuex = Length(info, relative_length=device.view.width, digits=4)
|
847
|
+
except ValueError:
|
848
|
+
channel(f"Invalid value: '{info}'")
|
849
|
+
return
|
850
|
+
channel(f"{info} translates to:")
|
851
|
+
channel(f"tat : {float(valuex):.4f}")
|
852
|
+
channel(f"mil : {valuex.mil}")
|
853
|
+
channel(f"um : {valuex.um}")
|
854
|
+
channel(f"nm : {valuex.nm}")
|
855
|
+
channel(f"mm : {valuex.mm}")
|
856
|
+
channel(f"cm : {valuex.cm}")
|
857
|
+
channel(f"Pixels : {valuex.pixels}")
|
858
|
+
channel(f"Point : {valuex.pt}")
|
859
|
+
channel(f"spx (screen) : {valuex.spx}")
|
860
|
+
channel(f"inch : {valuex.inches}")
|
861
|
+
channel(f"Device units : {float(valuex) / device.view.native_scale_x:.4f}")
|
862
|
+
|
863
|
+
|
835
864
|
def module_open(self, *args, **kwargs):
|
836
865
|
context = self.context
|
837
866
|
kernel = context.kernel
|
meerk40t/gui/wxmmain.py
CHANGED
@@ -2,6 +2,7 @@ import datetime
|
|
2
2
|
import os
|
3
3
|
import platform
|
4
4
|
import sys
|
5
|
+
import threading
|
5
6
|
from functools import partial
|
6
7
|
from math import isinf
|
7
8
|
|
@@ -126,6 +127,42 @@ from .mwindow import MWindow
|
|
126
127
|
_ = wx.GetTranslation
|
127
128
|
MULTIPLE = "<Multiple files loaded>"
|
128
129
|
|
130
|
+
class GUIThread:
|
131
|
+
"""
|
132
|
+
This will take from any thread a command to be executed and inserts it into the main thread
|
133
|
+
This prevents threading & lock issues exhibited by passing along commands
|
134
|
+
via ``consoleserver`` or ``webserver``
|
135
|
+
"""
|
136
|
+
def __init__(self, context, *args, **kwargs):
|
137
|
+
self.context = context
|
138
|
+
self._execution_lock = threading.Lock()
|
139
|
+
self._execution_buffer = []
|
140
|
+
self._execution_timer = Job(
|
141
|
+
process=self.execute_command,
|
142
|
+
job_name="console-execute",
|
143
|
+
interval=0.1,
|
144
|
+
run_main=True,
|
145
|
+
)
|
146
|
+
self.context.kernel.register("gui/handover", self.process_command)
|
147
|
+
|
148
|
+
def execute_command(self):
|
149
|
+
cmd = ""
|
150
|
+
another = False
|
151
|
+
with self._execution_lock:
|
152
|
+
if self._execution_buffer:
|
153
|
+
cmd = self._execution_buffer[0]
|
154
|
+
self._execution_buffer.pop(0)
|
155
|
+
another = len(self._execution_buffer) > 0
|
156
|
+
if cmd:
|
157
|
+
self.context(cmd + "\n")
|
158
|
+
if another:
|
159
|
+
self.context.kernel.schedule(self._execution_timer)
|
160
|
+
|
161
|
+
def process_command(self, command):
|
162
|
+
with self._execution_lock:
|
163
|
+
self._execution_buffer.append(command)
|
164
|
+
self.context.kernel.schedule(self._execution_timer)
|
165
|
+
|
129
166
|
class Autosaver:
|
130
167
|
"""
|
131
168
|
Minimal autosave functionality.
|
@@ -137,7 +174,7 @@ class Autosaver:
|
|
137
174
|
def __init__(self, context, *args, **kwargs):
|
138
175
|
self.context = context
|
139
176
|
self.needs_saving = False
|
140
|
-
safe_dir =
|
177
|
+
safe_dir = self.context.kernel.os_information["WORKDIR"]
|
141
178
|
self.autosave_file = os.path.join(safe_dir, "_autosave.svg")
|
142
179
|
|
143
180
|
choices = [
|
@@ -394,6 +431,7 @@ class MeerK40t(MWindow):
|
|
394
431
|
self.tips_at_startup()
|
395
432
|
self.parametric_info = None
|
396
433
|
self.autosave = Autosaver(self.context)
|
434
|
+
self.handover = GUIThread(self.context)
|
397
435
|
kernel = self.context.kernel
|
398
436
|
if hasattr(kernel.args, "maximized") and kernel.args.maximized:
|
399
437
|
self.Maximize()
|
@@ -538,10 +576,18 @@ class MeerK40t(MWindow):
|
|
538
576
|
myPilImage = Image.new(
|
539
577
|
"RGB", (myWxImage.GetWidth(), myWxImage.GetHeight())
|
540
578
|
)
|
541
|
-
|
579
|
+
try:
|
580
|
+
byte_data = bytes(myWxImage.GetData())
|
581
|
+
myPilImage.frombytes(byte_data)
|
582
|
+
except TypeError as e:
|
583
|
+
console = self.context.root.channel("console")
|
584
|
+
console(f"Error while pasting image: {e}")
|
585
|
+
return None
|
542
586
|
return myPilImage
|
543
587
|
|
544
588
|
image = imageToPil(WxBitmapToWxImage(bmp))
|
589
|
+
if image is None:
|
590
|
+
return
|
545
591
|
dpi = DEFAULT_PPI
|
546
592
|
matrix = Matrix(f"scale({UNITS_PER_PIXEL})")
|
547
593
|
# _("Paste image")
|
@@ -2345,6 +2391,22 @@ class MeerK40t(MWindow):
|
|
2345
2391
|
> 0,
|
2346
2392
|
},
|
2347
2393
|
)
|
2394
|
+
secondary_commands = [
|
2395
|
+
"element union",
|
2396
|
+
"element difference",
|
2397
|
+
"element xor",
|
2398
|
+
"element intersection",
|
2399
|
+
]
|
2400
|
+
try:
|
2401
|
+
import pyclipr
|
2402
|
+
primary_commands = [
|
2403
|
+
"clipper union",
|
2404
|
+
"clipper difference",
|
2405
|
+
"clipper xor",
|
2406
|
+
"clipper intersection",
|
2407
|
+
]
|
2408
|
+
except ImportError:
|
2409
|
+
primary_commands = list(secondary_commands)
|
2348
2410
|
kernel.register(
|
2349
2411
|
"button/geometry/Union",
|
2350
2412
|
{
|
@@ -2352,7 +2414,8 @@ class MeerK40t(MWindow):
|
|
2352
2414
|
"icon": icon_cag_union,
|
2353
2415
|
"tip": _("Create a union of the selected elements"),
|
2354
2416
|
"help": "cag",
|
2355
|
-
"action": exec_in_undo_scope("Union", "
|
2417
|
+
"action": exec_in_undo_scope("Union", f"{primary_commands[0]}\n"),
|
2418
|
+
"action_right": exec_in_undo_scope("Union", f"{secondary_commands[0]}\n"),
|
2356
2419
|
"size": bsize_small,
|
2357
2420
|
"rule_enabled": lambda cond: len(
|
2358
2421
|
list(kernel.elements.elems(emphasized=True))
|
@@ -2367,7 +2430,8 @@ class MeerK40t(MWindow):
|
|
2367
2430
|
"icon": icon_cag_subtract,
|
2368
2431
|
"tip": _("Create a difference of the selected elements"),
|
2369
2432
|
"help": "cag",
|
2370
|
-
"action": exec_in_undo_scope("Difference", "
|
2433
|
+
"action": exec_in_undo_scope("Difference", f"{primary_commands[1]}\n"),
|
2434
|
+
"action_right": exec_in_undo_scope("Difference", f"{secondary_commands[1]}\n"),
|
2371
2435
|
"size": bsize_small,
|
2372
2436
|
"rule_enabled": lambda cond: len(
|
2373
2437
|
list(kernel.elements.elems(emphasized=True))
|
@@ -2382,7 +2446,8 @@ class MeerK40t(MWindow):
|
|
2382
2446
|
"icon": icon_cag_xor,
|
2383
2447
|
"tip": _("Create a xor of the selected elements"),
|
2384
2448
|
"help": "cag",
|
2385
|
-
"action": exec_in_undo_scope("
|
2449
|
+
"action": exec_in_undo_scope("XOR", f"{primary_commands[2]}\n"),
|
2450
|
+
"action_right": exec_in_undo_scope("XOR", f"{secondary_commands[2]}\n"),
|
2386
2451
|
"size": bsize_small,
|
2387
2452
|
"rule_enabled": lambda cond: len(
|
2388
2453
|
list(kernel.elements.elems(emphasized=True))
|
@@ -2397,7 +2462,8 @@ class MeerK40t(MWindow):
|
|
2397
2462
|
"icon": icon_cag_common,
|
2398
2463
|
"tip": _("Create a intersection of the selected elements"),
|
2399
2464
|
"help": "cag",
|
2400
|
-
"action": exec_in_undo_scope("Intersection", "
|
2465
|
+
"action": exec_in_undo_scope("Intersection", f"{primary_commands[3]}\n"),
|
2466
|
+
"action_right": exec_in_undo_scope("Intersection", f"{secondary_commands[3]}\n"),
|
2401
2467
|
"size": bsize_small,
|
2402
2468
|
"rule_enabled": lambda cond: len(
|
2403
2469
|
list(kernel.elements.elems(emphasized=True))
|
meerk40t/gui/wxmscene.py
CHANGED
@@ -156,13 +156,9 @@ class MeerK40tScenePanel(wx.Panel):
|
|
156
156
|
self.context.setting(bool, "clear_magnets", True)
|
157
157
|
|
158
158
|
# Save / Load the content of magnets
|
159
|
-
from os.path import join
|
159
|
+
from os.path import join
|
160
160
|
|
161
|
-
|
162
|
-
|
163
|
-
self._magnet_file = join(
|
164
|
-
realpath(get_safe_path(self.context.kernel.name)), "magnets.cfg"
|
165
|
-
)
|
161
|
+
self._magnet_file = join(self.context.kernel.os_information["WORKDIR"], "magnets.cfg")
|
166
162
|
self.load_magnets()
|
167
163
|
# Add a plugin routine to be called at the time of a full new start
|
168
164
|
context.kernel.register(
|
@@ -872,6 +868,99 @@ class MeerK40tScenePanel(wx.Panel):
|
|
872
868
|
else:
|
873
869
|
channel(_("Target needs to be one of primary, secondary, circular"))
|
874
870
|
|
871
|
+
# Establishes magnet commands
|
872
|
+
@context.console_argument(
|
873
|
+
"action", type=str, help=_("Action: clear or set / delete with coordinate")
|
874
|
+
)
|
875
|
+
@context.console_argument(
|
876
|
+
"axis", type=str, help=_("Axis (X or Y)")
|
877
|
+
)
|
878
|
+
@context.console_argument("pos", type=str, help=_("Position for magnetline"))
|
879
|
+
@context.console_command(
|
880
|
+
"magnet",
|
881
|
+
help=_("magnet <action> <axis> <position>"),
|
882
|
+
input_type="scene",
|
883
|
+
)
|
884
|
+
def magnet_set(
|
885
|
+
command,
|
886
|
+
channel,
|
887
|
+
_,
|
888
|
+
action=None,
|
889
|
+
axis=None,
|
890
|
+
pos=None,
|
891
|
+
**kwarg,
|
892
|
+
):
|
893
|
+
def info(opt_msg):
|
894
|
+
channel(
|
895
|
+
_("You need to provide the intended action:") + "\n" +
|
896
|
+
_("clear x - clear y : will clear all magnets on the given axis") + "\n" +
|
897
|
+
_("set x <pos> - set y <pos>: will set a magnet line on the given axis") + "\n" +
|
898
|
+
_("delete x <pos> - delete y <pos>: will delete the magnet line on the given axis")
|
899
|
+
)
|
900
|
+
if opt_msg:
|
901
|
+
channel(opt_msg)
|
902
|
+
|
903
|
+
if action is None or axis is None or axis.upper() not in ("X", "Y"):
|
904
|
+
info("")
|
905
|
+
return
|
906
|
+
action = action.lower()
|
907
|
+
axis = axis.upper()
|
908
|
+
value = None
|
909
|
+
if pos:
|
910
|
+
try:
|
911
|
+
rel_len = self.context.device.view.width if axis == "X" else self.context.device.view.height
|
912
|
+
value = float(Length(pos, relative_length=rel_len))
|
913
|
+
except ValueError:
|
914
|
+
info (f"Invalid length: {pos}")
|
915
|
+
return
|
916
|
+
|
917
|
+
if action != "clear" and value is None:
|
918
|
+
info(_("You need to provide a position"))
|
919
|
+
return
|
920
|
+
|
921
|
+
if action == "clear":
|
922
|
+
if axis == "X":
|
923
|
+
count = len(self.magnet_x)
|
924
|
+
self.magnet_x.clear()
|
925
|
+
else:
|
926
|
+
count = len(self.magnet_y)
|
927
|
+
self.magnet_y.clear()
|
928
|
+
self.save_magnets()
|
929
|
+
self.context.signal("refresh_scene", "Scene")
|
930
|
+
channel (_("Deleted {count} magnet lines on axis {axis}").format(axis=axis, count=count))
|
931
|
+
elif action == "set":
|
932
|
+
done = False
|
933
|
+
if axis == "X":
|
934
|
+
if not value in self.magnet_x:
|
935
|
+
done = True
|
936
|
+
self.magnet_x.append(value)
|
937
|
+
else:
|
938
|
+
if not value in self.magnet_y:
|
939
|
+
done = True
|
940
|
+
self.magnet_y.append(value)
|
941
|
+
self.save_magnets()
|
942
|
+
self.context.signal("refresh_scene", "Scene")
|
943
|
+
if done:
|
944
|
+
channel(_("Magnetline appended at {pos} on axis {axis}").format(pos=pos, axis=axis))
|
945
|
+
else:
|
946
|
+
channel(_("Magnetline was already present"))
|
947
|
+
elif action.startswith("del"):
|
948
|
+
done = False
|
949
|
+
if axis == "X":
|
950
|
+
if value in self.magnet_x:
|
951
|
+
done = True
|
952
|
+
self.magnet_x.remove(value)
|
953
|
+
else:
|
954
|
+
if value in self.magnet_y:
|
955
|
+
done = True
|
956
|
+
self.magnet_y.remove(value)
|
957
|
+
self.save_magnets()
|
958
|
+
self.context.signal("refresh_scene", "Scene")
|
959
|
+
if done:
|
960
|
+
channel(_("Magnetline removed at {pos} on axis {axis}").format(pos=pos, axis=axis))
|
961
|
+
else:
|
962
|
+
channel(_("Magnetline was not existing"))
|
963
|
+
|
875
964
|
def toggle_ref_obj(self):
|
876
965
|
for e in self.scene.context.elements.flat(types=elem_nodes, emphasized=True):
|
877
966
|
if self.reference_object == e:
|
meerk40t/gui/wxmtree.py
CHANGED
@@ -580,7 +580,7 @@ class ShadowTree:
|
|
580
580
|
self.tree_images = None
|
581
581
|
self.name = "Project"
|
582
582
|
self._freeze = False
|
583
|
-
testsize = dip_size(self, 20, 20)
|
583
|
+
testsize = dip_size(self.wxtree, 20, 20)
|
584
584
|
self.iconsize = testsize[1]
|
585
585
|
self.iconstates = {}
|
586
586
|
self.last_call = 0
|
@@ -1430,17 +1430,23 @@ class ShadowTree:
|
|
1430
1430
|
@param kwargs:
|
1431
1431
|
@return:
|
1432
1432
|
"""
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1433
|
+
try:
|
1434
|
+
parent = node.parent
|
1435
|
+
parent_item = parent._item
|
1436
|
+
if parent_item is None:
|
1437
|
+
# We are appending items in tree before registration.
|
1438
|
+
return
|
1439
|
+
tree = self.wxtree
|
1440
|
+
if pos is None:
|
1441
|
+
node._item = tree.AppendItem(parent_item, self.name)
|
1442
|
+
else:
|
1443
|
+
node._item = tree.InsertItem(parent_item, pos, self.name)
|
1444
|
+
tree.SetItemData(node._item, node)
|
1445
|
+
except Exception as e:
|
1446
|
+
# Invalid tree?
|
1447
|
+
self.context.signal("rebuild_tree", "all")
|
1448
|
+
print (f"We encountered an error at node registration: {e}")
|
1437
1449
|
return
|
1438
|
-
tree = self.wxtree
|
1439
|
-
if pos is None:
|
1440
|
-
node._item = tree.AppendItem(parent_item, self.name)
|
1441
|
-
else:
|
1442
|
-
node._item = tree.InsertItem(parent_item, pos, self.name)
|
1443
|
-
tree.SetItemData(node._item, node)
|
1444
1450
|
self.update_decorations(node, False)
|
1445
1451
|
wxcolor = self.wxtree.GetForegroundColour()
|
1446
1452
|
attribute_to_try = "fill" if node.type == "elem text" else "stroke"
|
meerk40t/gui/wxutils.py
CHANGED
@@ -1222,7 +1222,7 @@ class StaticBoxSizer(wx.StaticBoxSizer):
|
|
1222
1222
|
if label is None:
|
1223
1223
|
label = ""
|
1224
1224
|
self.sbox = wx.StaticBox(parent, id, label=label)
|
1225
|
-
self.sbox.SetMinSize(dip_size(self, 50, 50))
|
1225
|
+
self.sbox.SetMinSize(dip_size(self.sbox, 50, 50))
|
1226
1226
|
super().__init__(self.sbox, orientation)
|
1227
1227
|
self.parent = parent
|
1228
1228
|
|
meerk40t/image/imagetools.py
CHANGED
@@ -43,7 +43,12 @@ def img_to_polygons(
|
|
43
43
|
_, th2 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
44
44
|
|
45
45
|
# Find contours in the binary image
|
46
|
-
|
46
|
+
try:
|
47
|
+
contours, hierarchies = cv2.findContours(th2, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
|
48
|
+
except ValueError:
|
49
|
+
# Invalid data
|
50
|
+
return ([], [])
|
51
|
+
|
47
52
|
# print(f"Found {len(contours)} contours and {len(hierarchies)} hierarchies")
|
48
53
|
width, height = node_image.size
|
49
54
|
minarea = int(minimal / 100.0 * width * height)
|
@@ -147,9 +152,14 @@ def do_innerwhite(
|
|
147
152
|
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)
|
148
153
|
|
149
154
|
# Find contours
|
150
|
-
|
151
|
-
|
152
|
-
|
155
|
+
try:
|
156
|
+
contours, hierarchy = cv2.findContours(
|
157
|
+
thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
|
158
|
+
)
|
159
|
+
except ValueError:
|
160
|
+
# Invalid data
|
161
|
+
continue
|
162
|
+
|
153
163
|
linecandidates = list()
|
154
164
|
|
155
165
|
minarea = int(minimal / 100.0 * width * height)
|
@@ -402,7 +412,12 @@ def img_to_rectangles(
|
|
402
412
|
_, th2 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
403
413
|
|
404
414
|
# Find contours in the binary image
|
405
|
-
|
415
|
+
try:
|
416
|
+
contours, hierarchies = cv2.findContours(th2, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
|
417
|
+
except ValueError:
|
418
|
+
# Invalid data
|
419
|
+
return ([], [])
|
420
|
+
|
406
421
|
# print(f"Found {len(contours)} contours and {len(hierarchies)} hierarchies")
|
407
422
|
width, height = node_image.size
|
408
423
|
minarea = int(minimal / 100.0 * width * height)
|
@@ -1480,7 +1495,7 @@ def plugin(kernel, lifecycle=None):
|
|
1480
1495
|
)
|
1481
1496
|
@context.console_command(
|
1482
1497
|
"linecut",
|
1483
|
-
help=_("Cuts
|
1498
|
+
help=_("Cuts an image with a line"),
|
1484
1499
|
input_type="image",
|
1485
1500
|
output_type="image",
|
1486
1501
|
hidden=True,
|
meerk40t/kernel/kernel.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import functools
|
2
2
|
import inspect
|
3
3
|
import os
|
4
|
-
import platform
|
5
4
|
import re
|
6
5
|
import subprocess
|
7
6
|
import threading
|
@@ -181,9 +180,20 @@ class Kernel(Settings):
|
|
181
180
|
# Arguments Objects
|
182
181
|
self.args = None
|
183
182
|
|
183
|
+
self.os_information = self._get_environment()
|
184
|
+
|
184
185
|
def __str__(self):
|
185
186
|
return f"Kernel({self.name}, {self.profile}, {self.version})"
|
186
187
|
|
188
|
+
def _get_environment(self):
|
189
|
+
from platform import system
|
190
|
+
from tempfile import gettempdir
|
191
|
+
return {
|
192
|
+
"OS_NAME": system(),
|
193
|
+
"OS_TEMPDIR": os.path.realpath(gettempdir()),
|
194
|
+
"WORKDIR": os.path.realpath(get_safe_path(self.name, create=True)),
|
195
|
+
}
|
196
|
+
|
187
197
|
def set_language(self, language, localedir="locale"):
|
188
198
|
from . import set_language
|
189
199
|
|
@@ -244,15 +254,21 @@ class Kernel(Settings):
|
|
244
254
|
kwargs_repr = [f"{k}={v}" for k, v in kwargs.items()]
|
245
255
|
signature = ", ".join(args_repr + kwargs_repr)
|
246
256
|
start = f"Calling {str(obj)}.{func.__name__}({signature})"
|
247
|
-
|
257
|
+
try:
|
258
|
+
debug_file.write(start + "\n")
|
259
|
+
except (ValueError, OSError):
|
260
|
+
pass
|
248
261
|
print(start)
|
249
262
|
t = time.time()
|
250
263
|
value = func(*args, **kwargs)
|
251
264
|
t = time.time() - t
|
252
265
|
finish = f" {func.__name__} returned {value} after {t * 1000}ms"
|
253
266
|
print(finish)
|
254
|
-
|
255
|
-
|
267
|
+
try:
|
268
|
+
debug_file.write(finish + "\n")
|
269
|
+
debug_file.flush()
|
270
|
+
except (ValueError, OSError):
|
271
|
+
pass
|
256
272
|
return value
|
257
273
|
|
258
274
|
return wrapper_debug
|
@@ -1226,7 +1242,7 @@ class Kernel(Settings):
|
|
1226
1242
|
|
1227
1243
|
line = await loop.run_in_executor(None, sys.stdin.readline)
|
1228
1244
|
line = line.strip()
|
1229
|
-
if line in ("quit", "shutdown", "restart"):
|
1245
|
+
if line in ("quit", "shutdown", "exit", "restart"):
|
1230
1246
|
self._quit = True
|
1231
1247
|
if line == "restart":
|
1232
1248
|
self._restart = True
|
@@ -1906,6 +1922,8 @@ class Kernel(Settings):
|
|
1906
1922
|
# Could be recurring job. Reset on reschedule.
|
1907
1923
|
except AttributeError:
|
1908
1924
|
pass
|
1925
|
+
if job.job_name is None:
|
1926
|
+
job.job_name = f"job_{id(job)}"
|
1909
1927
|
self.jobs[job.job_name] = job
|
1910
1928
|
return job
|
1911
1929
|
|
@@ -2667,6 +2685,9 @@ class Kernel(Settings):
|
|
2667
2685
|
channel(_("----------"))
|
2668
2686
|
channel(_("Scheduled Processes:"))
|
2669
2687
|
for i, job_name in enumerate(self.jobs):
|
2688
|
+
if job_name is None:
|
2689
|
+
channel(_("Empty job definition..."))
|
2690
|
+
continue
|
2670
2691
|
job = self.jobs[job_name]
|
2671
2692
|
parts = list()
|
2672
2693
|
parts.append(f"{i + 1}:")
|
@@ -2737,6 +2758,8 @@ class Kernel(Settings):
|
|
2737
2758
|
channel(_("Timers:"))
|
2738
2759
|
i = 0
|
2739
2760
|
for job_name in self.jobs:
|
2761
|
+
if job_name is None:
|
2762
|
+
continue
|
2740
2763
|
if not job_name.startswith("timer"):
|
2741
2764
|
continue
|
2742
2765
|
i += 1
|
@@ -2772,6 +2795,8 @@ class Kernel(Settings):
|
|
2772
2795
|
skipped = False
|
2773
2796
|
canceled = False
|
2774
2797
|
for job_name in list(self.jobs):
|
2798
|
+
if job_name is None:
|
2799
|
+
continue
|
2775
2800
|
if not job_name.startswith("timer"):
|
2776
2801
|
continue
|
2777
2802
|
timer_name = job_name[5:]
|
@@ -2830,7 +2855,7 @@ class Kernel(Settings):
|
|
2830
2855
|
|
2831
2856
|
@self.console_command("beep", _("Perform beep"))
|
2832
2857
|
def beep(channel, _, **kwargs):
|
2833
|
-
OS_NAME =
|
2858
|
+
OS_NAME = self.os_information["OS_NAME"]
|
2834
2859
|
system_sound = {
|
2835
2860
|
"Windows": r"c:\Windows\Media\Sounds\Alarm01.wav",
|
2836
2861
|
"Darwin": "/System/Library/Sounds/Ping.aiff",
|
meerk40t/kernel/settings.py
CHANGED
@@ -54,6 +54,8 @@ class Settings:
|
|
54
54
|
FileNotFoundError,
|
55
55
|
):
|
56
56
|
return
|
57
|
+
except UnicodeDecodeError as e:
|
58
|
+
print ("The config file contained unsupported characters, please share the file with the dev team")
|
57
59
|
except (configparser.DuplicateOptionError, configparser.DuplicateSectionError) as e:
|
58
60
|
print (f"We had a duplication error in the config, try to recover from {e}")
|
59
61
|
for section in parser.sections():
|
meerk40t/lihuiyu/device.py
CHANGED
@@ -6,14 +6,16 @@ the given device type.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
from hashlib import md5
|
9
|
+
import platform
|
10
|
+
|
9
11
|
import meerk40t.constants as mkconst
|
10
12
|
from meerk40t.core.laserjob import LaserJob
|
11
13
|
from meerk40t.core.spoolers import Spooler
|
12
14
|
from meerk40t.core.view import View
|
13
15
|
from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
|
14
16
|
|
15
|
-
from
|
16
|
-
from
|
17
|
+
from meerk40t.core.units import UNITS_PER_MIL, Length
|
18
|
+
from meerk40t.device.mixins import Status
|
17
19
|
from .controller import LihuiyuController
|
18
20
|
from .driver import LihuiyuDriver
|
19
21
|
from .tcp_connection import TCPOutput
|
@@ -514,7 +516,10 @@ class LihuiyuDevice(Service, Status):
|
|
514
516
|
self.setting(str, "serial", None)
|
515
517
|
self.setting(bool, "serial_enable", False)
|
516
518
|
|
517
|
-
|
519
|
+
# Linux prevents ports below 1024 to be used in an non-root context
|
520
|
+
def_port = 1022 if platform.system() == "Windows" else "1025"
|
521
|
+
|
522
|
+
self.setting(int, "port", def_port)
|
518
523
|
self.setting(str, "address", "localhost")
|
519
524
|
|
520
525
|
self.driver = LihuiyuDriver(self)
|
@@ -978,6 +983,7 @@ class LihuiyuDevice(Service, Status):
|
|
978
983
|
server = self.open_as("module/TCPServer", server_name, port=port)
|
979
984
|
if quit:
|
980
985
|
self.close(server_name)
|
986
|
+
channel(_("TCP Server for lihuiyu has been closed"))
|
981
987
|
return
|
982
988
|
channel(_("TCP Server for lihuiyu on port: {port}").format(port=port))
|
983
989
|
if verbose:
|
meerk40t/main.py
CHANGED
@@ -11,7 +11,7 @@ import os.path
|
|
11
11
|
import sys
|
12
12
|
|
13
13
|
APPLICATION_NAME = "MeerK40t"
|
14
|
-
APPLICATION_VERSION = "0.9.
|
14
|
+
APPLICATION_VERSION = "0.9.7030"
|
15
15
|
|
16
16
|
if not getattr(sys, "frozen", False):
|
17
17
|
# If .git directory does not exist we are running from a package like pypi
|
@@ -151,6 +151,9 @@ parser.add_argument(
|
|
151
151
|
action="store_true",
|
152
152
|
help="start window maximized",
|
153
153
|
)
|
154
|
+
parser.add_argument(
|
155
|
+
"-d", "--daemon", action="store_true", help="keep MeerK40t in background"
|
156
|
+
)
|
154
157
|
|
155
158
|
|
156
159
|
def run():
|
@@ -220,12 +223,26 @@ def _exe(restarted, args):
|
|
220
223
|
kernel.add_plugin(internal_plugins)
|
221
224
|
kernel.add_plugin(external_plugins)
|
222
225
|
auto = hasattr(kernel.args, "auto") and kernel.args.auto
|
226
|
+
command = hasattr(kernel.args, "execute") and kernel.args.execute
|
223
227
|
console = hasattr(kernel.args, "console") and kernel.args.console
|
228
|
+
daemon = hasattr(kernel.args, "daemon") and kernel.args.daemon
|
229
|
+
server_mode = False
|
230
|
+
if command:
|
231
|
+
for c in command:
|
232
|
+
server_mode = server_mode or any(substring in c for substring in ("lhyserver", "grblserver", "ruidacontrol", "grblcontrol", "webserver"))
|
233
|
+
nogui = (
|
234
|
+
(hasattr(kernel.args, "gui_suppress") and kernel.args.gui_suppress) or
|
235
|
+
(hasattr(kernel.args, "no_gui") and kernel.args.no_gui)
|
236
|
+
)
|
224
237
|
for idx, attrib in enumerate(("mktablength", "mktabpositions")):
|
225
238
|
kernel.register(f"registered_mk_svg_parameters/tabs{idx}", attrib)
|
226
239
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
240
|
+
require_partial_mode = False
|
241
|
+
if (
|
242
|
+
(not console or nogui) and
|
243
|
+
(auto or daemon or server_mode)
|
244
|
+
):
|
245
|
+
require_partial_mode = True
|
246
|
+
# print (f"Auto: {auto}, Command: {command}, Console: {console}, daemon: {daemon}, nogui:{nogui}, Server: {server_mode} -> {require_partial_mode}")
|
247
|
+
kernel(partial=require_partial_mode)
|
231
248
|
return hasattr(kernel, "restart") and kernel.restart
|