meerk40t 0.9.7010__py2.py3-none-any.whl → 0.9.7020__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/elements/branches.py +18 -4
- meerk40t/core/elements/element_treeops.py +34 -0
- meerk40t/core/elements/elements.py +49 -63
- meerk40t/core/elements/offset_clpr.py +4 -3
- meerk40t/core/elements/shapes.py +1 -1
- meerk40t/core/elements/testcases.py +105 -0
- 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/extra/encode_detect.py +8 -2
- meerk40t/extra/hershey.py +2 -3
- meerk40t/extra/inkscape.py +3 -5
- meerk40t/extra/outerworld.py +2 -3
- meerk40t/extra/param_functions.py +1 -1
- meerk40t/grbl/device.py +4 -1
- meerk40t/grbl/gui/grblcontroller.py +2 -2
- meerk40t/gui/busy.py +75 -13
- meerk40t/gui/choicepropertypanel.py +364 -375
- meerk40t/gui/consolepanel.py +3 -3
- 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 +2 -2
- meerk40t/gui/tips.py +2 -3
- meerk40t/gui/toolwidgets/toolmeasure.py +4 -1
- meerk40t/gui/wxmeerk40t.py +6 -3
- meerk40t/gui/wxmmain.py +72 -6
- meerk40t/gui/wxmscene.py +2 -6
- meerk40t/gui/wxmtree.py +17 -11
- meerk40t/gui/wxutils.py +1 -1
- meerk40t/image/imagetools.py +20 -5
- meerk40t/kernel/kernel.py +21 -2
- meerk40t/main.py +1 -1
- meerk40t/network/console_server.py +52 -14
- meerk40t/network/web_server.py +15 -1
- meerk40t/ruida/device.py +5 -1
- meerk40t/tools/polybool.py +2 -1
- meerk40t/tools/shxparser.py +92 -34
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/RECORD +55 -54
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7010.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +0 -0
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(
|
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)
|
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
|
|
@@ -1906,6 +1916,8 @@ class Kernel(Settings):
|
|
1906
1916
|
# Could be recurring job. Reset on reschedule.
|
1907
1917
|
except AttributeError:
|
1908
1918
|
pass
|
1919
|
+
if job.job_name is None:
|
1920
|
+
job.job_name = f"job_{id(job)}"
|
1909
1921
|
self.jobs[job.job_name] = job
|
1910
1922
|
return job
|
1911
1923
|
|
@@ -2667,6 +2679,9 @@ class Kernel(Settings):
|
|
2667
2679
|
channel(_("----------"))
|
2668
2680
|
channel(_("Scheduled Processes:"))
|
2669
2681
|
for i, job_name in enumerate(self.jobs):
|
2682
|
+
if job_name is None:
|
2683
|
+
channel(_("Empty job definition..."))
|
2684
|
+
continue
|
2670
2685
|
job = self.jobs[job_name]
|
2671
2686
|
parts = list()
|
2672
2687
|
parts.append(f"{i + 1}:")
|
@@ -2737,6 +2752,8 @@ class Kernel(Settings):
|
|
2737
2752
|
channel(_("Timers:"))
|
2738
2753
|
i = 0
|
2739
2754
|
for job_name in self.jobs:
|
2755
|
+
if job_name is None:
|
2756
|
+
continue
|
2740
2757
|
if not job_name.startswith("timer"):
|
2741
2758
|
continue
|
2742
2759
|
i += 1
|
@@ -2772,6 +2789,8 @@ class Kernel(Settings):
|
|
2772
2789
|
skipped = False
|
2773
2790
|
canceled = False
|
2774
2791
|
for job_name in list(self.jobs):
|
2792
|
+
if job_name is None:
|
2793
|
+
continue
|
2775
2794
|
if not job_name.startswith("timer"):
|
2776
2795
|
continue
|
2777
2796
|
timer_name = job_name[5:]
|
@@ -2830,7 +2849,7 @@ class Kernel(Settings):
|
|
2830
2849
|
|
2831
2850
|
@self.console_command("beep", _("Perform beep"))
|
2832
2851
|
def beep(channel, _, **kwargs):
|
2833
|
-
OS_NAME =
|
2852
|
+
OS_NAME = self.os_information["OS_NAME"]
|
2834
2853
|
system_sound = {
|
2835
2854
|
"Windows": r"c:\Windows\Media\Sounds\Alarm01.wav",
|
2836
2855
|
"Darwin": "/System/Library/Sounds/Ping.aiff",
|
meerk40t/main.py
CHANGED
@@ -26,6 +26,8 @@ def plugin(kernel, lifecycle=None):
|
|
26
26
|
command, channel, _, port=23, silent=False, quit=False, **kwargs
|
27
27
|
):
|
28
28
|
root = kernel.root
|
29
|
+
# Variable to store input
|
30
|
+
root.__console_buffer = ""
|
29
31
|
try:
|
30
32
|
server = root.open_as("module/TCPServer", "console-server", port=port)
|
31
33
|
if quit:
|
@@ -36,25 +38,61 @@ def plugin(kernel, lifecycle=None):
|
|
36
38
|
"{kernel_name} {kernel_version} Telnet Console.\r\n"
|
37
39
|
).format(kernel_name=kernel.name, kernel_version=kernel.version)
|
38
40
|
send.line_end = "\r\n"
|
39
|
-
|
40
41
|
recv = root.channel("console-server/recv")
|
41
|
-
|
42
|
-
channel(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
except (OSError, ValueError):
|
43
|
+
channel(_("Server failed on port: {port}").format(port=port))
|
44
|
+
return
|
45
|
+
|
46
|
+
def exec_command(data: str) -> None:
|
47
|
+
# We are in a different thread, so let's hand over stuff to the gui
|
48
|
+
if isinstance(data, bytes):
|
49
|
+
try:
|
50
|
+
data = data.decode()
|
51
|
+
except UnicodeDecodeError as e:
|
52
|
+
return
|
53
|
+
start = 0
|
54
|
+
while True:
|
55
|
+
idx = data.find("|", start)
|
56
|
+
if idx < 0:
|
57
|
+
break
|
58
|
+
# Is the amount of quotation marks odd (ie non-even)?
|
59
|
+
# Yes: we are in the middle of a str
|
60
|
+
# No: we can split the command
|
61
|
+
quotations = data.count('"', 0, idx)
|
62
|
+
if quotations % 2 == 0:
|
63
|
+
data = data[:idx].rstrip() + "\n" + data[idx+1:].lstrip()
|
64
|
+
start = idx + 1
|
65
|
+
root.__console_buffer += data
|
66
|
+
while "\n" in root.__console_buffer:
|
67
|
+
pos = root.__console_buffer.find("\n")
|
68
|
+
command = root.__console_buffer[0:pos].strip("\r")
|
69
|
+
root.__console_buffer = root.__console_buffer[pos + 1 :]
|
70
|
+
if handover is None:
|
71
|
+
root.console(command + "\n")
|
72
|
+
else:
|
73
|
+
handover(command)
|
74
|
+
|
75
|
+
handover = None
|
76
|
+
for result in root.find("gui/handover"):
|
77
|
+
# Do we have a thread handover routine?
|
78
|
+
if result is not None:
|
79
|
+
handover, _path, suffix_path = result
|
80
|
+
break
|
81
|
+
recv.watch(exec_command)
|
82
|
+
|
83
|
+
channel(
|
84
|
+
_(
|
85
|
+
"{name} {version} console server on port: {port}".format(
|
86
|
+
name=kernel.name, version=kernel.version, port=port
|
47
87
|
)
|
48
88
|
)
|
89
|
+
)
|
49
90
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
91
|
+
if not silent:
|
92
|
+
console = root.channel("console")
|
93
|
+
console.watch(send)
|
94
|
+
server.events_channel.watch(console)
|
54
95
|
|
55
|
-
except (OSError, ValueError):
|
56
|
-
channel(_("Server failed on port: {port}").format(port=port))
|
57
|
-
return
|
58
96
|
|
59
97
|
@kernel.console_option(
|
60
98
|
"port", "p", type=int, default=2080, help=_("port to listen on.")
|
meerk40t/network/web_server.py
CHANGED
@@ -34,6 +34,13 @@ class WebServer(Module):
|
|
34
34
|
)
|
35
35
|
self.server_headers = dict()
|
36
36
|
self.client_headers = dict()
|
37
|
+
self.handover = None
|
38
|
+
root = self.context.root
|
39
|
+
for result in root.find("gui/handover"):
|
40
|
+
# Do we have a thread handover routine?
|
41
|
+
if result is not None:
|
42
|
+
self.handover, _path, suffix_path = result
|
43
|
+
break
|
37
44
|
|
38
45
|
def stop(self):
|
39
46
|
self.state = "terminate"
|
@@ -45,6 +52,13 @@ class WebServer(Module):
|
|
45
52
|
if self.socket is not None:
|
46
53
|
self.socket.close()
|
47
54
|
self.socket = None
|
55
|
+
|
56
|
+
def send(self, command):
|
57
|
+
if self.handover is None:
|
58
|
+
self.context(f"{command}\n")
|
59
|
+
else:
|
60
|
+
self.handover(command)
|
61
|
+
|
48
62
|
|
49
63
|
def receive(self, data):
|
50
64
|
def parse_received_data():
|
@@ -92,7 +106,7 @@ class WebServer(Module):
|
|
92
106
|
content = f"Received command: '{command}'"
|
93
107
|
elif self.client_headers["TYPE"] == "POST" and "post_cmd" in self.client_headers:
|
94
108
|
command = self.client_headers["post_cmd"]
|
95
|
-
self.
|
109
|
+
self.send(command)
|
96
110
|
content = f"Received command: '{command}'"
|
97
111
|
return content_type, content
|
98
112
|
|
meerk40t/ruida/device.py
CHANGED
@@ -263,18 +263,22 @@ class RuidaDevice(Service):
|
|
263
263
|
choice_dict["choices"] = ["UNCONFIGURED"]
|
264
264
|
choice_dict["display"] = ["pyserial-not-installed"]
|
265
265
|
|
266
|
+
from platform import system
|
267
|
+
is_linux = system() == "Linux"
|
268
|
+
|
266
269
|
choices = [
|
267
270
|
{
|
268
271
|
"attr": "serial_port",
|
269
272
|
"object": self,
|
270
273
|
"default": "UNCONFIGURED",
|
271
274
|
"type": str,
|
272
|
-
"style": "option",
|
275
|
+
"style": "combosmall" if is_linux else "option",
|
273
276
|
"label": "",
|
274
277
|
"tip": _("What serial interface does this device connect to?"),
|
275
278
|
"section": "_10_Serial Interface",
|
276
279
|
"subsection": "_00_",
|
277
280
|
"dynamic": update,
|
281
|
+
"exclusive": not is_linux,
|
278
282
|
},
|
279
283
|
{
|
280
284
|
"attr": "baud_rate",
|
meerk40t/tools/polybool.py
CHANGED
@@ -275,7 +275,8 @@ class LinkedList:
|
|
275
275
|
data.next = None
|
276
276
|
|
277
277
|
def remove_func():
|
278
|
-
data.previous
|
278
|
+
if data.previous is not None:
|
279
|
+
data.previous.next = data.next
|
279
280
|
if data.next is not None:
|
280
281
|
data.next.previous = data.previous
|
281
282
|
data.previous = None
|