meerk40t 0.9.7050__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 +12 -2
- 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.7050.dist-info → meerk40t-0.9.7900.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/RECORD +68 -67
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7050.dist-info → meerk40t-0.9.7900.dist-info}/zip-safe +0 -0
meerk40t/balormk/controller.py
CHANGED
@@ -288,7 +288,7 @@ class GalvoController:
|
|
288
288
|
self._list_executing = False
|
289
289
|
self._number_of_list_packets = 0
|
290
290
|
self.paused = False
|
291
|
-
self.
|
291
|
+
self.service.setting(bool, "signal_updates", True)
|
292
292
|
|
293
293
|
def define_pins(self):
|
294
294
|
self._light_bit = self.service.setting(int, "light_pin", 8)
|
@@ -1090,7 +1090,7 @@ class GalvoController:
|
|
1090
1090
|
x = int(x)
|
1091
1091
|
y = int(y)
|
1092
1092
|
self._list_write(listJumpTo, x, y, angle, distance)
|
1093
|
-
if self.
|
1093
|
+
if self.service.signal_updates:
|
1094
1094
|
view = self.service.view
|
1095
1095
|
l_x, l_y = view.iposition(self._last_x, self._last_y)
|
1096
1096
|
n_x, n_y = view.iposition(x, y)
|
@@ -1125,7 +1125,7 @@ class GalvoController:
|
|
1125
1125
|
y = int(y)
|
1126
1126
|
self._list_write(listMarkTo, x, y, angle, distance)
|
1127
1127
|
|
1128
|
-
if self.
|
1128
|
+
if self.service.signal_updates:
|
1129
1129
|
view = self.service.view
|
1130
1130
|
l_x, l_y = view.iposition(self._last_x, self._last_y)
|
1131
1131
|
n_x, n_y = view.iposition(x, y)
|
meerk40t/balormk/device.py
CHANGED
@@ -893,6 +893,13 @@ class BalorDevice(Service, Status):
|
|
893
893
|
name = self.label.replace(" ", "-")
|
894
894
|
return name.replace("/", "-")
|
895
895
|
|
896
|
+
@property
|
897
|
+
def supports_pwm(self):
|
898
|
+
"""
|
899
|
+
Returns whether this device supports PWM.
|
900
|
+
"""
|
901
|
+
return True
|
902
|
+
|
896
903
|
def service_attach(self, *args, **kwargs):
|
897
904
|
self.realize()
|
898
905
|
|
meerk40t/balormk/driver.py
CHANGED
@@ -57,6 +57,7 @@ class BalorDriver:
|
|
57
57
|
self.plot_planner.settings_then_jog = True
|
58
58
|
self._aborting = False
|
59
59
|
self._list_bits = None
|
60
|
+
self.service.setting(bool, "signal_updates", True)
|
60
61
|
|
61
62
|
def __repr__(self):
|
62
63
|
return f"BalorDriver({self.name})"
|
@@ -552,11 +553,12 @@ class BalorDriver:
|
|
552
553
|
if self.native_y < 0:
|
553
554
|
self.native_y = 0
|
554
555
|
self.connection.set_xy(self.native_x, self.native_y)
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
556
|
+
if self.service.signal_updates:
|
557
|
+
new_current = self.service.current
|
558
|
+
self.service.signal(
|
559
|
+
"driver;position",
|
560
|
+
(old_current[0], old_current[1], new_current[0], new_current[1]),
|
561
|
+
)
|
560
562
|
|
561
563
|
def move_rel(self, dx, dy):
|
562
564
|
"""
|
@@ -581,11 +583,12 @@ class BalorDriver:
|
|
581
583
|
if self.native_y < 0:
|
582
584
|
self.native_y = 0
|
583
585
|
self.connection.set_xy(self.native_x, self.native_y)
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
586
|
+
if self.service.signal_updates:
|
587
|
+
new_current = self.service.current
|
588
|
+
self.service.signal(
|
589
|
+
"driver;position",
|
590
|
+
(old_current[0], old_current[1], new_current[0], new_current[1]),
|
591
|
+
)
|
589
592
|
|
590
593
|
def home(self):
|
591
594
|
"""
|
@@ -718,7 +721,7 @@ class BalorDriver:
|
|
718
721
|
self.connection.abort()
|
719
722
|
self.service.signal("pause")
|
720
723
|
|
721
|
-
def dwell(self, time_in_ms):
|
724
|
+
def dwell(self, time_in_ms, settings=None):
|
722
725
|
"""
|
723
726
|
Requests that the laser fire in place for the given time period. This could be done in a series of commands,
|
724
727
|
move to a location, turn laser on, wait, turn laser off. However, some drivers have specific laser-in-place
|
@@ -727,14 +730,20 @@ class BalorDriver:
|
|
727
730
|
@param time_in_ms:
|
728
731
|
@return:
|
729
732
|
"""
|
730
|
-
|
733
|
+
if settings is not None and "power" in settings:
|
734
|
+
power = settings.get("power")
|
735
|
+
else:
|
736
|
+
power = None
|
737
|
+
self.pulse(time_in_ms, power=power)
|
731
738
|
|
732
|
-
def pulse(self, pulse_time):
|
739
|
+
def pulse(self, pulse_time, power=None):
|
733
740
|
self.service.laser_status = "active"
|
734
741
|
con = self.connection
|
735
742
|
con.program_mode()
|
736
743
|
con.frequency(self.service.default_frequency)
|
737
|
-
|
744
|
+
if power is None:
|
745
|
+
power = self.service.default_power
|
746
|
+
con.power(power)
|
738
747
|
if self.service.pulse_width_enabled:
|
739
748
|
con.list_fiber_ylpm_pulse_width(self.service.default_pulse_width)
|
740
749
|
dwell_time = pulse_time * 100 # Dwell time in ms units in 10 us
|
@@ -299,12 +299,15 @@ def plugin(service, lifecycle):
|
|
299
299
|
action="store_true",
|
300
300
|
help=_("override one second laser fire pulse duration"),
|
301
301
|
)
|
302
|
+
@service.console_option("power", "p", type=str, help=_("Power level"))
|
302
303
|
@service.console_argument("time", type=float, help=_("laser fire pulse duration"))
|
303
304
|
@service.console_command(
|
304
305
|
"pulse",
|
305
306
|
help=_("pulse <time>: Pulse the laser in place."),
|
306
307
|
)
|
307
|
-
def pulse(
|
308
|
+
def pulse(
|
309
|
+
command, channel, _, time=None, power=None, idonotlovemyhouse=False, **kwargs
|
310
|
+
):
|
308
311
|
if time is None:
|
309
312
|
channel(_("Must specify a pulse time in milliseconds."))
|
310
313
|
return
|
@@ -319,9 +322,21 @@ def plugin(service, lifecycle):
|
|
319
322
|
return
|
320
323
|
except IndexError:
|
321
324
|
return
|
325
|
+
if power:
|
326
|
+
try:
|
327
|
+
if power.endswith("%"):
|
328
|
+
power = float(power[:-1]) * 10
|
329
|
+
else:
|
330
|
+
power = float(power)
|
331
|
+
except ValueError:
|
332
|
+
channel(_("Invalid power value: {power}").format(power=power))
|
333
|
+
return
|
322
334
|
if service.spooler.is_idle:
|
323
|
-
service.spooler.command("pulse", time)
|
324
|
-
channel(
|
335
|
+
service.spooler.command("pulse", time, power)
|
336
|
+
channel(
|
337
|
+
_("Pulse laser for {time} milliseconds").format(time=time)
|
338
|
+
+ f"[{power}]"
|
339
|
+
)
|
325
340
|
else:
|
326
341
|
channel(_("Pulse laser failed: Busy"))
|
327
342
|
return
|
@@ -235,3 +235,9 @@ class BalorConfiguration(MWindow):
|
|
235
235
|
@staticmethod
|
236
236
|
def helptext():
|
237
237
|
return _("Display and edit device configuration")
|
238
|
+
|
239
|
+
@signal_listener("activate;device")
|
240
|
+
def on_device_changes(self, *args):
|
241
|
+
# Device activated, make sure we are still fine...
|
242
|
+
if self.context.device.name != 'balor':
|
243
|
+
wx.CallAfter(self.Close)
|
meerk40t/balormk/livelightjob.py
CHANGED
@@ -41,11 +41,12 @@ class LiveLightJob:
|
|
41
41
|
self._travel_speed = travel_speed
|
42
42
|
self._jump_delay = jump_delay
|
43
43
|
|
44
|
+
self._signal_mode = True
|
44
45
|
# Kernel Job definition
|
45
46
|
self.redlight_lock = threading.RLock()
|
46
47
|
self.redlight_job = Job(
|
47
48
|
self.jobevent,
|
48
|
-
interval=0.1,
|
49
|
+
# interval=0.1,
|
49
50
|
job_name=f"redlight_{self.mode}_{time.perf_counter():3f}",
|
50
51
|
run_main=False,
|
51
52
|
)
|
@@ -177,20 +178,24 @@ class LiveLightJob:
|
|
177
178
|
con._goto_speed = self.service.redlight_speed
|
178
179
|
con.light_mode()
|
179
180
|
|
181
|
+
# print (f"Redlight job {self.label} running...")
|
180
182
|
if self.stopped or self._connection is None:
|
181
183
|
return
|
182
184
|
con = self._connection
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
self.update_method
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
185
|
+
while not self.stopped:
|
186
|
+
if self.changed:
|
187
|
+
# print ("Something changed")
|
188
|
+
with self.redlight_lock:
|
189
|
+
if self.update_method is not None:
|
190
|
+
self.update_method()
|
191
|
+
# print (f"We are having now {len(self.points)} points")
|
192
|
+
# for i, e in enumerate(self.points):
|
193
|
+
# print (f"Point {i}: {e}")
|
194
|
+
self.changed = False
|
195
|
+
init_red(con)
|
196
|
+
|
197
|
+
# Now draw the stuff
|
198
|
+
self.trace_redlight(con)
|
194
199
|
|
195
200
|
def trace_redlight(self, con):
|
196
201
|
"""Trace the redlight path.
|
@@ -206,6 +211,9 @@ class LiveLightJob:
|
|
206
211
|
delay_dark = self.service.delay_jump_long
|
207
212
|
delay_between = self.service.delay_jump_short
|
208
213
|
move = True
|
214
|
+
# We need to jump back to the first point
|
215
|
+
first = True
|
216
|
+
first_x, first_y = None, None
|
209
217
|
for i, e in enumerate(self.points):
|
210
218
|
if self.stopped or self.changed:
|
211
219
|
# Abort due to stoppage or change, no sense to continue
|
@@ -227,11 +235,16 @@ class LiveLightJob:
|
|
227
235
|
# Fix them.
|
228
236
|
x &= 0xFFFF
|
229
237
|
y &= 0xFFFF
|
238
|
+
if first:
|
239
|
+
first_x, first_y = x, y
|
240
|
+
first = False
|
230
241
|
if move:
|
231
242
|
con.dark(x, y, long=delay_dark, short=delay_dark)
|
232
243
|
move = False
|
233
244
|
continue
|
234
245
|
con.light(x, y, long=delay_between, short=delay_between)
|
246
|
+
if first_x is not None and first_y is not None:
|
247
|
+
con.dark(first_x, first_y, long=delay_dark, short=delay_dark)
|
235
248
|
con.light_off()
|
236
249
|
con.write_port()
|
237
250
|
|
@@ -243,7 +256,13 @@ class LiveLightJob:
|
|
243
256
|
"""
|
244
257
|
if not self.listen:
|
245
258
|
return
|
246
|
-
for method in (
|
259
|
+
for method in (
|
260
|
+
"emphasized",
|
261
|
+
"modified_by_tool",
|
262
|
+
"updating",
|
263
|
+
"view;realized",
|
264
|
+
"update_group_labels",
|
265
|
+
):
|
247
266
|
if start:
|
248
267
|
self.service.listen(method, self.on_emphasis_changed)
|
249
268
|
else:
|
@@ -265,6 +284,8 @@ class LiveLightJob:
|
|
265
284
|
self._connection = driver.connection
|
266
285
|
self._connection.rapid_mode()
|
267
286
|
self._connection.light_mode()
|
287
|
+
self._signal_mode = driver.service.signal_updates
|
288
|
+
driver.service.signal_updates = False
|
268
289
|
self.update()
|
269
290
|
self.service.kernel.schedule(self.redlight_job)
|
270
291
|
|
@@ -283,6 +304,7 @@ class LiveLightJob:
|
|
283
304
|
self.redlight_job.cancel()
|
284
305
|
self.service.kernel.unschedule(self.redlight_job)
|
285
306
|
self.setup_listen(False)
|
307
|
+
driver.service.signal_updates = self._signal_mode
|
286
308
|
if self._connection is not None:
|
287
309
|
self._connection.abort()
|
288
310
|
if self.service.redlight_preferred:
|
@@ -458,7 +480,7 @@ class LiveLightJob:
|
|
458
480
|
geometry.transform(rotate)
|
459
481
|
self.points = list(
|
460
482
|
geometry.as_equal_interpolated_points(
|
461
|
-
distance=self.quantization, expand_lines=True
|
483
|
+
distance=self.quantization, # expand_lines=True
|
462
484
|
)
|
463
485
|
)
|
464
486
|
# print (f"Interpolation delivered: {len(self.points)} segments")
|
meerk40t/camera/camera.py
CHANGED
@@ -38,6 +38,7 @@ class Camera(Service):
|
|
38
38
|
self.camera_thread = None
|
39
39
|
self.max_tries_connect = 10
|
40
40
|
self.max_tries_frame = 10
|
41
|
+
self.setting(str, "desc", "")
|
41
42
|
self.setting(int, "width", 640)
|
42
43
|
self.setting(int, "height", 480)
|
43
44
|
self.setting(bool, "correction_fisheye", False)
|
@@ -19,7 +19,13 @@ from meerk40t.gui.scene.sceneconst import (
|
|
19
19
|
)
|
20
20
|
from meerk40t.gui.scene.scenepanel import ScenePanel
|
21
21
|
from meerk40t.gui.scene.widget import Widget
|
22
|
-
from meerk40t.gui.wxutils import
|
22
|
+
from meerk40t.gui.wxutils import (
|
23
|
+
TextCtrl,
|
24
|
+
wxBitmapButton,
|
25
|
+
wxButton,
|
26
|
+
wxCheckBox,
|
27
|
+
wxListCtrl,
|
28
|
+
)
|
23
29
|
from meerk40t.kernel import Job, signal_listener
|
24
30
|
from meerk40t.svgelements import Color
|
25
31
|
|
@@ -28,23 +34,32 @@ _ = wx.GetTranslation
|
|
28
34
|
CORNER_SIZE = 25
|
29
35
|
|
30
36
|
|
37
|
+
def _get_camera_attribute(kernel, camera, attribute, default=None):
|
38
|
+
if isinstance(camera, int):
|
39
|
+
camera = f"camera/{camera}"
|
40
|
+
label = kernel.read_persistent(str, camera, attribute, default)
|
41
|
+
return label or default
|
42
|
+
|
43
|
+
|
31
44
|
def register_panel_camera(window, context):
|
32
45
|
for index in range(5):
|
33
46
|
panel = CameraPanel(
|
34
47
|
window, wx.ID_ANY, context=context, gui=window, index=index, pane=True
|
35
48
|
)
|
49
|
+
label = _("Camera {index}").format(index=index)
|
36
50
|
pane = (
|
37
51
|
aui.AuiPaneInfo()
|
38
52
|
.Left()
|
39
53
|
.MinSize(200, 150)
|
40
54
|
.FloatingSize(640, 480)
|
41
|
-
.Caption(
|
55
|
+
.Caption(label)
|
42
56
|
.Name(f"camera{index}")
|
43
57
|
.CaptionVisible(not context.pane_lock)
|
44
58
|
.Hide()
|
45
59
|
)
|
46
60
|
pane.dock_proportion = 200
|
47
61
|
pane.control = panel
|
62
|
+
panel.pane_aui = pane
|
48
63
|
pane.submenu = "_60_" + _("Camera")
|
49
64
|
pane.helptext = _("Show camera capture panel")
|
50
65
|
window.on_pane_create(pane)
|
@@ -64,6 +79,7 @@ class CameraPanel(wx.Panel, Job):
|
|
64
79
|
self.index = index
|
65
80
|
self.cam_device_link = {}
|
66
81
|
self.pane = pane
|
82
|
+
self.pane_aui = None
|
67
83
|
|
68
84
|
if pane:
|
69
85
|
job_name = f"CamPane{self.index}"
|
@@ -83,12 +99,16 @@ class CameraPanel(wx.Panel, Job):
|
|
83
99
|
|
84
100
|
if not pane:
|
85
101
|
self.button_update = wxBitmapButton(
|
86
|
-
self,
|
102
|
+
self,
|
103
|
+
wx.ID_ANY,
|
104
|
+
icons8_camera.GetBitmap(resize=get_default_icon_size(self.context)),
|
87
105
|
)
|
88
106
|
self.button_export = wxBitmapButton(
|
89
107
|
self,
|
90
108
|
wx.ID_ANY,
|
91
|
-
icons8_image_in_frame.GetBitmap(
|
109
|
+
icons8_image_in_frame.GetBitmap(
|
110
|
+
resize=get_default_icon_size(self.context)
|
111
|
+
),
|
92
112
|
)
|
93
113
|
self.button_reconnect = wxBitmapButton(
|
94
114
|
self,
|
@@ -224,6 +244,29 @@ class CameraPanel(wx.Panel, Job):
|
|
224
244
|
self.camera.listen("camera;stopped", self.on_camera_stop)
|
225
245
|
self.camera.gui = self
|
226
246
|
self.camera("camera focus -5% -5% 105% 105%\n")
|
247
|
+
label = _get_camera_attribute(
|
248
|
+
self.context.kernel,
|
249
|
+
self.index,
|
250
|
+
"desc",
|
251
|
+
_("Camera {index}").format(index=self.index),
|
252
|
+
)
|
253
|
+
if self.pane and self.pane_aui is not None:
|
254
|
+
# If this is a pane, we set the title of the pane.
|
255
|
+
# print (f"Setting pane title to {label}")
|
256
|
+
self.pane_aui.Caption(label)
|
257
|
+
self.pane_aui.caption = label
|
258
|
+
else:
|
259
|
+
self.GetParent().SetTitle(label)
|
260
|
+
|
261
|
+
@property
|
262
|
+
def caption(self):
|
263
|
+
# Property to get the caption of the corresponding pane menu item
|
264
|
+
return _get_camera_attribute(
|
265
|
+
self.context.kernel,
|
266
|
+
self.index,
|
267
|
+
"desc",
|
268
|
+
_("Camera {index}").format(index=self.index),
|
269
|
+
)
|
227
270
|
|
228
271
|
def pane_hide(self, *args):
|
229
272
|
self.camera(f"camera{self.index} stop\n")
|
@@ -407,6 +450,7 @@ class CameraPanel(wx.Panel, Job):
|
|
407
450
|
self.camera(f"camera{self.index} --uri {str(uri)} stop start\n")
|
408
451
|
self.frame_bitmap = None
|
409
452
|
|
453
|
+
|
410
454
|
class CamInterfaceWidget(Widget):
|
411
455
|
def __init__(self, scene, camera):
|
412
456
|
Widget.__init__(self, scene, all=True)
|
@@ -436,6 +480,7 @@ class CamInterfaceWidget(Widget):
|
|
436
480
|
|
437
481
|
def event(self, window_pos=None, space_pos=None, event_type=None, **kwargs):
|
438
482
|
if event_type == "rightdown":
|
483
|
+
|
439
484
|
def set_resolution(width, height):
|
440
485
|
def handler(*args):
|
441
486
|
self.cam.set_resolution(this_w, this_h)
|
@@ -477,9 +522,27 @@ class CamInterfaceWidget(Widget):
|
|
477
522
|
def set_device_link(devlabel):
|
478
523
|
if devlabel:
|
479
524
|
self.cam.cam_device_link[self.cam.index] = devlabel
|
480
|
-
else:
|
525
|
+
else: # Empty or None
|
481
526
|
self.cam.cam_device_link.pop(self.cam.index, None)
|
482
527
|
|
528
|
+
def change_label(event=None):
|
529
|
+
"""
|
530
|
+
Change the label of the camera.
|
531
|
+
"""
|
532
|
+
dialog = wx.TextEntryDialog(
|
533
|
+
self.cam,
|
534
|
+
_("Enter a new label for the camera:"),
|
535
|
+
_("Change camera label"),
|
536
|
+
str(self.cam.camera.desc or ""),
|
537
|
+
)
|
538
|
+
if dialog.ShowModal() == wx.ID_OK:
|
539
|
+
new_label = dialog.GetValue().strip()
|
540
|
+
if new_label:
|
541
|
+
self.cam.context(
|
542
|
+
f'camera{self.cam.index} label "{new_label}"\n'
|
543
|
+
)
|
544
|
+
dialog.Destroy()
|
545
|
+
|
483
546
|
menu = wx.Menu()
|
484
547
|
|
485
548
|
item = menu.Append(wx.ID_ANY, _("Update Background"), "")
|
@@ -519,7 +582,9 @@ class CamInterfaceWidget(Widget):
|
|
519
582
|
def has_live_job():
|
520
583
|
we_have_a_job = False
|
521
584
|
try:
|
522
|
-
obj = self.cam.context.kernel.jobs[
|
585
|
+
obj = self.cam.context.kernel.jobs[
|
586
|
+
f"timer.updatebg{self.cam.index}"
|
587
|
+
]
|
523
588
|
if obj is not None:
|
524
589
|
we_have_a_job = True
|
525
590
|
except KeyError:
|
@@ -539,15 +604,19 @@ class CamInterfaceWidget(Widget):
|
|
539
604
|
def set_link(devlabel):
|
540
605
|
def handler(event):
|
541
606
|
set_device_link(devlabel)
|
607
|
+
|
542
608
|
return handler
|
543
609
|
|
544
610
|
def unset_link(devlabel):
|
545
611
|
def handler(event):
|
546
612
|
set_device_link("")
|
613
|
+
|
547
614
|
return handler
|
548
615
|
|
549
616
|
link = get_device_link()
|
550
|
-
item = submenu.Append(
|
617
|
+
item = submenu.Append(
|
618
|
+
wx.ID_ANY, _("Device independent"), kind=wx.ITEM_RADIO
|
619
|
+
)
|
551
620
|
if link == "":
|
552
621
|
item.Check(True)
|
553
622
|
self.cam.Bind(wx.EVT_MENU, unset_link(""), id=item.GetId())
|
@@ -587,17 +656,18 @@ class CamInterfaceWidget(Widget):
|
|
587
656
|
cam_w, cam_h = self.cam.camera.get_resolution()
|
588
657
|
resmen = wx.Menu()
|
589
658
|
for res_w, res_h, res_desc in self.cam.available_resolutions:
|
590
|
-
item = resmen.Append(
|
659
|
+
item = resmen.Append(
|
660
|
+
wx.ID_ANY, f"{res_w}x{res_h} - {res_desc}", kind=wx.ITEM_RADIO
|
661
|
+
)
|
591
662
|
if res_h == cam_h and res_w == cam_w:
|
592
663
|
item.Check(True)
|
593
664
|
self.cam.Bind(
|
594
665
|
wx.EVT_MENU,
|
595
666
|
set_resolution(res_w, res_h),
|
596
667
|
id=item.GetId(),
|
597
|
-
|
668
|
+
)
|
598
669
|
menu.AppendSubMenu(resmen, _("Set camera resolution..."))
|
599
670
|
|
600
|
-
|
601
671
|
item = menu.Append(wx.ID_ANY, _("Open CameraInterface"), "")
|
602
672
|
self.cam.Bind(
|
603
673
|
wx.EVT_MENU,
|
@@ -733,6 +803,13 @@ class CamInterfaceWidget(Widget):
|
|
733
803
|
_("Manage URIs"),
|
734
804
|
sub_menu,
|
735
805
|
)
|
806
|
+
menu.AppendSeparator()
|
807
|
+
item = menu.Append(wx.ID_ANY, _("Change Label"), "")
|
808
|
+
self.cam.Bind(
|
809
|
+
wx.EVT_MENU,
|
810
|
+
change_label,
|
811
|
+
id=item.GetId(),
|
812
|
+
)
|
736
813
|
if menu.MenuItemCount != 0:
|
737
814
|
self.cam.PopupMenu(menu)
|
738
815
|
menu.Destroy()
|
@@ -863,14 +940,43 @@ class CameraInterface(MWindow):
|
|
863
940
|
_icon = wx.NullIcon
|
864
941
|
_icon.CopyFromBitmap(icons8_camera.GetBitmap())
|
865
942
|
self.SetIcon(_icon)
|
866
|
-
self.
|
943
|
+
self.update_title()
|
867
944
|
self.Layout()
|
868
945
|
self.restore_aspect()
|
869
946
|
|
947
|
+
def update_title(self):
|
948
|
+
"""
|
949
|
+
Updates the title of the window to reflect the current camera index.
|
950
|
+
"""
|
951
|
+
label = _get_camera_attribute(
|
952
|
+
self.context.kernel,
|
953
|
+
self.index,
|
954
|
+
"desc",
|
955
|
+
_("Camera {index}").format(index=self.index),
|
956
|
+
)
|
957
|
+
self.SetTitle(label)
|
958
|
+
|
870
959
|
def create_menu(self, append):
|
871
960
|
def identify_cameras(event=None):
|
872
961
|
self.context("camdetect\n")
|
873
962
|
|
963
|
+
def change_label(event=None):
|
964
|
+
"""
|
965
|
+
Change the label of the camera.
|
966
|
+
"""
|
967
|
+
dialog = wx.TextEntryDialog(
|
968
|
+
self,
|
969
|
+
_("Enter a new label for the camera:"),
|
970
|
+
_("Change camera label"),
|
971
|
+
str(self.camera.desc or ""),
|
972
|
+
)
|
973
|
+
if dialog.ShowModal() == wx.ID_OK:
|
974
|
+
new_label = dialog.GetValue().strip()
|
975
|
+
if new_label:
|
976
|
+
self.context(f'camera{self.index} label "{new_label}"\n')
|
977
|
+
self.update_title()
|
978
|
+
dialog.Destroy()
|
979
|
+
|
874
980
|
wxglade_tmp_menu = wx.Menu()
|
875
981
|
item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Fisheye"), "")
|
876
982
|
self.Bind(wx.EVT_MENU, self.panel.reset_fisheye, id=item.GetId())
|
@@ -904,6 +1010,12 @@ class CameraInterface(MWindow):
|
|
904
1010
|
)
|
905
1011
|
self.Bind(wx.EVT_MENU, self.panel.swap_camera(i), id=item.GetId())
|
906
1012
|
wxglade_tmp_menu.AppendSeparator()
|
1013
|
+
item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Change camera label"), "")
|
1014
|
+
self.Bind(
|
1015
|
+
wx.EVT_MENU,
|
1016
|
+
change_label,
|
1017
|
+
id=item.GetId(),
|
1018
|
+
)
|
907
1019
|
item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Identify cameras"), "")
|
908
1020
|
self.Bind(wx.EVT_MENU, identify_cameras, id=item.GetId())
|
909
1021
|
|
@@ -932,6 +1044,29 @@ class CameraInterface(MWindow):
|
|
932
1044
|
def detect_usb_cameras(event=None):
|
933
1045
|
camera("camdetect\n")
|
934
1046
|
|
1047
|
+
caminfo = []
|
1048
|
+
for idx in range(5):
|
1049
|
+
label = _get_camera_attribute(
|
1050
|
+
kernel, idx, "desc", _("Camera {index}").format(index=idx)
|
1051
|
+
)
|
1052
|
+
caminfo.append(
|
1053
|
+
{
|
1054
|
+
"identifier": f"cam{idx}",
|
1055
|
+
"label": label,
|
1056
|
+
"action": camera_click(idx),
|
1057
|
+
"signal": f"camset{idx}",
|
1058
|
+
"multi_autoexec": True,
|
1059
|
+
},
|
1060
|
+
)
|
1061
|
+
caminfo.append(
|
1062
|
+
{
|
1063
|
+
"identifier": "id_cam",
|
1064
|
+
"label": _("Identify cameras"),
|
1065
|
+
"tip": _("Detects cameras on the system and sets them up"),
|
1066
|
+
"action": detect_usb_cameras,
|
1067
|
+
"multi_autoexec": True,
|
1068
|
+
},
|
1069
|
+
)
|
935
1070
|
kernel.register(
|
936
1071
|
"button/preparation/Camera",
|
937
1072
|
{
|
@@ -941,49 +1076,7 @@ class CameraInterface(MWindow):
|
|
941
1076
|
"identifier": "camera_id",
|
942
1077
|
"action": camera_click(),
|
943
1078
|
"priority": 3,
|
944
|
-
"multi":
|
945
|
-
{
|
946
|
-
"identifier": "cam0",
|
947
|
-
"label": _("Camera {index}").format(index=0),
|
948
|
-
"action": camera_click(0),
|
949
|
-
"signal": "camset0",
|
950
|
-
"multi_autoexec": True,
|
951
|
-
},
|
952
|
-
{
|
953
|
-
"identifier": "cam1",
|
954
|
-
"label": _("Camera {index}").format(index=1),
|
955
|
-
"action": camera_click(1),
|
956
|
-
"signal": "camset1",
|
957
|
-
"multi_autoexec": True,
|
958
|
-
},
|
959
|
-
{
|
960
|
-
"identifier": "cam2",
|
961
|
-
"label": _("Camera {index}").format(index=2),
|
962
|
-
"action": camera_click(2),
|
963
|
-
"signal": "camset2",
|
964
|
-
"multi_autoexec": True,
|
965
|
-
},
|
966
|
-
{
|
967
|
-
"identifier": "cam3",
|
968
|
-
"label": _("Camera {index}").format(index=3),
|
969
|
-
"action": camera_click(3),
|
970
|
-
"signal": "camset3",
|
971
|
-
"multi_autoexec": True,
|
972
|
-
},
|
973
|
-
{
|
974
|
-
"identifier": "cam4",
|
975
|
-
"label": _("Camera {index}").format(index=4),
|
976
|
-
"action": camera_click(4),
|
977
|
-
"signal": "camset4",
|
978
|
-
"multi_autoexec": True,
|
979
|
-
},
|
980
|
-
{
|
981
|
-
"identifier": "id_cam",
|
982
|
-
"label": _("Identify cameras"),
|
983
|
-
"action": detect_usb_cameras,
|
984
|
-
"multi_autoexec": True,
|
985
|
-
},
|
986
|
-
],
|
1079
|
+
"multi": caminfo,
|
987
1080
|
},
|
988
1081
|
)
|
989
1082
|
kernel.register("window/CameraURI", CameraURI)
|
@@ -1058,6 +1151,7 @@ class CameraInterface(MWindow):
|
|
1058
1151
|
def helptext():
|
1059
1152
|
return _("Display the camera window")
|
1060
1153
|
|
1154
|
+
|
1061
1155
|
class CameraURIPanel(wx.Panel):
|
1062
1156
|
def __init__(self, *args, context=None, index=None, **kwds):
|
1063
1157
|
kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
|
@@ -1070,8 +1164,11 @@ class CameraURIPanel(wx.Panel):
|
|
1070
1164
|
assert isinstance(self.index, int)
|
1071
1165
|
self.context.setting(list, "uris", [])
|
1072
1166
|
self.list_uri = wxListCtrl(
|
1073
|
-
self,
|
1074
|
-
|
1167
|
+
self,
|
1168
|
+
wx.ID_ANY,
|
1169
|
+
style=wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES,
|
1170
|
+
context=self.context,
|
1171
|
+
list_name="list_camerauri",
|
1075
1172
|
)
|
1076
1173
|
self.button_add = wxButton(self, wx.ID_ANY, _("Add URI"))
|
1077
1174
|
self.text_uri = TextCtrl(self, wx.ID_ANY, "")
|
@@ -1198,8 +1295,7 @@ class CameraURIPanel(wx.Panel):
|
|
1198
1295
|
def on_tree_popup_clear(self, index):
|
1199
1296
|
def delete(event):
|
1200
1297
|
if self.context.kernel.yesno(
|
1201
|
-
_("Do you really want to delete all entries?"),
|
1202
|
-
caption=_("URI-Manager")
|
1298
|
+
_("Do you really want to delete all entries?"), caption=_("URI-Manager")
|
1203
1299
|
):
|
1204
1300
|
self.context.uris.clear()
|
1205
1301
|
self.changed = True
|