meerk40t 0.9.7051__py2.py3-none-any.whl → 0.9.7900__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/controller.py +3 -3
- meerk40t/balormk/device.py +7 -0
- meerk40t/balormk/driver.py +23 -14
- meerk40t/balormk/galvo_commands.py +18 -3
- meerk40t/balormk/gui/balorconfig.py +6 -0
- meerk40t/balormk/livelightjob.py +36 -14
- meerk40t/camera/camera.py +1 -0
- meerk40t/camera/gui/camerapanel.py +154 -58
- meerk40t/camera/plugin.py +46 -5
- meerk40t/core/elements/branches.py +90 -20
- meerk40t/core/elements/elements.py +59 -37
- meerk40t/core/elements/trace.py +10 -6
- meerk40t/core/node/node.py +2 -0
- meerk40t/core/plotplanner.py +7 -4
- meerk40t/device/gui/defaultactions.py +78 -14
- meerk40t/dxf/dxf_io.py +42 -0
- meerk40t/grbl/controller.py +245 -35
- meerk40t/grbl/device.py +102 -26
- meerk40t/grbl/driver.py +8 -2
- meerk40t/grbl/gui/grblconfiguration.py +6 -0
- meerk40t/grbl/gui/grblcontroller.py +1 -1
- meerk40t/gui/about.py +7 -0
- meerk40t/gui/choicepropertypanel.py +20 -30
- meerk40t/gui/devicepanel.py +27 -16
- meerk40t/gui/icons.py +15 -0
- meerk40t/gui/laserpanel.py +102 -54
- meerk40t/gui/materialtest.py +10 -0
- meerk40t/gui/mkdebug.py +268 -9
- meerk40t/gui/navigationpanels.py +65 -7
- meerk40t/gui/propertypanels/operationpropertymain.py +185 -91
- meerk40t/gui/scenewidgets/elementswidget.py +7 -1
- meerk40t/gui/scenewidgets/selectionwidget.py +24 -9
- meerk40t/gui/simulation.py +1 -1
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +50 -40
- meerk40t/gui/statusbarwidgets/statusbar.py +2 -2
- meerk40t/gui/toolwidgets/toolmeasure.py +1 -1
- meerk40t/gui/toolwidgets/toolnodeedit.py +4 -1
- meerk40t/gui/toolwidgets/tooltabedit.py +9 -7
- meerk40t/gui/wxmeerk40t.py +2 -0
- meerk40t/gui/wxmmain.py +23 -9
- meerk40t/gui/wxmribbon.py +36 -0
- meerk40t/gui/wxutils.py +66 -42
- meerk40t/kernel/inhibitor.py +120 -0
- meerk40t/kernel/kernel.py +38 -0
- meerk40t/lihuiyu/controller.py +33 -3
- meerk40t/lihuiyu/device.py +99 -4
- meerk40t/lihuiyu/driver.py +62 -5
- meerk40t/lihuiyu/gui/lhycontrollergui.py +69 -24
- meerk40t/lihuiyu/gui/lhydrivergui.py +6 -0
- meerk40t/lihuiyu/laserspeed.py +17 -10
- meerk40t/lihuiyu/parser.py +23 -0
- meerk40t/main.py +1 -1
- meerk40t/moshi/gui/moshidrivergui.py +7 -0
- meerk40t/newly/controller.py +3 -2
- meerk40t/newly/device.py +23 -2
- meerk40t/newly/driver.py +8 -3
- meerk40t/newly/gui/newlyconfig.py +7 -0
- meerk40t/ruida/gui/ruidaconfig.py +7 -0
- meerk40t/tools/geomstr.py +68 -48
- meerk40t/tools/rasterplotter.py +0 -5
- meerk40t/tools/ttfparser.py +155 -82
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/RECORD +68 -67
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/zip-safe +0 -0
meerk40t/gui/laserpanel.py
CHANGED
@@ -29,7 +29,6 @@ from meerk40t.gui.wxutils import (
|
|
29
29
|
wxButton,
|
30
30
|
wxCheckBox,
|
31
31
|
wxComboBox,
|
32
|
-
wxStaticBitmap,
|
33
32
|
wxStaticText,
|
34
33
|
)
|
35
34
|
from meerk40t.kernel import lookup_listener, signal_listener
|
@@ -262,7 +261,7 @@ class LaserPanel(wx.Panel):
|
|
262
261
|
self.check_laser_arm()
|
263
262
|
|
264
263
|
self.button_outline = wxButton(self, wx.ID_ANY, _("Outline"))
|
265
|
-
self.button_outline.SetToolTip(_("Trace the outline the job"))
|
264
|
+
self.button_outline.SetToolTip(_("Trace the outline of the job"))
|
266
265
|
self.button_outline.SetBitmap(
|
267
266
|
icons8_pentagon.GetBitmap(
|
268
267
|
resize=self.icon_size,
|
@@ -332,6 +331,7 @@ class LaserPanel(wx.Panel):
|
|
332
331
|
lb_speed = wxStaticText(self, wx.ID_ANY, _("Speed"))
|
333
332
|
self.label_speed = wxStaticText(self, wx.ID_ANY, "0%")
|
334
333
|
self.slider_size = 20
|
334
|
+
self.power_mode = "relative"
|
335
335
|
self.slider_speed = wx.Slider(
|
336
336
|
self, wx.ID_ANY, value=10, minValue=1, maxValue=20
|
337
337
|
)
|
@@ -380,33 +380,67 @@ class LaserPanel(wx.Panel):
|
|
380
380
|
self.button_start_was_clicked = False
|
381
381
|
|
382
382
|
def update_override_controls(self):
|
383
|
+
def set_boundaries(slider, current_value, min_value, max_value):
|
384
|
+
slider.SetMin(min_value)
|
385
|
+
slider.SetMax(max_value)
|
386
|
+
slider.SetValue(current_value)
|
387
|
+
|
383
388
|
flag_power = False
|
384
389
|
override = False
|
385
|
-
if
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
390
|
+
if (
|
391
|
+
hasattr(self.context.device.driver, "has_adjustable_power")
|
392
|
+
and self.context.device.driver.has_adjustable_power
|
393
|
+
):
|
394
|
+
flag_power = True
|
395
|
+
# Let's establish the value and update the slider...
|
396
|
+
value = self.context.device.driver.power_scale
|
397
|
+
if value != 1:
|
398
|
+
override = True
|
399
|
+
half = self.slider_size / 2
|
400
|
+
sliderval = int(value * half)
|
401
|
+
sliderval = max(1, min(self.slider_size, sliderval))
|
402
|
+
set_boundaries(self.slider_power, sliderval, 1, self.slider_size)
|
403
|
+
self.slider_power.SetToolTip(
|
404
|
+
_("Increases/decreases the regular laser power by this amount.")
|
405
|
+
+ "\n"
|
406
|
+
+ _("This affects running jobs, so use with care!")
|
407
|
+
)
|
408
|
+
self.power_mode = "relative"
|
409
|
+
self.on_slider_power(None)
|
410
|
+
elif (
|
411
|
+
hasattr(self.context.device.driver, "has_adjustable_maximum_power")
|
412
|
+
and self.context.device.driver.has_adjustable_maximum_power
|
413
|
+
):
|
414
|
+
flag_power = True
|
415
|
+
override = True
|
416
|
+
dev_mode = getattr(self.context.kernel.root, "developer_mode", False)
|
417
|
+
# Let's establish the value and update the slider...
|
418
|
+
min_value = 1
|
419
|
+
max_value = 100 if dev_mode else 50
|
420
|
+
sliderval = min(max_value, self.context.device.driver.max_power_scale)
|
421
|
+
set_boundaries(self.slider_power, sliderval, min_value, max_value)
|
422
|
+
self.slider_power.SetToolTip(
|
423
|
+
_("Sets the maximum laser power level.")
|
424
|
+
+ "\n"
|
425
|
+
+ _("Setting this too high may cause damage to your laser tube!")
|
426
|
+
)
|
427
|
+
self.power_mode = "maximum"
|
428
|
+
self.on_slider_power(None)
|
397
429
|
flag_speed = False
|
398
|
-
if
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
430
|
+
if (
|
431
|
+
hasattr(self.context.device.driver, "has_adjustable_speed")
|
432
|
+
and self.context.device.driver.has_adjustable_speed
|
433
|
+
):
|
434
|
+
flag_speed = True
|
435
|
+
# Let's establish the value and update the slider...
|
436
|
+
value = self.context.device.driver.speed_scale
|
437
|
+
if value != 1:
|
438
|
+
override = True
|
439
|
+
half = self.slider_size / 2
|
440
|
+
sliderval = int(value * half)
|
441
|
+
sliderval = max(1, min(self.slider_size, sliderval))
|
442
|
+
set_boundaries(self.slider_speed, sliderval, 1, self.slider_size)
|
443
|
+
self.on_slider_speed(None)
|
410
444
|
|
411
445
|
self.sizer_power.Show(flag_power)
|
412
446
|
self.sizer_power.ShowItems(flag_power)
|
@@ -427,18 +461,22 @@ class LaserPanel(wx.Panel):
|
|
427
461
|
else:
|
428
462
|
self.slider_power.Enable(False)
|
429
463
|
self.slider_speed.Enable(False)
|
430
|
-
if
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
self.
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
464
|
+
if (
|
465
|
+
hasattr(self.context.device.driver, "has_adjustable_power")
|
466
|
+
and self.context.device.driver.has_adjustable_power
|
467
|
+
):
|
468
|
+
if event is not None:
|
469
|
+
self.context.device.driver.set_power_scale(1.0)
|
470
|
+
self.slider_power.SetValue(10)
|
471
|
+
self.on_slider_power(None)
|
472
|
+
if (
|
473
|
+
hasattr(self.context.device.driver, "has_adjustable_speed")
|
474
|
+
and self.context.device.driver.has_adjustable_speed
|
475
|
+
):
|
476
|
+
if event is not None:
|
477
|
+
self.context.device.driver.set_speed_scale(1.0)
|
478
|
+
self.slider_speed.SetValue(10)
|
479
|
+
self.on_slider_speed(None)
|
442
480
|
|
443
481
|
def on_slider_speed(self, event):
|
444
482
|
sliderval = self.slider_speed.GetValue()
|
@@ -452,12 +490,18 @@ class LaserPanel(wx.Panel):
|
|
452
490
|
|
453
491
|
def on_slider_power(self, event):
|
454
492
|
sliderval = self.slider_power.GetValue()
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
493
|
+
if self.power_mode == "maximum":
|
494
|
+
# Maximum power mode, so we just set the value.
|
495
|
+
if event is not None:
|
496
|
+
self.context.device.driver.max_power_scale = sliderval
|
497
|
+
msg = f"{sliderval}%"
|
498
|
+
else:
|
499
|
+
half = self.slider_size / 2
|
500
|
+
newvalue = sliderval - half # -> -9 to +10
|
501
|
+
factor = 1 + newvalue / half
|
502
|
+
if event is not None:
|
503
|
+
self.context.device.driver.set_power_scale(factor)
|
504
|
+
msg = f"{'+' if factor > 1 else ''}{100 * (factor - 1.0):.0f}%"
|
461
505
|
self.label_power.SetLabel(msg)
|
462
506
|
|
463
507
|
def on_optimize(self, event):
|
@@ -478,6 +522,14 @@ class LaserPanel(wx.Panel):
|
|
478
522
|
if self.checkbox_optimize.GetValue() != newvalue:
|
479
523
|
self.checkbox_optimize.SetValue(newvalue)
|
480
524
|
|
525
|
+
@signal_listener("pwm_mode_changed")
|
526
|
+
def on_pwm_mode_changed(self, origin, *message):
|
527
|
+
"""
|
528
|
+
This is called when the power scale of the device changes.
|
529
|
+
It updates the slider and label accordingly.
|
530
|
+
"""
|
531
|
+
self.update_override_controls()
|
532
|
+
|
481
533
|
@signal_listener("device;modified")
|
482
534
|
@signal_listener("device;renamed")
|
483
535
|
@lookup_listener("service/device/active")
|
@@ -655,13 +707,12 @@ class LaserPanel(wx.Panel):
|
|
655
707
|
self.context.setting(bool, "laserpane_hold", False)
|
656
708
|
if plan.plan and self.context.laserpane_hold:
|
657
709
|
self.context("planz spool\n")
|
710
|
+
elif self.checkbox_optimize.GetValue():
|
711
|
+
self.context(
|
712
|
+
"planz clear copy preprocess validate blob preopt optimize spool\n"
|
713
|
+
)
|
658
714
|
else:
|
659
|
-
|
660
|
-
self.context(
|
661
|
-
"planz clear copy preprocess validate blob preopt optimize spool\n"
|
662
|
-
)
|
663
|
-
else:
|
664
|
-
self.context("planz clear copy preprocess validate blob spool\n")
|
715
|
+
self.context("planz clear copy preprocess validate blob spool\n")
|
665
716
|
self.armed = False
|
666
717
|
self.check_laser_arm()
|
667
718
|
if self.context.auto_spooler:
|
@@ -807,10 +858,7 @@ class JobPanel(wx.Panel):
|
|
807
858
|
|
808
859
|
if not pathname.lower().endswith(f".{extension}"):
|
809
860
|
pathname += f".{extension}"
|
810
|
-
if self.context.planner.do_optimization
|
811
|
-
optpart = "preopt optimize "
|
812
|
-
else:
|
813
|
-
optpart = ""
|
861
|
+
optpart = "preopt optimize " if self.context.planner.do_optimization else ""
|
814
862
|
self.context(
|
815
863
|
f'planz clear copy preprocess validate blob {optpart}save_job "{pathname}"\n'
|
816
864
|
)
|
meerk40t/gui/materialtest.py
CHANGED
@@ -947,6 +947,11 @@ class TemplatePanel(wx.Panel):
|
|
947
947
|
standard_items = False
|
948
948
|
choices = self.parameters[idx][7]
|
949
949
|
self.list_options_1.Set(choices)
|
950
|
+
self.list_options_1.SetToolTip(
|
951
|
+
_("Select the values you want to test for {param}").format(
|
952
|
+
param=self.parameters[idx][2]
|
953
|
+
)
|
954
|
+
)
|
950
955
|
checked_strings = [
|
951
956
|
s for s in self.context.template_list1.split("|") if s
|
952
957
|
]
|
@@ -981,6 +986,11 @@ class TemplatePanel(wx.Panel):
|
|
981
986
|
standard_items = False
|
982
987
|
choices = self.parameters[idx][7]
|
983
988
|
self.list_options_2.Set(choices)
|
989
|
+
self.list_options_2.SetToolTip(
|
990
|
+
_("Select the values you want to test for {param}").format(
|
991
|
+
param=self.parameters[idx][2]
|
992
|
+
)
|
993
|
+
)
|
984
994
|
checked_strings = [
|
985
995
|
s for s in self.context.template_list2.split("|") if s
|
986
996
|
]
|
meerk40t/gui/mkdebug.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
This module contains panels that display internal developer information.
|
3
|
+
They will become visible if you type 'set debug_mode True' in the
|
4
|
+
console and restart the program.
|
5
5
|
"""
|
6
6
|
|
7
7
|
import time
|
@@ -10,6 +10,17 @@ import wx
|
|
10
10
|
from wx import aui
|
11
11
|
|
12
12
|
import meerk40t.gui.icons as mkicons
|
13
|
+
from meerk40t.constants import (
|
14
|
+
RASTER_B2T,
|
15
|
+
RASTER_CROSSOVER,
|
16
|
+
RASTER_GREEDY_H,
|
17
|
+
RASTER_GREEDY_V,
|
18
|
+
RASTER_HATCH,
|
19
|
+
RASTER_L2R,
|
20
|
+
RASTER_R2L,
|
21
|
+
RASTER_SPIRAL,
|
22
|
+
RASTER_T2B,
|
23
|
+
)
|
13
24
|
from meerk40t.core.units import Angle, Length
|
14
25
|
from meerk40t.gui.wxutils import (
|
15
26
|
ScrolledPanel,
|
@@ -123,6 +134,25 @@ def register_panel_window(window, context):
|
|
123
134
|
context.register("pane/debug_window", pane)
|
124
135
|
|
125
136
|
|
137
|
+
def register_panel_plotter(window, context):
|
138
|
+
pane = (
|
139
|
+
aui.AuiPaneInfo()
|
140
|
+
.Float()
|
141
|
+
.MinSize(225, 110)
|
142
|
+
.FloatingSize(400, 400)
|
143
|
+
.Caption(_("Plotter Test"))
|
144
|
+
.CaptionVisible(not context.pane_lock)
|
145
|
+
.Name("debug_plotter")
|
146
|
+
.Hide()
|
147
|
+
)
|
148
|
+
pane.dock_proportion = 225
|
149
|
+
pane.control = DebugRasterPlotterPanel(window, wx.ID_ANY, context=context)
|
150
|
+
pane.submenu = "_ZZ_" + _("Debug")
|
151
|
+
pane.helptext = _("Raster plotter test")
|
152
|
+
window.on_pane_create(pane)
|
153
|
+
context.register("pane/debug_plotter", pane)
|
154
|
+
|
155
|
+
|
126
156
|
class ShutdownPanel(wx.Panel):
|
127
157
|
"""
|
128
158
|
Tries to create a scenario that has led to multiple runtime errors durign shutdown
|
@@ -339,7 +369,7 @@ class DebugColorPanel(ScrolledPanel):
|
|
339
369
|
sizer_main.Add(sizer_line, 0, wx.EXPAND, 0)
|
340
370
|
count = 0
|
341
371
|
|
342
|
-
col:wx.Colour = wx.SystemSettings().GetColour(getattr(wx, prop))
|
372
|
+
col: wx.Colour = wx.SystemSettings().GetColour(getattr(wx, prop))
|
343
373
|
infosizer = wx.BoxSizer(wx.VERTICAL)
|
344
374
|
box = wxStaticBitmap(
|
345
375
|
self, wx.ID_ANY, size=wx.Size(32, 32), style=wx.SB_RAISED
|
@@ -518,8 +548,13 @@ class DebugIconPanel(wx.Panel):
|
|
518
548
|
if obj is not None:
|
519
549
|
if isinstance(obj, (mkicons.VectorIcon, mkicons.PyEmbeddedImage)):
|
520
550
|
imgs = self.icon_show.Size
|
521
|
-
ms =
|
522
|
-
|
551
|
+
ms = (
|
552
|
+
min(imgs[0], imgs[1])
|
553
|
+
* self.context.root.bitmap_correction_scale
|
554
|
+
)
|
555
|
+
bmp = obj.GetBitmap(
|
556
|
+
resize=ms, force_darkmode=self.context.themes.dark
|
557
|
+
)
|
523
558
|
self.icon_show.SetBitmap(bmp)
|
524
559
|
|
525
560
|
def pane_show(self, *args):
|
@@ -578,11 +613,19 @@ class DebugWindowPanel(wx.Panel):
|
|
578
613
|
toggle_left = wx.ToggleButton(self, wx.ID_ANY, "Toggle")
|
579
614
|
radio_left = wx.RadioBox(self, wx.ID_ANY, choices=("Yes", "No", "Maybe"))
|
580
615
|
btn_bmap_left = wx.BitmapButton(
|
581
|
-
self,
|
616
|
+
self,
|
617
|
+
wx.ID_ANY,
|
618
|
+
mkicons.icon_bell.GetBitmap(
|
619
|
+
resize=mkicons.get_default_icon_size(self.context) / 2
|
620
|
+
),
|
582
621
|
)
|
583
622
|
slider_left = wx.Slider(self, wx.ID_ANY, value=0, minValue=0, maxValue=100)
|
584
623
|
static_left = wx.StaticBitmap(
|
585
|
-
self,
|
624
|
+
self,
|
625
|
+
wx.ID_ANY,
|
626
|
+
mkicons.icon_closed_door.GetBitmap(
|
627
|
+
resize=mkicons.get_default_icon_size(self.context)
|
628
|
+
),
|
586
629
|
)
|
587
630
|
left_side.Add(cb_left, 0, 0, 0)
|
588
631
|
left_side.Add(text_left, 0, 0, 0)
|
@@ -605,7 +648,11 @@ class DebugWindowPanel(wx.Panel):
|
|
605
648
|
toggle_right = wxToggleButton(self, wx.ID_ANY, "Toggle")
|
606
649
|
radio_right = wxRadioBox(self, wx.ID_ANY, choices=("Yes", "No", "Maybe"))
|
607
650
|
static_right = wxStaticBitmap(
|
608
|
-
self,
|
651
|
+
self,
|
652
|
+
wx.ID_ANY,
|
653
|
+
mkicons.icon_closed_door.GetBitmap(
|
654
|
+
resize=mkicons.get_default_icon_size(self.context)
|
655
|
+
),
|
609
656
|
)
|
610
657
|
# right_side.Add(cb_right, 0, 0, 0)
|
611
658
|
right_side.Add(text_right, 0, 0, 0)
|
@@ -644,3 +691,215 @@ class DebugWindowPanel(wx.Panel):
|
|
644
691
|
|
645
692
|
def pane_hide(self, *args):
|
646
693
|
return
|
694
|
+
|
695
|
+
|
696
|
+
class DebugRasterPlotterPanel(wx.Panel):
|
697
|
+
def __init__(self, *args, context=None, **kwds):
|
698
|
+
# begin wxGlade: PositionPanel.__init__
|
699
|
+
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
700
|
+
wx.Panel.__init__(self, *args, **kwds)
|
701
|
+
|
702
|
+
self.context = context
|
703
|
+
self.context.themes.set_window_colors(self)
|
704
|
+
|
705
|
+
sizer_main = wx.BoxSizer(wx.VERTICAL)
|
706
|
+
info = StaticBoxSizer(
|
707
|
+
self, wx.ID_ANY, _("Raster Plotter Test Info"), wx.HORIZONTAL
|
708
|
+
)
|
709
|
+
info.Add(
|
710
|
+
wxStaticText(self, wx.ID_ANY, "Dimensions:"), 0, wx.ALIGN_CENTER_VERTICAL, 0
|
711
|
+
)
|
712
|
+
self.text_x = TextCtrl(self, wx.ID_ANY, "1", check="int")
|
713
|
+
self.text_y = TextCtrl(self, wx.ID_ANY, "1", check="int")
|
714
|
+
info.Add(self.text_x, 1, wx.EXPAND, 0)
|
715
|
+
info.Add(wxStaticText(self, wx.ID_ANY, "x"), 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
716
|
+
info.Add(self.text_y, 1, wx.EXPAND, 0)
|
717
|
+
info.Add(wxStaticText(self, wx.ID_ANY, "pixel"), 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
718
|
+
# raster properties
|
719
|
+
raster_sizer = StaticBoxSizer(
|
720
|
+
self, wx.ID_ANY, _("Raster Properties"), wx.VERTICAL
|
721
|
+
)
|
722
|
+
raster_type_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
723
|
+
raster_sizer.Add(raster_type_sizer, 0, wx.EXPAND, 0)
|
724
|
+
self.raster_terms = [
|
725
|
+
(RASTER_T2B, "Top To Bottom"),
|
726
|
+
(RASTER_B2T, "Bottom To Top"),
|
727
|
+
(RASTER_R2L, "Right To Left"),
|
728
|
+
(RASTER_L2R, "Left To Right"),
|
729
|
+
(RASTER_HATCH, "Crosshatch"),
|
730
|
+
(RASTER_GREEDY_H, "Greedy horizontal"),
|
731
|
+
(RASTER_GREEDY_V, "Greedy vertical"),
|
732
|
+
(RASTER_CROSSOVER, "Crossover"),
|
733
|
+
(RASTER_SPIRAL, "Spiral"),
|
734
|
+
]
|
735
|
+
# Look for registered raster (image) preprocessors,
|
736
|
+
# these are routines that take one image as parameter
|
737
|
+
# and deliver a set of (result image, method (aka raster_direction) )
|
738
|
+
# that will be dealt with independently
|
739
|
+
# The registered datastructure is (rasterid, description, method)
|
740
|
+
self.raster_terms.extend(
|
741
|
+
(key, description)
|
742
|
+
for key, description, method in self.context.kernel.lookup_all(
|
743
|
+
"raster_preprocessor/.*"
|
744
|
+
)
|
745
|
+
)
|
746
|
+
# Add a couple of testcases
|
747
|
+
# test_methods = (
|
748
|
+
# (-1, "Test: Horizontal Rectangle"),
|
749
|
+
# (-2, "Test: Vertical Rectangle"),
|
750
|
+
# (-3, "Test: Horizontal Snake"),
|
751
|
+
# (-4, "Test: Vertical Snake"),
|
752
|
+
# (-5, "Test: Spiral"),
|
753
|
+
# )
|
754
|
+
# self.raster_terms.extend(test_methods)
|
755
|
+
|
756
|
+
self.raster_methods = [key for key, info in self.raster_terms]
|
757
|
+
|
758
|
+
self.combo_raster_direction = wxComboBox(
|
759
|
+
self,
|
760
|
+
wx.ID_ANY,
|
761
|
+
style=wx.CB_DROPDOWN | wx.CB_READONLY,
|
762
|
+
)
|
763
|
+
raster_type_sizer.Add(
|
764
|
+
wxStaticText(self, wx.ID_ANY, "Raster Direction:"),
|
765
|
+
0,
|
766
|
+
wx.ALIGN_CENTER_VERTICAL,
|
767
|
+
0,
|
768
|
+
)
|
769
|
+
self.combo_raster_direction.AppendItems(
|
770
|
+
[info for key, info in self.raster_terms]
|
771
|
+
)
|
772
|
+
raster_type_sizer.Add(
|
773
|
+
self.combo_raster_direction, 1, wx.ALIGN_CENTER_VERTICAL, 0
|
774
|
+
)
|
775
|
+
self.check_raster_bidirectional = wxCheckBox(self, wx.ID_ANY, "Bidirectional")
|
776
|
+
|
777
|
+
raster_type_sizer.Add(
|
778
|
+
self.check_raster_bidirectional, 0, wx.ALIGN_CENTER_VERTICAL, 0
|
779
|
+
)
|
780
|
+
self.combo_raster_direction.SetSelection(0)
|
781
|
+
|
782
|
+
buttons = wx.BoxSizer(wx.HORIZONTAL)
|
783
|
+
self.btn_test_onepixel_black = wxButton(self, wx.ID_ANY, "Fully Black")
|
784
|
+
self.btn_test_onepixel_white = wxButton(self, wx.ID_ANY, "Fully White")
|
785
|
+
buttons.Add(self.btn_test_onepixel_black, 1, wx.EXPAND, 0)
|
786
|
+
buttons.Add(self.btn_test_onepixel_white, 1, wx.EXPAND, 0)
|
787
|
+
self.text_result = TextCtrl(
|
788
|
+
self, wx.ID_ANY, style=wx.TE_MULTILINE | wx.TE_READONLY
|
789
|
+
)
|
790
|
+
|
791
|
+
sizer_main.Add(info, 0, wx.EXPAND, 0)
|
792
|
+
sizer_main.Add(raster_sizer, 0, wx.EXPAND, 0)
|
793
|
+
sizer_main.Add(buttons, 0, wx.EXPAND, 0)
|
794
|
+
sizer_main.Add(self.text_result, 1, wx.EXPAND, 0)
|
795
|
+
|
796
|
+
self.SetSizer(sizer_main)
|
797
|
+
sizer_main.Fit(self)
|
798
|
+
self.Layout()
|
799
|
+
|
800
|
+
self.btn_test_onepixel_black.Bind(wx.EVT_BUTTON, self.test_empty_image)
|
801
|
+
self.btn_test_onepixel_white.Bind(wx.EVT_BUTTON, self.test_fully_covered_image)
|
802
|
+
|
803
|
+
def test_empty_image(self, event):
|
804
|
+
"""
|
805
|
+
Tests the speed of rasterplotter for a fully black image.
|
806
|
+
"""
|
807
|
+
import numpy as np
|
808
|
+
from PIL import Image, ImageDraw
|
809
|
+
|
810
|
+
from meerk40t.tools.rasterplotter import RasterPlotter
|
811
|
+
|
812
|
+
try:
|
813
|
+
x = int(self.text_x.GetValue())
|
814
|
+
y = int(self.text_y.GetValue())
|
815
|
+
except ValueError:
|
816
|
+
self.text_result.SetValue("Invalid dimensions. Please enter integers.")
|
817
|
+
return
|
818
|
+
raster_string = self.combo_raster_direction.GetStringSelection()
|
819
|
+
raster_direction = -1
|
820
|
+
for key, info in self.raster_terms:
|
821
|
+
if raster_string == info:
|
822
|
+
raster_direction = key
|
823
|
+
break
|
824
|
+
if raster_direction < 0:
|
825
|
+
self.text_result.SetValue("Invalid raster direction selected.")
|
826
|
+
return
|
827
|
+
bidir = self.check_raster_bidirectional.GetValue()
|
828
|
+
|
829
|
+
# Notabene: a black pixel is a non-burnt one, so we invert the logic here
|
830
|
+
image = Image.new("RGBA", (x, y), "white")
|
831
|
+
image = image.convert("L")
|
832
|
+
plotter = RasterPlotter(image.load(), x, y, direction=raster_direction, bidirectional=bidir)
|
833
|
+
t = time.time()
|
834
|
+
i = 0
|
835
|
+
res = []
|
836
|
+
pixels = 0
|
837
|
+
last_x, last_y = plotter.initial_position_in_scene()
|
838
|
+
for x, y, on in plotter.plot():
|
839
|
+
i += 1
|
840
|
+
if on:
|
841
|
+
pixels += (abs(x - last_x) + 1) * (abs(y - last_y) + 1)
|
842
|
+
res.append(f"{i}: ({x}, {y}) {'on' if on else 'off'}")
|
843
|
+
last_x, last_y = x, y
|
844
|
+
ipos = plotter.initial_position_in_scene()
|
845
|
+
lpos = plotter.final_position_in_scene()
|
846
|
+
res.insert(
|
847
|
+
0,
|
848
|
+
f"Black found: {pixels} pixels: ranging from ({ipos[0]}, {ipos[1]}) to ({lpos[0]}, {lpos[1]})",
|
849
|
+
)
|
850
|
+
res.append(f"Time taken to finish process {time.time() - t:.3f}s\n")
|
851
|
+
self.text_result.SetValue("\n".join(res))
|
852
|
+
|
853
|
+
def test_fully_covered_image(self, event):
|
854
|
+
"""
|
855
|
+
Tests the speed of rasterplotter for a fully black image.
|
856
|
+
"""
|
857
|
+
import numpy as np
|
858
|
+
from PIL import Image, ImageDraw
|
859
|
+
|
860
|
+
from meerk40t.tools.rasterplotter import RasterPlotter
|
861
|
+
|
862
|
+
try:
|
863
|
+
x = int(self.text_x.GetValue())
|
864
|
+
y = int(self.text_y.GetValue())
|
865
|
+
except ValueError:
|
866
|
+
self.text_result.SetValue("Invalid dimensions. Please enter integers.")
|
867
|
+
return
|
868
|
+
if self.combo_raster_direction.GetSelection() < 0:
|
869
|
+
self.text_result.SetValue("Please select a raster direction.")
|
870
|
+
return
|
871
|
+
raster_string = self.combo_raster_direction.GetStringSelection()
|
872
|
+
raster_direction = -1
|
873
|
+
for key, info in self.raster_terms:
|
874
|
+
if raster_string == info:
|
875
|
+
raster_direction = key
|
876
|
+
break
|
877
|
+
if raster_direction < 0:
|
878
|
+
self.text_result.SetValue("Invalid raster direction selected.")
|
879
|
+
return
|
880
|
+
bidir = self.check_raster_bidirectional.GetValue()
|
881
|
+
|
882
|
+
# Notabene: a black pixel is a non-burnt one, so we invert the logic here
|
883
|
+
image = Image.new("RGBA", (x, y), "black")
|
884
|
+
image = image.convert("L")
|
885
|
+
|
886
|
+
plotter = RasterPlotter(image.load(), x, y, direction=raster_direction, bidirectional=bidir)
|
887
|
+
t = time.time()
|
888
|
+
i = 0
|
889
|
+
res = []
|
890
|
+
pixels = 0
|
891
|
+
ipos = plotter.initial_position_in_scene()
|
892
|
+
lpos = plotter.final_position_in_scene()
|
893
|
+
last_x, last_y = ipos
|
894
|
+
for x, y, on in plotter.plot():
|
895
|
+
i += 1
|
896
|
+
if on:
|
897
|
+
pixels += (abs(x - last_x) + 1) * (abs(y - last_y) + 1)
|
898
|
+
res.append(f"{i}: ({x}, {y}) {'on' if on else 'off'}")
|
899
|
+
last_x, last_y = x, y
|
900
|
+
res.insert(
|
901
|
+
0,
|
902
|
+
f"White found: {pixels} pixels: ranging from ({ipos[0]}, {ipos[1]}) to ({lpos[0]}, {lpos[1]})",
|
903
|
+
)
|
904
|
+
res.append(f"Time taken to finish process {time.time() - t:.3f}s\n")
|
905
|
+
self.text_result.SetValue("\n".join(res))
|
meerk40t/gui/navigationpanels.py
CHANGED
@@ -1789,8 +1789,13 @@ class PulsePanel(wx.Panel):
|
|
1789
1789
|
self.spin_pulse_duration = wx.SpinCtrl(
|
1790
1790
|
self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER, value="50", min=1, max=1000
|
1791
1791
|
)
|
1792
|
+
self.text_power = TextCtrl(
|
1793
|
+
self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER, value="1000", check="float"
|
1794
|
+
)
|
1795
|
+
self.label_power = wxStaticText(self, wx.ID_ANY, "ppi")
|
1792
1796
|
self.__set_properties()
|
1793
1797
|
self.__do_layout()
|
1798
|
+
self.update_power_controls()
|
1794
1799
|
|
1795
1800
|
self.Bind(
|
1796
1801
|
wx.EVT_BUTTON, self.on_button_navigate_pulse, self.button_navigate_pulse
|
@@ -1813,13 +1818,17 @@ class PulsePanel(wx.Panel):
|
|
1813
1818
|
|
1814
1819
|
def __do_layout(self):
|
1815
1820
|
# begin wxGlade: PulsePanel.__do_layout
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1821
|
+
self.main_sizer = StaticBoxSizer(
|
1822
|
+
self, wx.ID_ANY, _("Short Pulse:"), wx.HORIZONTAL
|
1823
|
+
)
|
1824
|
+
self.main_sizer.Add(self.button_navigate_pulse, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
1825
|
+
self.main_sizer.Add(self.spin_pulse_duration, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1819
1826
|
label_4 = wxStaticText(self, wx.ID_ANY, _(" ms"))
|
1820
|
-
|
1821
|
-
self.
|
1822
|
-
|
1827
|
+
self.main_sizer.Add(label_4, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
1828
|
+
self.main_sizer.Add(self.text_power, 1, wx.ALIGN_CENTER_VERTICAL, 0)
|
1829
|
+
self.main_sizer.Add(self.label_power, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
1830
|
+
self.SetSizer(self.main_sizer)
|
1831
|
+
self.main_sizer.Fit(self)
|
1823
1832
|
self.Layout()
|
1824
1833
|
# end wxGlade
|
1825
1834
|
|
@@ -1827,11 +1836,60 @@ class PulsePanel(wx.Panel):
|
|
1827
1836
|
self, event=None
|
1828
1837
|
): # wxGlade: Navigation.<event_handler>
|
1829
1838
|
value = self.spin_pulse_duration.GetValue()
|
1830
|
-
|
1839
|
+
powerstr = ""
|
1840
|
+
if self.text_power.IsShown():
|
1841
|
+
power = self.text_power.GetValue()
|
1842
|
+
if power:
|
1843
|
+
try:
|
1844
|
+
power = float(power)
|
1845
|
+
except ValueError:
|
1846
|
+
power = None
|
1847
|
+
if power:
|
1848
|
+
if self.context.device.setting(
|
1849
|
+
bool, "use_percent_for_power_display", False
|
1850
|
+
):
|
1851
|
+
# Convert percent to ppi
|
1852
|
+
power = power * 10
|
1853
|
+
power = max(0, min(1000, int(power)))
|
1854
|
+
self.context.device.setting(float, "last_pulse_power", 1000)
|
1855
|
+
self.context.device.last_pulse_power = power
|
1856
|
+
powerstr = f" -p {power}"
|
1857
|
+
self.context(f"pulse {value}{powerstr}\n")
|
1831
1858
|
|
1832
1859
|
def on_spin_pulse_duration(self, event=None): # wxGlade: Navigation.<event_handler>
|
1833
1860
|
self.context.navigate_pulse = float(self.spin_pulse_duration.GetValue())
|
1834
1861
|
|
1862
|
+
def pane_show(self, *args):
|
1863
|
+
# Is the current device pwm pulse capable?
|
1864
|
+
self.context.listen("activate;device", self.on_update)
|
1865
|
+
self.update_power_controls()
|
1866
|
+
|
1867
|
+
def pane_hide(self, *args):
|
1868
|
+
self.context.unlisten("activate;device", self.on_update)
|
1869
|
+
|
1870
|
+
def on_update(self, origin, *args):
|
1871
|
+
self.update_power_controls()
|
1872
|
+
|
1873
|
+
def update_power_controls(self):
|
1874
|
+
show_power = getattr(self.context.device, "supports_pwm", False)
|
1875
|
+
self.text_power.Show(show_power)
|
1876
|
+
self.label_power.Show(show_power)
|
1877
|
+
pval = self.context.device.setting(float, "last_pulse_power", 1000)
|
1878
|
+
if pval is None:
|
1879
|
+
pval = 1000
|
1880
|
+
if self.context.device.setting(bool, "use_percent_for_power_display", False):
|
1881
|
+
self.text_power.SetValue(f"{pval/10.0:.1f}")
|
1882
|
+
self.text_power.set_range(0, 100)
|
1883
|
+
self.text_power.SetToolTip(_("Set the power of the laser pulse in percent"))
|
1884
|
+
self.label_power.SetLabel("%")
|
1885
|
+
else:
|
1886
|
+
self.text_power.SetValue(f"{pval}")
|
1887
|
+
self.text_power.set_range(0, 1000)
|
1888
|
+
self.text_power.SetToolTip(_("Set the power of the laser pulse in ppi"))
|
1889
|
+
self.label_power.SetLabel("ppi")
|
1890
|
+
|
1891
|
+
self.main_sizer.Layout()
|
1892
|
+
|
1835
1893
|
|
1836
1894
|
# class SizePanel(wx.Panel):
|
1837
1895
|
# object_ratio = None
|