meerk40t 0.9.7051__py2.py3-none-any.whl → 0.9.7910__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.
Files changed (69) hide show
  1. meerk40t/balormk/controller.py +3 -3
  2. meerk40t/balormk/device.py +7 -0
  3. meerk40t/balormk/driver.py +23 -14
  4. meerk40t/balormk/galvo_commands.py +18 -3
  5. meerk40t/balormk/gui/balorconfig.py +6 -0
  6. meerk40t/balormk/livelightjob.py +36 -14
  7. meerk40t/camera/camera.py +1 -0
  8. meerk40t/camera/gui/camerapanel.py +154 -58
  9. meerk40t/camera/plugin.py +46 -5
  10. meerk40t/core/elements/branches.py +90 -20
  11. meerk40t/core/elements/elements.py +59 -37
  12. meerk40t/core/elements/trace.py +10 -6
  13. meerk40t/core/node/node.py +2 -0
  14. meerk40t/core/plotplanner.py +7 -4
  15. meerk40t/device/gui/defaultactions.py +78 -14
  16. meerk40t/dxf/dxf_io.py +42 -0
  17. meerk40t/grbl/controller.py +245 -35
  18. meerk40t/grbl/device.py +102 -26
  19. meerk40t/grbl/driver.py +8 -2
  20. meerk40t/grbl/gui/grblconfiguration.py +6 -0
  21. meerk40t/grbl/gui/grblcontroller.py +1 -1
  22. meerk40t/gui/about.py +7 -0
  23. meerk40t/gui/choicepropertypanel.py +20 -30
  24. meerk40t/gui/devicepanel.py +27 -16
  25. meerk40t/gui/help_assets/help_assets.py +126 -2
  26. meerk40t/gui/icons.py +15 -0
  27. meerk40t/gui/laserpanel.py +102 -54
  28. meerk40t/gui/materialtest.py +10 -0
  29. meerk40t/gui/mkdebug.py +268 -9
  30. meerk40t/gui/navigationpanels.py +74 -8
  31. meerk40t/gui/propertypanels/operationpropertymain.py +185 -91
  32. meerk40t/gui/scenewidgets/elementswidget.py +7 -1
  33. meerk40t/gui/scenewidgets/selectionwidget.py +24 -9
  34. meerk40t/gui/simulation.py +1 -1
  35. meerk40t/gui/statusbarwidgets/shapepropwidget.py +50 -40
  36. meerk40t/gui/statusbarwidgets/statusbar.py +2 -2
  37. meerk40t/gui/toolwidgets/toolmeasure.py +1 -1
  38. meerk40t/gui/toolwidgets/toolnodeedit.py +4 -1
  39. meerk40t/gui/toolwidgets/tooltabedit.py +9 -7
  40. meerk40t/gui/wxmeerk40t.py +45 -15
  41. meerk40t/gui/wxmmain.py +23 -9
  42. meerk40t/gui/wxmribbon.py +36 -0
  43. meerk40t/gui/wxutils.py +66 -42
  44. meerk40t/kernel/inhibitor.py +120 -0
  45. meerk40t/kernel/kernel.py +38 -0
  46. meerk40t/lihuiyu/controller.py +33 -3
  47. meerk40t/lihuiyu/device.py +99 -4
  48. meerk40t/lihuiyu/driver.py +65 -5
  49. meerk40t/lihuiyu/gui/lhycontrollergui.py +69 -24
  50. meerk40t/lihuiyu/gui/lhydrivergui.py +6 -0
  51. meerk40t/lihuiyu/laserspeed.py +17 -10
  52. meerk40t/lihuiyu/parser.py +23 -0
  53. meerk40t/main.py +2 -2
  54. meerk40t/moshi/gui/moshidrivergui.py +7 -0
  55. meerk40t/newly/controller.py +3 -2
  56. meerk40t/newly/device.py +23 -2
  57. meerk40t/newly/driver.py +8 -3
  58. meerk40t/newly/gui/newlyconfig.py +7 -0
  59. meerk40t/ruida/gui/ruidaconfig.py +7 -0
  60. meerk40t/tools/geomstr.py +142 -49
  61. meerk40t/tools/rasterplotter.py +0 -5
  62. meerk40t/tools/ttfparser.py +921 -168
  63. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/METADATA +1 -1
  64. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/RECORD +69 -68
  65. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/LICENSE +0 -0
  66. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/WHEEL +0 -0
  67. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/entry_points.txt +0 -0
  68. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/top_level.txt +0 -0
  69. {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7910.dist-info}/zip-safe +0 -0
@@ -159,13 +159,112 @@ I segnaposto per "data" e "ora" possono anche contenere istruzioni di formattazi
159
159
 
160
160
  Per un insieme completo delle istruzioni di formattazione, vedere: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
161
161
  """
162
+ french_wordlist_howto = """
163
+ Les listes de mots vous permettent de créer des éléments de texte dans votre conception contenant du texte d'espace réservé qui est remplacé au moment de la gravure à partir de cette liste de mots. Vous pouvez ainsi graver plusieurs éléments avec des textes différents sans avoir à modifier votre conception à chaque fois.
164
+
165
+ Un espace réservé consiste en un nom entre accolades, par exemple '{PRENOM}'. Vous utilisez ce nom dans l'éditeur de listes de mots pour l'associer à l'espace réservé et l'espace réservé sera remplacé par le texte que vous saisissez dans le contenu de la liste de mots associée.
166
+
167
+ Comme exemple d'utilisation de cette fonctionnalité, imaginez que vous voulez créer un ensemble d'étiquettes de réservation de places pour un dîner, chacune avec le nom d'une personne différente. Après avoir créé le chemin de découpe pour le contour de l'étiquette de nom, par exemple un rectangle, utilisez l'outil de dessin de texte pour créer un élément de texte contenant ce qui suit :
168
+ 'Cette place est réservée pour {PRENOM}'
169
+
170
+ Ensuite, vous utilisez cet éditeur de listes de mots pour créer une ou plusieurs entrées comme suit :
171
+ |-----------|------|-------|
172
+ | Nom | Type | Index |
173
+ |-----------|------|-------|
174
+ | prenom | Texte| 0 |
175
+ |-----------|------|-------|
176
+ Puis cliquez sur la ligne 'prenom' et ajoutez plusieurs éléments au panneau Contenu, par exemple :
177
+ Paul
178
+ David
179
+ Andy
180
+ Maintenant, lorsque vous exécutez la gravure, vous obtiendrez des étiquettes de place individuelles qui ont des noms différents, par exemple 'Cette place est réservée pour Andy'.
181
+
182
+ Vous pouvez utiliser autant de noms d'espaces réservés différents que vous le souhaitez dans les champs de texte de votre conception.
183
+
184
+ La valeur 'Index' dans la table de liste de mots indique quelle entrée de la liste de contenu sera utilisée ensuite, zéro signifiant la première entrée. L'index est automatiquement augmenté de un à la fin de chaque gravure.
185
+
186
+ Mais supposons que pour l'efficacité, vous voulez maintenant graver deux étiquettes de réservation de places en même temps, chacune ayant un nom différent de la même liste. Dans ce cas, si la première étiquette utilise '{NOM#+0}' et la seconde '{NOM#+1}' (notez le signe plus). '{NOM}' ou '{NOM#+0}' utilise l'entrée actuelle (pointée par la valeur Index), '{NOM#+1}' utilise l'entrée suivante après la courante, etc.
187
+
188
+ Avec l'usage ci-dessus, vous pouvez utiliser ces valeurs autant de fois que vous le souhaitez dans votre conception. Pour faire avancer l'index, vous devez cliquer sur les boutons Précédent / Suivant dans la barre d'outils.
189
+
190
+ Comme alternative à la saisie manuelle des valeurs de liste de mots en utilisant cet éditeur de listes de mots, vous pouvez utiliser un fichier CSV standard séparé par des virgules. Les noms d'espaces réservés sont définis dans la ligne d'en-tête CSV standard (la première ligne du fichier CSV), et le contenu est ensuite pris de toutes les lignes suivantes. Le moyen le plus simple de créer un fichier CSV est d'utiliser un tableur, par exemple Excel, cependant, par exemple pour les sites de commerce électronique, votre site web pourrait automatiquement créer le fichier CSV à partir des commandes passées en ligne par les clients.
191
+
192
+ Les entrées chargées à partir d'un fichier CSV sont affichées comme Type CSV, et vous pouvez définir les valeurs Index pour toutes les entrées CSV en même temps.
193
+
194
+ Note : Si votre CSV n'a pas de ligne d'en-tête, les colonnes seront nommées 'column_1', 'column_2', etc.
195
+
196
+ La liste de mots contient également quelques entrées spéciales (qui pourraient être particulièrement utiles pour les conceptions de calibrage) :
197
+ * 'version' - Version de Meerk40t
198
+ * 'date' - Date de début de la gravure
199
+ * 'time' - Heure de début de la gravure
200
+ * 'op_device' - Appareil sur lequel vous gravez
201
+ * 'op_speed' - Vitesse de l'opération courante
202
+ * 'op_power' - PPI de l'opération courante
203
+ * 'op_dpi' - DPI de l'opération courante (trame)
204
+ * 'op_passes' - Passes de l'opération courante
205
+
206
+ Les espaces réservés pour 'date' et 'time' peuvent également contenir des directives de formatage qui vous permettent de les formater selon vos conventions locales, par exemple :
207
+ {date@%d.%m.%Y} - 31.12.2022
208
+ {time@%H:%M} - 23:59
209
+
210
+ Pour un ensemble complet de directives de format, voir : https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
211
+ """
212
+ russian_wordlist_howto = """
213
+ Списки слов позволяют создавать текстовые элементы в дизайне, содержащие текст-заполнители, которые заменяются во время выжигания содержимым из данного списка слов. Таким образом можно выжигать несколько элементов с разным текстом, не изменяя каждый раз дизайн.
214
+
215
+ Заполнитель состоит из имени в фигурных скобках, например '{ИМЯФАМИЛИЯ}'. Вы используете это имя в редакторе списков слов, чтобы связать его с заполнителем, и заполнитель будет заменен текстом, который вы введете в связанное содержимое списка слов.
216
+
217
+ В качестве примера использования этой функциональности, представьте, что вы хотите создать набор карточек для резервирования мест на ужине, каждая с именем разного человека. Создав путь резки для контура именной карточки, например прямоугольник, используйте инструмент рисования текста для создания текстового элемента, содержащего следующее:
218
+ 'Это место зарезервировано для {ИМЯФАМИЛИЯ}'
219
+
220
+ Затем используйте редактор списков слов для создания одной или нескольких записей следующим образом:
221
+ |-----------|------|-------|
222
+ | Имя | Тип | Индекс|
223
+ |-----------|------|-------|
224
+ | имяфамилия| Текст| 0 |
225
+ |-----------|------|-------|
226
+ Затем нажмите на строку 'имяфамилия' и добавьте несколько элементов в панель содержимого, например:
227
+ Павел
228
+ Давид
229
+ Андрей
230
+ Теперь при выполнении выжигания вы получите индивидуальные карточки мест с разными именами, например 'Это место зарезервировано для Андрей'.
231
+
232
+ Вы можете использовать столько разных имен заполнителей, сколько захотите, в текстовых полях вашего дизайна.
233
+
234
+ Значение 'Индекс' в таблице списка слов указывает, какая запись в списке содержимого будет использована следующей, ноль означает первую запись. Индекс автоматически увеличивается на единицу в конце каждого выжигания.
235
+
236
+ Но предположим, что для эффективности вы теперь хотите выжечь две карточки резервирования мест одновременно, каждая с разным именем из того же списка. В этом случае, если первая карточка использует '{ИМЯ#+0}', а вторая '{ИМЯ#+1}' (обратите внимание на знак плюс). '{ИМЯ}' или '{ИМЯ#+0}' использует текущую запись (на которую указывает значение индекса), '{ИМЯ#+1}' использует следующую запись после текущей и т.д.
237
+
238
+ При таком использовании вы можете использовать эти значения столько раз, сколько захотите в вашем дизайне. Для продвижения индекса вам нужно нажать кнопки Пред/След в панели инструментов.
239
+
240
+ В качестве альтернативы ручному вводу значений списка слов с помощью этого редактора, вы можете использовать стандартный CSV-файл, разделенный запятыми. Имена заполнителей определяются в стандартной строке заголовка CSV (первая строка в CSV-файле), а содержимое затем берется из всех следующих строк. Самый простой способ создать CSV-файл - использовать электронную таблицу, например Excel, однако для сайтов электронной коммерции ваш веб-сайт может автоматически создать CSV-файл из заказов, размещенных онлайн клиентами.
241
+
242
+ Записи, загруженные из CSV-файла, показываются как тип CSV, и вы можете установить значения индекса для всех CSV-записей одновременно.
243
+
244
+ Примечание: Если ваш CSV не имеет строки заголовка, столбцы будут названы 'column_1', 'column_2' и т.д.
245
+
246
+ Список слов также содержит некоторые специальные записи (которые могут быть особенно полезны для калибровочных дизайнов):
247
+ * 'version' - Версия Meerk40t
248
+ * 'date' - Дата начала выжигания
249
+ * 'time' - Время начала выжигания
250
+ * 'op_device' - Устройство, на котором вы выжигаете
251
+ * 'op_speed' - Скорость текущей операции
252
+ * 'op_power' - PPI текущей операции
253
+ * 'op_dpi' - DPI текущей (растровой) операции
254
+ * 'op_passes' - Проходы операции текущей операции
255
+
256
+ Заполнители для 'date' и 'time' также могут содержать директивы форматирования, которые позволяют форматировать их согласно вашим местным соглашениям, например:
257
+ {date@%d.%m.%Y} - 31.12.2022
258
+ {time@%H:%M} - 23:59
259
+
260
+ Для полного набора директив форматирования см.: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
261
+ """
162
262
 
163
263
  english_material_howto = """
164
264
  The Material Library Manager allows to create, maintain, use and manage operations that are customized to provide a desired effect with a given material (hence the name Material Library).
165
265
  The parameters you want to use e.g. for cutting acrylic are very different from the ones you want to use to engrave a picture on slate.
166
266
  You can share such a material setting with the MeerK40t community and you can benefit from the contributions of others by loading and using their settings.
167
267
  """
168
-
169
268
  german_material_howto = """
170
269
  Die Material-Bibliothek erlaubt es Arbeitsgangs-Einstellungen für spezifische Materialien anzulegen und zu verwalten.
171
270
  Die Parameter, die man z.B. für das Schneiden von Acryl benötigt unterscheiden sich deutlich von denen, die man etwa zum Gravieren eine Fotos auf Schiefer braucht.
@@ -176,35 +275,60 @@ Il Gestore della libreria di materiali consente di creare, gestire, memorizzare
176
275
  I parametri da utilizzare, ad esempio, per tagliare l'acrilico sono molto diversi da quelli da utilizzare per incidere un'immagine sull'ardesia.
177
276
  È possibile condividere tali impostazioni di lavorazione dei materiali con la comunità MeerK40t e beneficiare dei contributi degli altri caricando e utilizzando le loro impostazioni.
178
277
  """
278
+ french_material_howto = """
279
+ Le gestionnaire de bibliothèque de matériaux permet de créer, maintenir, utiliser et gérer des opérations qui sont personnalisées pour fournir un effet désiré avec un matériau donné (d'où le nom Bibliothèque de matériaux).
280
+ Les paramètres que vous voulez utiliser, par exemple pour découper de l'acrylique, sont très différents de ceux que vous voulez utiliser pour graver une image sur de l'ardoise.
281
+ Vous pouvez partager de tels paramètres de matériau avec la communauté MeerK40t et vous pouvez bénéficier des contributions des autres en chargeant et en utilisant leurs paramètres.
282
+ """
283
+ russian_material_howto = """
284
+ Менеджер библиотеки материалов позволяет создавать, поддерживать, использовать и управлять операциями, которые настроены для обеспечения желаемого эффекта с определенным материалом (отсюда и название "Библиотека материалов").
285
+ Параметры, которые вы хотите использовать, например, для резки акрила, очень отличаются от тех, которые вы хотите использовать для гравировки изображения на сланце.
286
+ Вы можете поделиться такими настройками материала с сообществом MeerK40t и можете извлечь пользу из вкладов других, загружая и используя их настройки.
287
+ """
179
288
 
180
289
 
181
290
  def asset(context, asset):
182
291
  language = context.language
183
292
  lang = "english"
293
+ # Language #00 : en - English
184
294
  if language == 0: # ("en", "English", wx.LANGUAGE_ENGLISH)
185
295
  lang = "english"
296
+ # Language #01 : it - italiano
186
297
  if language == 1: # ("it", "italiano", wx.LANGUAGE_ITALIAN),
187
298
  lang = "italian"
299
+ # Language #02 : fr - français
188
300
  if language == 2: # ("fr", "français", wx.LANGUAGE_FRENCH),
189
301
  lang = "french"
302
+ # Language #03 : de - Deutsch
190
303
  if language == 3: # ("de", "Deutsch", wx.LANGUAGE_GERMAN),
191
304
  lang = "german"
305
+ # Language #04 : es - español
192
306
  if language == 4: # ("es", "español", wx.LANGUAGE_SPANISH),
193
307
  lang = "spanish"
308
+ # Language #05 : zh - 中文
194
309
  if language == 5: # ("zh", "中文", wx.LANGUAGE_CHINESE),
195
310
  lang = "chinese"
311
+ # Language #06 : hu - Magyar
196
312
  if language == 6: # ("hu", "Magyar", wx.LANGUAGE_HUNGARIAN),
197
313
  lang = "hungarian"
314
+ # Language #07 : pt_PT - português
198
315
  if language == 7: # ("pt_PT", "português", wx.LANGUAGE_PORTUGUESE),
199
316
  lang = "portuguese"
317
+ # Language #08 : pt_BR - português brasileiro
200
318
  if (
201
319
  language == 8
202
320
  ): # ("pt_BR", "português brasileiro", wx.LANGUAGE_PORTUGUESE_BRAZILIAN),
203
321
  lang = "portuguese_brazilian"
322
+ # Language #09 : ja - 日本
204
323
  if language == 9: # ("ja", "日本", wx.LANGUAGE_JAPANESE),
205
324
  lang = "japanese"
206
- if language == 9: # ("nl", "Nederlands", wx.LANGUAGE_DUTCH),
325
+ # Language #10 : nl - Nederlands
326
+ if language == 10: # ("nl", "Nederlands", wx.LANGUAGE_DUTCH),
207
327
  lang = "dutch"
328
+ # Language #11 : ru - русский
329
+ if language == 11: # ("ru", "русский", wx.LANGUAGE_RUSSIAN),
330
+ lang = "russian"
331
+
208
332
  text = ""
209
333
  try:
210
334
  text = globals()[f"{lang}_{asset}"]
meerk40t/gui/icons.py CHANGED
@@ -3497,3 +3497,18 @@ icon_z_home = VectorIcon(
3497
3497
  "M 37.5,25 h 25 l -25, 25 h 25",
3498
3498
  ),
3499
3499
  )
3500
+
3501
+ icon_coffee = VectorIcon(
3502
+ fill=(),
3503
+ stroke=(
3504
+ "M4 20H10.9433M10.9433 20H11.0567M10.9433 20C10.9622 20.0002 10.9811 20.0002 11 20.0002C11.0189 20.0002 11.0378 20.0002 11.0567 20M10.9433 20C7.1034 19.9695 4 16.8468 4 12.9998V8.92285C4 8.41305 4.41305 8 4.92285 8H17.0767C17.5865 8 18 8.41305 18 8.92285V9M11.0567 20H18M11.0567 20C14.8966 19.9695 18 16.8468 18 12.9998M18 9H19.5C20.8807 9 22 10.1193 22 11.5C22 12.8807 20.8807 14 19.5 14H18V12.9998M18 9V12.9998M15 3L14 5M12 3L11 5M9 3L8 5",
3505
+ ),
3506
+ )
3507
+
3508
+ icon_sleep = VectorIcon(
3509
+ fill=(
3510
+ "M7.953,13 L1.027,13 C0.65,13 0.311,12.769 0.16,12.411 C0.009,12.053 0.076,11.636 0.328,11.348 L5.859,4.973 L0.988,4.973 C0.465,4.973 -2.27373675e-13,4.572 -2.27373675e-13,4.028 C-2.27373675e-13,3.483 0.425,3.043 0.947,3.043 L7.973,3.043 C8.347,3.043 8.689,3.273 8.839,3.632 C8.989,3.991 8.924,4.406 8.671,4.695 L3.179,11.082 L7.952,11.082 C8.475,11.082 8.899,11.471 8.899,12.015 C8.9,12.559 8.477,13 7.953,13 L7.953,13 Z",
3511
+ "M15.755,7.935 L9.701,7.935 C9.447,7.935 9.219,7.783 9.115,7.544 C9.011,7.307 9.053,7.027 9.222,6.833 L14.338,0.964 L9.338,0.964 C8.984,0.964 9.02,0.034 9.373,0.034 L15.471,0.034 C15.724,0.034 15.917,0.151 16.021,0.388 C16.126,0.625 16.083,0.905 15.914,1.1 L10.88,6.963 L15.755,6.963 C16.109,6.964 16.109,7.935 15.755,7.935 L15.755,7.935 Z",
3512
+ ),
3513
+ stroke=(),
3514
+ )
@@ -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 hasattr(self.context.device.driver, "has_adjustable_power"):
386
- if self.context.device.driver.has_adjustable_power:
387
- flag_power = True
388
- # Let's establish the value and update the slider...
389
- value = self.context.device.driver.power_scale
390
- if value != 1:
391
- override = True
392
- half = self.slider_size / 2
393
- sliderval = int(value * half)
394
- sliderval = max(1, min(self.slider_size, sliderval))
395
- self.slider_power.SetValue(sliderval)
396
- self.on_slider_speed(None)
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 hasattr(self.context.device.driver, "has_adjustable_speed"):
399
- if self.context.device.driver.has_adjustable_speed:
400
- flag_speed = True
401
- # Let's establish the value and update the slider...
402
- value = self.context.device.driver.speed_scale
403
- if value != 1:
404
- override = True
405
- half = self.slider_size / 2
406
- sliderval = int(value * half)
407
- sliderval = max(1, min(self.slider_size, sliderval))
408
- self.slider_speed.SetValue(sliderval)
409
- self.on_slider_speed(None)
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 hasattr(self.context.device.driver, "has_adjustable_power"):
431
- if self.context.device.driver.has_adjustable_power:
432
- if event is not None:
433
- self.context.device.driver.set_power_scale(1.0)
434
- self.slider_power.SetValue(10)
435
- self.on_slider_power(None)
436
- if hasattr(self.context.device.driver, "has_adjustable_speed"):
437
- if self.context.device.driver.has_adjustable_speed:
438
- if event is not None:
439
- self.context.device.driver.set_speed_scale(1.0)
440
- self.slider_speed.SetValue(10)
441
- self.on_slider_speed(None)
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
- half = self.slider_size / 2
456
- newvalue = sliderval - half # -> -9 to +10
457
- factor = 1 + newvalue / half
458
- if event is not None:
459
- self.context.device.driver.set_power_scale(factor)
460
- msg = f"{'+' if factor > 1 else ''}{100 * (factor - 1.0):.0f}%"
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
- if self.checkbox_optimize.GetValue():
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
  )
@@ -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
  ]