meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7040__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/balormk/clone_loader.py +3 -2
- meerk40t/balormk/controller.py +28 -11
- meerk40t/balormk/cylindermod.py +1 -0
- meerk40t/balormk/device.py +13 -9
- meerk40t/balormk/driver.py +9 -2
- meerk40t/balormk/galvo_commands.py +3 -1
- meerk40t/balormk/gui/gui.py +6 -0
- meerk40t/balormk/livelightjob.py +338 -321
- meerk40t/balormk/mock_connection.py +4 -3
- meerk40t/balormk/usb_connection.py +11 -2
- meerk40t/camera/camera.py +19 -14
- meerk40t/camera/gui/camerapanel.py +6 -0
- meerk40t/core/cutplan.py +109 -51
- meerk40t/core/elements/element_treeops.py +435 -140
- meerk40t/core/elements/elements.py +100 -9
- meerk40t/core/elements/shapes.py +259 -39
- meerk40t/core/elements/tree_commands.py +10 -5
- meerk40t/core/node/elem_ellipse.py +18 -8
- meerk40t/core/node/elem_image.py +51 -19
- meerk40t/core/node/elem_line.py +18 -8
- meerk40t/core/node/elem_path.py +18 -8
- meerk40t/core/node/elem_point.py +10 -4
- meerk40t/core/node/elem_polyline.py +19 -11
- meerk40t/core/node/elem_rect.py +18 -8
- meerk40t/core/node/elem_text.py +11 -5
- meerk40t/core/node/filenode.py +2 -8
- meerk40t/core/node/groupnode.py +11 -11
- meerk40t/core/node/image_processed.py +11 -5
- meerk40t/core/node/image_raster.py +11 -5
- meerk40t/core/node/node.py +64 -16
- meerk40t/core/node/refnode.py +2 -1
- meerk40t/core/svg_io.py +91 -34
- meerk40t/device/dummydevice.py +7 -1
- meerk40t/extra/vtracer.py +222 -0
- meerk40t/grbl/device.py +81 -8
- meerk40t/gui/about.py +20 -0
- meerk40t/gui/devicepanel.py +20 -16
- meerk40t/gui/gui_mixins.py +4 -0
- meerk40t/gui/icons.py +330 -253
- meerk40t/gui/laserpanel.py +8 -3
- meerk40t/gui/laserrender.py +41 -21
- meerk40t/gui/magnetoptions.py +158 -65
- meerk40t/gui/materialtest.py +229 -39
- meerk40t/gui/navigationpanels.py +229 -24
- meerk40t/gui/propertypanels/hatchproperty.py +2 -0
- meerk40t/gui/propertypanels/imageproperty.py +160 -106
- meerk40t/gui/ribbon.py +6 -1
- meerk40t/gui/scenewidgets/gridwidget.py +29 -32
- meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
- meerk40t/gui/simulation.py +75 -77
- meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/tips.py +15 -1
- meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
- meerk40t/gui/wxmmain.py +242 -114
- meerk40t/gui/wxmscene.py +107 -24
- meerk40t/gui/wxmtree.py +4 -2
- meerk40t/gui/wxutils.py +60 -15
- meerk40t/image/imagetools.py +129 -65
- meerk40t/internal_plugins.py +4 -0
- meerk40t/kernel/kernel.py +39 -18
- meerk40t/kernel/settings.py +28 -9
- meerk40t/lihuiyu/device.py +24 -12
- meerk40t/main.py +1 -1
- meerk40t/moshi/device.py +20 -6
- meerk40t/network/console_server.py +22 -6
- meerk40t/newly/device.py +10 -3
- meerk40t/newly/gui/gui.py +10 -0
- meerk40t/ruida/device.py +22 -2
- meerk40t/ruida/loader.py +6 -3
- meerk40t/tools/geomstr.py +193 -125
- meerk40t/tools/rasterplotter.py +179 -93
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/RECORD +79 -78
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7040.dist-info}/zip-safe +0 -0
meerk40t/gui/simulation.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import math
|
2
2
|
import platform
|
3
|
-
from time import perf_counter
|
3
|
+
from time import perf_counter
|
4
4
|
|
5
5
|
import wx
|
6
6
|
|
@@ -280,7 +280,6 @@ class OperationsPanel(wx.Panel):
|
|
280
280
|
def on_text_operation_param(self, event):
|
281
281
|
content = self.text_operation_param.GetValue()
|
282
282
|
idx = self.list_operations.GetFirstSelected()
|
283
|
-
flag = False
|
284
283
|
if idx < 0:
|
285
284
|
return
|
286
285
|
op = self.cutplan.plan[idx]
|
@@ -325,7 +324,7 @@ class OperationsPanel(wx.Panel):
|
|
325
324
|
content = op.command
|
326
325
|
flag = True
|
327
326
|
elif isinstance(op, GotoOperation):
|
328
|
-
content = str(op.x)
|
327
|
+
content = f"{str(op.x)},{str(op.y)}"
|
329
328
|
flag = True
|
330
329
|
elif isinstance(op, WaitOperation):
|
331
330
|
content = str(op.wait)
|
@@ -485,9 +484,7 @@ class CutcodePanel(wx.Panel):
|
|
485
484
|
|
486
485
|
self.cutcode = cutcode
|
487
486
|
self.plan_name = plan_name
|
488
|
-
self.list_cutcode = wxListBox(
|
489
|
-
self, wx.ID_ANY, choices=[], style=wx.LB_MULTIPLE
|
490
|
-
)
|
487
|
+
self.list_cutcode = wxListBox(self, wx.ID_ANY, choices=[], style=wx.LB_MULTIPLE)
|
491
488
|
self.last_selected = []
|
492
489
|
self.display_highlighted_only = False
|
493
490
|
# self.text_operation_param = TextCtrl(
|
@@ -901,15 +898,9 @@ class SimulationPanel(wx.Panel, Job):
|
|
901
898
|
|
902
899
|
self.slider_progress = wx.Slider(self, wx.ID_ANY, self.max, 0, self.max)
|
903
900
|
self.slider_progress.SetFocus()
|
904
|
-
self.text_distance_laser = TextCtrl(
|
905
|
-
|
906
|
-
)
|
907
|
-
self.text_distance_travel = TextCtrl(
|
908
|
-
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
909
|
-
)
|
910
|
-
self.text_distance_total = TextCtrl(
|
911
|
-
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
912
|
-
)
|
901
|
+
self.text_distance_laser = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
902
|
+
self.text_distance_travel = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
903
|
+
self.text_distance_total = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
913
904
|
self.text_time_laser = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
914
905
|
self.text_time_travel = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
915
906
|
self.text_time_extra = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
@@ -923,18 +914,10 @@ class SimulationPanel(wx.Panel, Job):
|
|
923
914
|
self.text_distance_total_step = TextCtrl(
|
924
915
|
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
925
916
|
)
|
926
|
-
self.text_time_laser_step = TextCtrl(
|
927
|
-
|
928
|
-
)
|
929
|
-
self.
|
930
|
-
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
931
|
-
)
|
932
|
-
self.text_time_extra_step = TextCtrl(
|
933
|
-
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
934
|
-
)
|
935
|
-
self.text_time_total_step = TextCtrl(
|
936
|
-
self, wx.ID_ANY, "", style=wx.TE_READONLY
|
937
|
-
)
|
917
|
+
self.text_time_laser_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
918
|
+
self.text_time_travel_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
919
|
+
self.text_time_extra_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
920
|
+
self.text_time_total_step = TextCtrl(self, wx.ID_ANY, "", style=wx.TE_READONLY)
|
938
921
|
self.button_play = wxButton(self, wx.ID_ANY, "")
|
939
922
|
self.button_play.SetToolTip(_("Start the simulation replay"))
|
940
923
|
self.slider_playbackspeed = wx.Slider(self, wx.ID_ANY, 180, 0, 310)
|
@@ -1257,12 +1240,16 @@ class SimulationPanel(wx.Panel, Job):
|
|
1257
1240
|
try:
|
1258
1241
|
if newvalue:
|
1259
1242
|
# Slided in ->
|
1260
|
-
self.hscene_sizer.Show(
|
1243
|
+
self.hscene_sizer.Show(
|
1244
|
+
sizer=self.voption_sizer, show=False, recursive=True
|
1245
|
+
)
|
1261
1246
|
self.voption_sizer.Layout()
|
1262
1247
|
self.btn_slide_options.SetLabel("<")
|
1263
1248
|
else:
|
1264
1249
|
# Slided out ->
|
1265
|
-
self.hscene_sizer.Show(
|
1250
|
+
self.hscene_sizer.Show(
|
1251
|
+
sizer=self.voption_sizer, show=True, recursive=True
|
1252
|
+
)
|
1266
1253
|
self.voption_sizer.Layout()
|
1267
1254
|
self.btn_slide_options.SetLabel(">")
|
1268
1255
|
self.hscene_sizer.Layout()
|
@@ -1316,7 +1303,10 @@ class SimulationPanel(wx.Panel, Job):
|
|
1316
1303
|
if self.laserspot_display:
|
1317
1304
|
spot_value = getattr(self.context.device, "laserspot", "0.3mm")
|
1318
1305
|
try:
|
1319
|
-
scale = 0.5 * (
|
1306
|
+
scale = 0.5 * (
|
1307
|
+
self.context.device.view.native_scale_x
|
1308
|
+
+ self.context.device.view.native_scale_y
|
1309
|
+
)
|
1320
1310
|
spotwidth_in_scene = float(Length(spot_value))
|
1321
1311
|
spot_width = spotwidth_in_scene / scale
|
1322
1312
|
# print (f"Scale for device: {scale}, spot in scene: {spot_value} = {spotwidth_in_scene} -> {spot_width}")
|
@@ -1560,10 +1550,7 @@ class SimulationPanel(wx.Panel, Job):
|
|
1560
1550
|
self.slider_progress.SetValue(self.max)
|
1561
1551
|
value = self.slider_playbackspeed.GetValue()
|
1562
1552
|
value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
|
1563
|
-
if self.radio_cut.GetValue()
|
1564
|
-
factor = 0.1 # steps
|
1565
|
-
else:
|
1566
|
-
factor = 1 # seconds
|
1553
|
+
factor = 0.1 if self.radio_cut.GetValue() else 1 # steps / seconds
|
1567
1554
|
self.interval = factor * 100.0 / float(value)
|
1568
1555
|
|
1569
1556
|
def _refresh_simulated_plan(self):
|
@@ -1620,12 +1607,11 @@ class SimulationPanel(wx.Panel, Job):
|
|
1620
1607
|
|
1621
1608
|
self.update_job.cancel()
|
1622
1609
|
self.context.schedule(self.update_job)
|
1623
|
-
|
1610
|
+
self.reload_statistics()
|
1624
1611
|
self._startup()
|
1625
1612
|
self.request_refresh()
|
1626
1613
|
self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
1627
1614
|
|
1628
|
-
|
1629
1615
|
@signal_listener("device;modified")
|
1630
1616
|
@signal_listener("plan")
|
1631
1617
|
def on_plan_change(self, origin, plan_name=None, status=None):
|
@@ -1693,7 +1679,7 @@ class SimulationPanel(wx.Panel, Job):
|
|
1693
1679
|
self.button_spool.Enable(False)
|
1694
1680
|
except RuntimeError:
|
1695
1681
|
# Control no longer existant
|
1696
|
-
return
|
1682
|
+
return
|
1697
1683
|
msg = self.button_spool.GetLabel()
|
1698
1684
|
self.button_spool.SetLabel(_("Calculating"))
|
1699
1685
|
for cut in self.cutcode:
|
@@ -1701,9 +1687,15 @@ class SimulationPanel(wx.Panel, Job):
|
|
1701
1687
|
if hasattr(cut, "_plotcache") and cut._plotcache is not None:
|
1702
1688
|
continue
|
1703
1689
|
if isinstance(cut, RasterCut):
|
1704
|
-
|
1690
|
+
try:
|
1691
|
+
cut._plotcache = list(cut.plot.plot())
|
1692
|
+
except MemoryError:
|
1693
|
+
cut._plotcache = None
|
1705
1694
|
elif isinstance(cut, PlotCut):
|
1706
|
-
|
1695
|
+
try:
|
1696
|
+
cut._plotcache = list(cut.plot)
|
1697
|
+
except MemoryError:
|
1698
|
+
cut._plotcache = None
|
1707
1699
|
self.context.signal("refresh_scene", self.widget_scene.name)
|
1708
1700
|
self.reload_statistics()
|
1709
1701
|
try:
|
@@ -1963,10 +1955,7 @@ class SimulationPanel(wx.Panel, Job):
|
|
1963
1955
|
|
1964
1956
|
value = self.slider_playbackspeed.GetValue()
|
1965
1957
|
value = int((10.0 ** (value // 90)) * (1.0 + float(value % 90) / 10.0))
|
1966
|
-
if self.radio_cut.GetValue()
|
1967
|
-
factor = 0.1 # steps
|
1968
|
-
else:
|
1969
|
-
factor = 1 # seconds
|
1958
|
+
factor = 0.1 if self.radio_cut.GetValue() else 1 # steps / seconds
|
1970
1959
|
self.interval = factor * 100.0 / float(value)
|
1971
1960
|
|
1972
1961
|
self.text_playback_speed.SetValue(f"{value}%")
|
@@ -1991,7 +1980,7 @@ class SimulationWidget(Widget):
|
|
1991
1980
|
self.matrix.post_cat(~scene.context.device.view.matrix)
|
1992
1981
|
self.last_msg = None
|
1993
1982
|
self.raster_as_image = True
|
1994
|
-
self.laserspot_width = None
|
1983
|
+
self.laserspot_width = None # 1 Pixel
|
1995
1984
|
|
1996
1985
|
def process_draw(self, gc: wx.GraphicsContext):
|
1997
1986
|
if self.sim.progress < 0:
|
@@ -2005,7 +1994,9 @@ class SimulationWidget(Widget):
|
|
2005
1994
|
sim_cut = self.sim.cutcode[:idx]
|
2006
1995
|
else:
|
2007
1996
|
sim_cut = self.sim.cutcode
|
2008
|
-
self.renderer.draw_cutcode(
|
1997
|
+
self.renderer.draw_cutcode(
|
1998
|
+
sim_cut, gc, 0, 0, self.raster_as_image, laserspot_width=spot_width
|
1999
|
+
)
|
2009
2000
|
if residual <= 0:
|
2010
2001
|
return
|
2011
2002
|
# We draw interpolated lines to acknowledge we are in the middle of a cut operation
|
@@ -2022,7 +2013,9 @@ class SimulationWidget(Widget):
|
|
2022
2013
|
image = cut.image
|
2023
2014
|
gc.PushState()
|
2024
2015
|
matrix = Matrix.scale(cut.step_x, cut.step_y)
|
2025
|
-
matrix.post_translate(
|
2016
|
+
matrix.post_translate(
|
2017
|
+
cut.offset_x + x, cut.offset_y + y
|
2018
|
+
) # Adjust image xy
|
2026
2019
|
gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(matrix)))
|
2027
2020
|
try:
|
2028
2021
|
cache = cut._cache
|
@@ -2043,31 +2036,23 @@ class SimulationWidget(Widget):
|
|
2043
2036
|
cut._cache_id = id(image)
|
2044
2037
|
# Set draw - constraint
|
2045
2038
|
if cut.horizontal:
|
2039
|
+
# mode = "T2B"
|
2040
|
+
clip_w = cut._cache_width
|
2041
|
+
clip_h = int(residual * cut._cache_height)
|
2046
2042
|
if cut.start_minimum_y:
|
2047
|
-
# mode = "T2B"
|
2048
|
-
clip_w = cut._cache_width
|
2049
|
-
clip_h = int(residual * cut._cache_height)
|
2050
|
-
clip_x = 0
|
2051
2043
|
clip_y = 0
|
2052
2044
|
else:
|
2053
|
-
# mode = "B2T"
|
2054
|
-
clip_w = cut._cache_width
|
2055
|
-
clip_h = int(residual * cut._cache_height)
|
2056
|
-
clip_x = 0
|
2057
2045
|
clip_y = cut._cache_height - clip_h
|
2046
|
+
clip_x = 0
|
2058
2047
|
else:
|
2048
|
+
# mode = "L2R"
|
2049
|
+
clip_w = int(residual * cut._cache_width)
|
2050
|
+
clip_h = cut._cache_height
|
2051
|
+
clip_y = 0
|
2059
2052
|
if cut.start_minimum_x:
|
2060
|
-
# mode = "L2R"
|
2061
|
-
clip_w = int(residual * cut._cache_width)
|
2062
|
-
clip_h = cut._cache_height
|
2063
2053
|
clip_x = 0
|
2064
|
-
clip_y = 0
|
2065
2054
|
else:
|
2066
|
-
# mode = "R2L"
|
2067
|
-
clip_w = int(residual * cut._cache_width)
|
2068
|
-
clip_h = cut._cache_height
|
2069
2055
|
clip_x = cut._cache_width - clip_w
|
2070
|
-
clip_y = 0
|
2071
2056
|
|
2072
2057
|
# msg = f"Mode: {mode}, Horiz: {cut.horizontal}, from left: {cut.start_on_left}, from top: {cut.start_on_top}"
|
2073
2058
|
# if msg != self.last_msg:
|
@@ -2094,8 +2079,16 @@ class SimulationWidget(Widget):
|
|
2094
2079
|
gc.PopState()
|
2095
2080
|
else:
|
2096
2081
|
# We draw the cutcode up to a certain percentage
|
2097
|
-
simcut = (self.sim.cutcode[idx],
|
2098
|
-
self.renderer.draw_cutcode(
|
2082
|
+
simcut = (self.sim.cutcode[idx],)
|
2083
|
+
self.renderer.draw_cutcode(
|
2084
|
+
simcut,
|
2085
|
+
gc,
|
2086
|
+
0,
|
2087
|
+
0,
|
2088
|
+
self.raster_as_image,
|
2089
|
+
residual=residual,
|
2090
|
+
laserspot_width=spot_width,
|
2091
|
+
)
|
2099
2092
|
|
2100
2093
|
return
|
2101
2094
|
# # We draw a rectangle covering the raster area
|
@@ -2163,9 +2156,9 @@ class SimulationTravelWidget(Widget):
|
|
2163
2156
|
self.initvars()
|
2164
2157
|
|
2165
2158
|
def initvars(self):
|
2166
|
-
self.starts =
|
2167
|
-
self.ends =
|
2168
|
-
self.pos =
|
2159
|
+
self.starts = []
|
2160
|
+
self.ends = []
|
2161
|
+
self.pos = []
|
2169
2162
|
self.starts.append(wx.Point2D(0, 0))
|
2170
2163
|
self.ends.append(wx.Point2D(0, 0))
|
2171
2164
|
prev = None
|
@@ -2202,8 +2195,8 @@ class SimulationTravelWidget(Widget):
|
|
2202
2195
|
self.ends.append(wx.Point2D(m1.real, m1.imag))
|
2203
2196
|
else:
|
2204
2197
|
end = wx.Point2D(*curr.start)
|
2205
|
-
self.starts =
|
2206
|
-
self.ends =
|
2198
|
+
self.starts = []
|
2199
|
+
self.ends = []
|
2207
2200
|
self.starts.append(wx.Point2D(0, 0))
|
2208
2201
|
self.ends.append(end)
|
2209
2202
|
self.pos.append(len(self.starts))
|
@@ -2297,17 +2290,20 @@ class SimReticleWidget(Widget):
|
|
2297
2290
|
y = 0
|
2298
2291
|
if (
|
2299
2292
|
# self.sim.progress > 0 and
|
2300
|
-
self.sim.cutcode is not None
|
2301
|
-
and len(self.sim.cutcode)
|
2293
|
+
self.sim.cutcode is not None and len(self.sim.cutcode)
|
2302
2294
|
):
|
2303
2295
|
idx, residual = self.sim.progress_to_idx(self.sim.progress)
|
2296
|
+
if idx >= len(self.sim.cutcode):
|
2297
|
+
idx = len(self.sim.cutcode) - 1
|
2298
|
+
self.sim.progress = 0
|
2304
2299
|
dx = 0
|
2305
2300
|
dy = 0
|
2306
2301
|
if self.sim.progress != self.sim.max:
|
2307
|
-
|
2308
|
-
|
2309
|
-
|
2310
|
-
|
2302
|
+
pos = (
|
2303
|
+
self.sim.cutcode[idx - 1].end
|
2304
|
+
if idx > 0
|
2305
|
+
else self.sim.cutcode[idx].start
|
2306
|
+
)
|
2311
2307
|
if residual > 0:
|
2312
2308
|
# We could still be traversing or already burning...
|
2313
2309
|
# We have two time stamps one after travel,
|
@@ -2399,7 +2395,7 @@ class Simulation(MWindow):
|
|
2399
2395
|
busy.start()
|
2400
2396
|
|
2401
2397
|
kernel.console(
|
2402
|
-
f"planz copy preprocess validate blob{optpart}\nwindow toggle Simulation z\n"
|
2398
|
+
f"planz clear copy preprocess validate blob{optpart}\nwindow toggle Simulation z\n"
|
2403
2399
|
)
|
2404
2400
|
busy.end()
|
2405
2401
|
|
@@ -2415,7 +2411,9 @@ class Simulation(MWindow):
|
|
2415
2411
|
{
|
2416
2412
|
"label": _("Simulate"),
|
2417
2413
|
"icon": icons8_laser_beam_hazard,
|
2418
|
-
"tip": _("Simulate the current laser job")
|
2414
|
+
"tip": _("Simulate the current laser job")
|
2415
|
+
+ "\n"
|
2416
|
+
+ _("(Right click: no optimisation)"),
|
2419
2417
|
"action": open_simulator,
|
2420
2418
|
"action_right": open_simulator_simple,
|
2421
2419
|
"rule_enabled": lambda cond: kernel.elements.have_burnable_elements(),
|
@@ -6,7 +6,8 @@ from meerk40t.core.node.op_image import ImageOpNode
|
|
6
6
|
from meerk40t.core.node.op_raster import RasterOpNode
|
7
7
|
from meerk40t.gui.icons import EmptyIcon, icon_library
|
8
8
|
from meerk40t.gui.laserrender import swizzlecolor
|
9
|
-
from meerk40t.gui.wxutils import
|
9
|
+
from meerk40t.gui.wxutils import dip_size, wxStaticBitmap
|
10
|
+
|
10
11
|
from .statusbarwidget import StatusBarWidget
|
11
12
|
|
12
13
|
_ = wx.GetTranslation
|
@@ -28,24 +29,40 @@ class DefaultOperationWidget(StatusBarWidget):
|
|
28
29
|
self.first_to_show = 0
|
29
30
|
|
30
31
|
def node_label(self, node):
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
percent = ""
|
33
|
+
if hasattr(node, "power"):
|
34
|
+
if getattr(self.context.device, "use_percent_for_power_display", False):
|
35
|
+
percent = f"{node.power / 10.0:.1f}%"
|
36
|
+
else:
|
37
|
+
percent = f"{node.power:.0f}ppi"
|
38
|
+
speed = ""
|
39
|
+
if hasattr(node, "speed"):
|
40
|
+
if getattr(self.context.device, "use_mm_min_for_speed_display", False):
|
41
|
+
speed = f"{node.speed * 60:.0f}mm/min"
|
42
|
+
else:
|
43
|
+
speed = f"{node.speed:.0f}mm/s"
|
44
|
+
|
45
|
+
op_map = {
|
46
|
+
CutOpNode: _("Cut ({percent}, {speed})"),
|
47
|
+
EngraveOpNode: _("Engrave ({percent}, {speed})"),
|
48
|
+
RasterOpNode: _("Raster ({percent}, {speed})"),
|
49
|
+
ImageOpNode: _("Image ({percent}, {speed})"),
|
50
|
+
}
|
51
|
+
|
52
|
+
for op_type, template in op_map.items():
|
53
|
+
if isinstance(node, op_type):
|
54
|
+
slabel = template.format(percent=percent, speed=speed)
|
55
|
+
break
|
39
56
|
else:
|
40
57
|
slabel = ""
|
41
|
-
|
58
|
+
|
59
|
+
return (
|
42
60
|
_("Assign the selection to:")
|
43
61
|
+ "\n"
|
44
62
|
+ slabel
|
45
63
|
+ "\n"
|
46
64
|
+ _("Right click for options")
|
47
65
|
)
|
48
|
-
return slabel
|
49
66
|
|
50
67
|
def GenerateControls(self, parent, panelidx, identifier, context):
|
51
68
|
def size_it(ctrl, dimen_x, dimen_y):
|
@@ -337,13 +354,52 @@ class DefaultOperationWidget(StatusBarWidget):
|
|
337
354
|
|
338
355
|
menu.Destroy()
|
339
356
|
|
357
|
+
def _should_activate(self, idx, x, gap, residual):
|
358
|
+
# If no assigned operation, always inactive.
|
359
|
+
if self.assign_operations[idx] is None or idx < self.first_to_show:
|
360
|
+
return False, x, residual
|
361
|
+
|
362
|
+
btn_width = self.buttonsize_x
|
363
|
+
# When this is the last button:
|
364
|
+
if idx == len(self.assign_buttons) - 1 and not residual:
|
365
|
+
if x + btn_width > self.width:
|
366
|
+
return False, x, residual
|
367
|
+
else:
|
368
|
+
return True, x + gap + btn_width, False
|
369
|
+
# For non-last buttons:
|
370
|
+
if x + 2 * btn_width + gap > self.width:
|
371
|
+
return False, x, True
|
372
|
+
return True, x + gap + btn_width, residual
|
373
|
+
|
374
|
+
def _update_button_states(self, x, residual, gap):
|
375
|
+
"""
|
376
|
+
Loops through all buttons (assign_buttons) and decides whether to activate or deactivate each one.
|
377
|
+
Conditions for each button:
|
378
|
+
|
379
|
+
- If the corresponding operation (self.assign_operations[idx]) is None, deactivate the button.
|
380
|
+
- Otherwise:
|
381
|
+
- Check if the button fits within the available width (self.width).
|
382
|
+
- If it doesn't fit, mark it as "residual" (indicating more buttons exist off-screen) and deactivate it.
|
383
|
+
- If it fits, activate it and update the horizontal position
|
384
|
+
"""
|
385
|
+
for idx, btn in enumerate(self.assign_buttons):
|
386
|
+
active, x, residual = self._should_activate(idx, x, gap, residual)
|
387
|
+
self.SetActive(btn, active)
|
388
|
+
return residual
|
389
|
+
|
340
390
|
def Show(self, showit=True):
|
341
|
-
|
391
|
+
"""
|
392
|
+
Controls the visibility and layout of the operation assignment buttons in the status bar.
|
393
|
+
Updates which buttons are active and visible based on the current page and available width.
|
394
|
+
|
395
|
+
Args:
|
396
|
+
showit (bool): Whether to show the operation assignment controls.
|
397
|
+
"""
|
342
398
|
if showit:
|
399
|
+
# Determine how many buttons can fit horizontally on the screen
|
343
400
|
self.page_size = int(
|
344
401
|
(self.width - 2 * self.buttonsize_x) / self.buttonsize_x
|
345
402
|
)
|
346
|
-
# print(f"Page-Size: {self.page_size}, width={self.width}")
|
347
403
|
x = 0
|
348
404
|
gap = 0
|
349
405
|
if self.first_to_show > 0:
|
@@ -351,36 +407,10 @@ class DefaultOperationWidget(StatusBarWidget):
|
|
351
407
|
x = self.buttonsize_x + gap
|
352
408
|
else:
|
353
409
|
self.SetActive(self.btn_prev, False)
|
354
|
-
|
355
410
|
residual = False
|
356
|
-
|
357
|
-
w = self.buttonsize_x
|
358
|
-
btnflag = False
|
359
|
-
if not residual:
|
360
|
-
if self.assign_operations[idx] is None:
|
361
|
-
self.SetActive(btn, False)
|
362
|
-
else:
|
363
|
-
if idx < self.first_to_show:
|
364
|
-
btnflag = False
|
365
|
-
elif idx == len(self.assign_buttons) - 1:
|
366
|
-
# The last
|
367
|
-
if x + w > self.width:
|
368
|
-
residual = True
|
369
|
-
btnflag = False
|
370
|
-
else:
|
371
|
-
btnflag = True
|
372
|
-
x += gap + w
|
373
|
-
else:
|
374
|
-
if x + w + gap + self.buttonsize_x > self.width:
|
375
|
-
residual = True
|
376
|
-
btnflag = False
|
377
|
-
else:
|
378
|
-
btnflag = True
|
379
|
-
x += gap + w
|
380
|
-
self.SetActive(btn, btnflag)
|
411
|
+
residual = self._update_button_states(x, residual, gap)
|
381
412
|
self.SetActive(self.btn_next, residual)
|
382
413
|
self.SetActive(self.btn_matman, not residual)
|
383
|
-
|
384
414
|
else:
|
385
415
|
self.SetActive(self.btn_prev, False)
|
386
416
|
for btn in self.assign_buttons:
|
@@ -392,16 +422,19 @@ class DefaultOperationWidget(StatusBarWidget):
|
|
392
422
|
|
393
423
|
def on_prev(self, event):
|
394
424
|
self.first_to_show -= self.page_size
|
395
|
-
|
396
|
-
self.first_to_show = 0
|
425
|
+
self.first_to_show = max(self.first_to_show, 0)
|
397
426
|
self.Show(True)
|
398
427
|
|
399
428
|
def on_next(self, event):
|
400
|
-
|
429
|
+
if (
|
430
|
+
self.first_to_show == 0
|
431
|
+
): # We did not have a previous button, so we displayed one more than normal
|
432
|
+
self.first_to_show += self.page_size + 1
|
433
|
+
else:
|
434
|
+
self.first_to_show += self.page_size
|
401
435
|
if self.first_to_show + self.page_size >= len(self.assign_buttons):
|
402
436
|
self.first_to_show = len(self.assign_buttons) - self.page_size
|
403
|
-
|
404
|
-
self.first_to_show = 0
|
437
|
+
self.first_to_show = max(self.first_to_show, 0)
|
405
438
|
self.Show(True)
|
406
439
|
|
407
440
|
def execute_on(self, targetop, use_parent):
|
@@ -409,9 +442,12 @@ class DefaultOperationWidget(StatusBarWidget):
|
|
409
442
|
data = list(self.context.elements.elems(emphasized=True))
|
410
443
|
for node in data:
|
411
444
|
add_node = node
|
412
|
-
if
|
413
|
-
|
414
|
-
|
445
|
+
if (
|
446
|
+
use_parent
|
447
|
+
and node.parent is not None
|
448
|
+
and node.parent.type.startswith("effect")
|
449
|
+
):
|
450
|
+
add_node = node.parent
|
415
451
|
if add_node not in targetdata:
|
416
452
|
targetdata.append(add_node)
|
417
453
|
|
@@ -61,7 +61,7 @@ class SimpleInfoWidget(StatusBarWidget):
|
|
61
61
|
self.btn_next.Bind(wx.EVT_LEFT_DOWN, self.on_button_next)
|
62
62
|
self.btn_next.Bind(wx.EVT_RIGHT_DOWN, self.on_button_prev)
|
63
63
|
|
64
|
-
self.Add(self.info_text, 5,
|
64
|
+
self.Add(self.info_text, 5, 0, 0)
|
65
65
|
self.Add(self.progress_bar, 1, wx.EXPAND, 0)
|
66
66
|
self.Add(self.btn_next, 0, wx.EXPAND, 0)
|
67
67
|
self.SetActive(self.btn_next, False)
|
@@ -201,7 +201,7 @@ class InformationWidget(SimpleInfoWidget):
|
|
201
201
|
while new_height > 1024 or new_width > 1024:
|
202
202
|
new_height //= 2
|
203
203
|
new_width //= 2
|
204
|
-
|
204
|
+
|
205
205
|
all_pixel = new_height * new_width
|
206
206
|
if all_pixel > 0:
|
207
207
|
try:
|
meerk40t/gui/tips.py
CHANGED
@@ -22,7 +22,14 @@ from .icons import (
|
|
22
22
|
icons8_manager,
|
23
23
|
)
|
24
24
|
from .mwindow import MWindow
|
25
|
-
from .wxutils import
|
25
|
+
from .wxutils import (
|
26
|
+
TextCtrl,
|
27
|
+
dip_size,
|
28
|
+
wxButton,
|
29
|
+
wxCheckBox,
|
30
|
+
wxStaticBitmap,
|
31
|
+
wxStaticText,
|
32
|
+
)
|
26
33
|
|
27
34
|
_ = wx.GetTranslation
|
28
35
|
|
@@ -98,6 +105,9 @@ class TipPanel(wx.Panel):
|
|
98
105
|
sizer_main.Add(button_sizer, 0, wx.EXPAND, 0)
|
99
106
|
|
100
107
|
self.check_startup = wxCheckBox(self, wx.ID_ANY, _("Show tips at startup"))
|
108
|
+
self.check_startup.SetFont(
|
109
|
+
wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
|
110
|
+
)
|
101
111
|
self.check_startup.SetToolTip(
|
102
112
|
_(
|
103
113
|
"Show tips at program start.\n"
|
@@ -234,6 +244,10 @@ class TipPanel(wx.Panel):
|
|
234
244
|
self.image_tip.Show(True)
|
235
245
|
return True
|
236
246
|
|
247
|
+
img_size = self.image_tip.GetSize()
|
248
|
+
if img_size[0] == 0 or img_size[1] == 0:
|
249
|
+
# Invalid display area
|
250
|
+
return False
|
237
251
|
# self.image_tip.SetBitmap(wx.NullBitmap)
|
238
252
|
self.image_tip.Show(False)
|
239
253
|
self.tip_image = path
|
@@ -147,6 +147,7 @@ class PointMoveTool(ToolWidget):
|
|
147
147
|
|
148
148
|
def reset(self):
|
149
149
|
self.points.clear()
|
150
|
+
self.point_index.clear()
|
150
151
|
|
151
152
|
def done(self):
|
152
153
|
self.scene.pane.tool_active = False
|
@@ -156,7 +157,8 @@ class PointMoveTool(ToolWidget):
|
|
156
157
|
self.scene.context("tool none\n")
|
157
158
|
|
158
159
|
def tool_change(self):
|
159
|
-
self.
|
160
|
+
self.reset()
|
161
|
+
|
160
162
|
for node in self.scene.context.elements.flat(emphasized=True):
|
161
163
|
if not hasattr(node, "as_geometry"):
|
162
164
|
continue
|