meerk40t 0.9.7910__py2.py3-none-any.whl → 0.9.7940__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 (40) hide show
  1. meerk40t/balormk/controller.py +46 -13
  2. meerk40t/balormk/livelightjob.py +34 -7
  3. meerk40t/core/bindalias.py +12 -4
  4. meerk40t/core/cutcode/plotcut.py +2 -1
  5. meerk40t/core/elements/branches.py +35 -14
  6. meerk40t/core/elements/clipboard.py +10 -12
  7. meerk40t/core/elements/elements.py +23 -0
  8. meerk40t/core/elements/files.py +1 -1
  9. meerk40t/core/elements/geometry.py +48 -14
  10. meerk40t/core/elements/grid.py +56 -24
  11. meerk40t/core/elements/offset_clpr.py +2 -4
  12. meerk40t/core/elements/placements.py +17 -22
  13. meerk40t/core/elements/render.py +30 -11
  14. meerk40t/core/elements/shapes.py +206 -126
  15. meerk40t/core/node/effect_hatch.py +8 -7
  16. meerk40t/core/node/effect_warp.py +7 -2
  17. meerk40t/core/node/effect_wobble.py +8 -2
  18. meerk40t/core/node/op_image.py +79 -25
  19. meerk40t/core/spoolers.py +1 -1
  20. meerk40t/core/units.py +4 -0
  21. meerk40t/grbl/emulator.py +10 -8
  22. meerk40t/grbl/gcodejob.py +11 -3
  23. meerk40t/grbl/plugin.py +10 -1
  24. meerk40t/gui/help_assets/help_assets.py +402 -43
  25. meerk40t/gui/plugin.py +12 -0
  26. meerk40t/gui/tips.py +78 -41
  27. meerk40t/gui/wxmmain.py +99 -4
  28. meerk40t/lihuiyu/driver.py +46 -9
  29. meerk40t/main.py +1 -1
  30. meerk40t/ruida/emulator.py +13 -10
  31. meerk40t/ruida/plugin.py +5 -0
  32. meerk40t/ruida/rdjob.py +5 -5
  33. meerk40t/tools/geomstr.py +15 -0
  34. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/METADATA +1 -1
  35. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/RECORD +40 -40
  36. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/LICENSE +0 -0
  37. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/WHEEL +0 -0
  38. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/entry_points.txt +0 -0
  39. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/top_level.txt +0 -0
  40. {meerk40t-0.9.7910.dist-info → meerk40t-0.9.7940.dist-info}/zip-safe +0 -0
@@ -210,7 +210,6 @@ class WarpEffectNode(Node, FunctionalParameter):
210
210
  self.d3 = complex(nn3.x - pp3.x, nn3.y - pp3.y)
211
211
  self.d4 = complex(nn4.x - pp4.x, nn4.y - pp4.y)
212
212
 
213
-
214
213
  def default_map(self, default_map=None):
215
214
  default_map = super().default_map(default_map=default_map)
216
215
  default_map["element_type"] = "Warp"
@@ -229,6 +228,8 @@ class WarpEffectNode(Node, FunctionalParameter):
229
228
  subs = right_types(e)
230
229
  res.extend(subs)
231
230
  elif e.type.startswith("elem"):
231
+ if hasattr(e, "hidden") and e.hidden:
232
+ continue
232
233
  res.append(e)
233
234
  return res
234
235
 
@@ -297,7 +298,11 @@ class WarpEffectNode(Node, FunctionalParameter):
297
298
  self.altered()
298
299
 
299
300
  def can_drop(self, drag_node):
300
- if hasattr(drag_node, "as_geometry") or drag_node.type in ("effect", "file", "group", "reference") or (drag_node.type.startswith("op ") and drag_node.type != "op dots"):
301
+ if (
302
+ hasattr(drag_node, "as_geometry")
303
+ or drag_node.type in ("effect", "file", "group", "reference")
304
+ or (drag_node.type.startswith("op ") and drag_node.type != "op dots")
305
+ ):
301
306
  return True
302
307
  return False
303
308
 
@@ -2,8 +2,8 @@ import math
2
2
  from copy import copy
3
3
  from math import sqrt
4
4
 
5
- from meerk40t.core.node.node import Node
6
5
  from meerk40t.core.node.mixins import Suppressable
6
+ from meerk40t.core.node.node import Node
7
7
  from meerk40t.core.units import Length
8
8
  from meerk40t.svgelements import Color
9
9
  from meerk40t.tools.geomstr import Geomstr # , Scanbeam
@@ -225,6 +225,8 @@ class WobbleEffectNode(Node, Suppressable):
225
225
  subs = right_types(e)
226
226
  res.extend(subs)
227
227
  elif e.type.startswith("elem"):
228
+ if hasattr(e, "hidden") and e.hidden:
229
+ continue
228
230
  res.append(e)
229
231
  return res
230
232
 
@@ -384,7 +386,11 @@ class WobbleEffectNode(Node, Suppressable):
384
386
  self.altered()
385
387
 
386
388
  def can_drop(self, drag_node):
387
- if hasattr(drag_node, "as_geometry") or drag_node.type in ("effect", "file", "group", "reference") or (drag_node.type.startswith("op ") and drag_node.type != "op dots"):
389
+ if (
390
+ hasattr(drag_node, "as_geometry")
391
+ or drag_node.type in ("effect", "file", "group", "reference")
392
+ or (drag_node.type.startswith("op ") and drag_node.type != "op dots")
393
+ ):
388
394
  return True
389
395
  return False
390
396
 
@@ -21,16 +21,17 @@ Methods:
21
21
  """
22
22
  from copy import copy
23
23
  from math import isnan
24
+
24
25
  from meerk40t.constants import (
25
- RASTER_T2B,
26
26
  RASTER_B2T,
27
- RASTER_R2L,
28
- RASTER_L2R,
29
- RASTER_HATCH,
27
+ RASTER_CROSSOVER,
30
28
  RASTER_GREEDY_H,
31
29
  RASTER_GREEDY_V,
32
- RASTER_CROSSOVER,
30
+ RASTER_HATCH,
31
+ RASTER_L2R,
32
+ RASTER_R2L,
33
33
  RASTER_SPIRAL,
34
+ RASTER_T2B,
34
35
  )
35
36
  from meerk40t.core.cutcode.rastercut import RasterCut
36
37
  from meerk40t.core.elements.element_types import *
@@ -67,7 +68,13 @@ class ImageOpNode(Node, Parameters):
67
68
  super().__init__(type="op image", **kwargs)
68
69
  self._formatter = "{enabled}{pass}{element_type}{direction}{speed}mm/s @{power}"
69
70
  # They might come from a svg read, but shouldnt be in settings
70
- for attrib in ("lock", "dangerous", "use_grayscale", "consider_laserspot", "overrule_dpi"):
71
+ for attrib in (
72
+ "lock",
73
+ "dangerous",
74
+ "use_grayscale",
75
+ "consider_laserspot",
76
+ "overrule_dpi",
77
+ ):
71
78
  if attrib in self.settings:
72
79
  del self.settings[attrib]
73
80
 
@@ -146,7 +153,9 @@ class ImageOpNode(Node, Parameters):
146
153
  # Move operation to a different position.
147
154
  return True
148
155
  elif drag_node.type in ("file", "group"):
149
- return not any(e.has_ancestor("branch reg") for e in drag_node.flat(elem_nodes))
156
+ return not any(
157
+ e.has_ancestor("branch reg") for e in drag_node.flat(elem_nodes)
158
+ )
150
159
  return False
151
160
 
152
161
  def drop(self, drag_node, modify=True, flag=False):
@@ -255,8 +264,12 @@ class ImageOpNode(Node, Parameters):
255
264
  height_in_inches = (max_y - min_y) / UNITS_PER_INCH
256
265
  speed_in_per_s = self.speed / MM_PER_INCH
257
266
  if self.raster_direction in (
258
- RASTER_T2B, RASTER_B2T, RASTER_HATCH,
259
- RASTER_GREEDY_H, RASTER_CROSSOVER, RASTER_SPIRAL,
267
+ RASTER_T2B,
268
+ RASTER_B2T,
269
+ RASTER_HATCH,
270
+ RASTER_GREEDY_H,
271
+ RASTER_CROSSOVER,
272
+ RASTER_SPIRAL,
260
273
  ):
261
274
  scanlines = height_in_inches * dpi
262
275
  if not self.bidirectional:
@@ -268,7 +281,12 @@ class ImageOpNode(Node, Parameters):
268
281
  this_len = scanlines * width_in_inches + height_in_inches
269
282
  estimate += this_len / speed_in_per_s
270
283
  # print (f"Horizontal scanlines: {scanlines}, Length: {this_len:.1f}")
271
- if self.raster_direction in (RASTER_L2R, RASTER_R2L, RASTER_HATCH, RASTER_GREEDY_V):
284
+ if self.raster_direction in (
285
+ RASTER_L2R,
286
+ RASTER_R2L,
287
+ RASTER_HATCH,
288
+ RASTER_GREEDY_V,
289
+ ):
272
290
  scanlines = width_in_inches * dpi
273
291
  if not self.bidirectional:
274
292
  scanlines *= 2
@@ -304,7 +322,14 @@ class ImageOpNode(Node, Parameters):
304
322
  if self.consider_laserspot:
305
323
  try:
306
324
  laserspot = getattr(context.device, "laserspot", "0.3mm")
307
- spot = 2 * float(Length(laserspot)) / ( context.device.view.native_scale_x + context.device.view.native_scale_y)
325
+ spot = (
326
+ 2
327
+ * float(Length(laserspot))
328
+ / (
329
+ context.device.view.native_scale_x
330
+ + context.device.view.native_scale_y
331
+ )
332
+ )
308
333
  # print (f"Laserpot in device units: {spot:.2f} [{laserspot.length_mm}], scale: {context.device.view.native_scale_x + context.device.view.native_scale_y:.2f}")
309
334
  except (ValueError, AttributeError):
310
335
  spot = 0
@@ -388,9 +413,12 @@ class ImageOpNode(Node, Parameters):
388
413
  def call_me(method):
389
414
  def handler():
390
415
  method(self)
416
+
391
417
  return handler
392
418
 
393
- for key, description, method in context.kernel.lookup_all("raster_preprocessor/.*"):
419
+ for key, description, method in context.kernel.lookup_all(
420
+ "raster_preprocessor/.*"
421
+ ):
394
422
  if key == self.raster_direction:
395
423
  plan.commands.append(call_me(method))
396
424
  # print (f"Found {description}")
@@ -433,7 +461,13 @@ class ImageOpNode(Node, Parameters):
433
461
  start_on_top = self.raster_preference_top
434
462
  if direction in (RASTER_GREEDY_V, RASTER_L2R, RASTER_R2L):
435
463
  horizontal = False
436
- if direction in (RASTER_B2T, RASTER_T2B, RASTER_HATCH, RASTER_CROSSOVER, RASTER_GREEDY_H):
464
+ if direction in (
465
+ RASTER_B2T,
466
+ RASTER_T2B,
467
+ RASTER_HATCH,
468
+ RASTER_CROSSOVER,
469
+ RASTER_GREEDY_H,
470
+ ):
437
471
  horizontal = True
438
472
  if direction in (RASTER_T2B, RASTER_CROSSOVER):
439
473
  start_on_top = True
@@ -494,7 +528,7 @@ class ImageOpNode(Node, Parameters):
494
528
  gres = 0
495
529
  if gres > 255:
496
530
  gres = 255
497
- stepsize = 255 / gres
531
+ stepsize = 255 / gres
498
532
 
499
533
  # no need for the filter as we have already moved every
500
534
  # pixel during preprocessing to either 255 or 0
@@ -607,27 +641,42 @@ class ImageOpNode(Node, Parameters):
607
641
  else:
608
642
  # Create Cut Object for regular image
609
643
  image_filter = None
610
- do_optimize = self.raster_direction in (RASTER_GREEDY_H, RASTER_GREEDY_V)
644
+ do_optimize = self.raster_direction in (
645
+ RASTER_GREEDY_H,
646
+ RASTER_GREEDY_V,
647
+ )
611
648
  if do_optimize:
612
649
  # get some image statistics
613
650
  white_pixels = 0
614
651
  used_colors = pil_image.getcolors()
615
652
  for col_count, col in used_colors:
616
- if col==255:
653
+ if col == 255:
617
654
  white_pixels = col_count
618
655
  break
619
- white_pixel_ratio = white_pixels / (pil_image.width * pil_image.height)
656
+ white_pixel_ratio = white_pixels / (
657
+ pil_image.width * pil_image.height
658
+ )
620
659
  # print (f"white pixels: {white_pixels}, ratio = {white_pixel_ratio:.3f}")
621
660
  if white_pixel_ratio < 0.3:
622
- self.raster_direction = RASTER_T2B if self.raster_direction == RASTER_GREEDY_H else RASTER_L2R
661
+ self.raster_direction = (
662
+ RASTER_T2B
663
+ if self.raster_direction == RASTER_GREEDY_H
664
+ else RASTER_L2R
665
+ )
623
666
 
624
- if self.raster_direction in (RASTER_CROSSOVER, RASTER_SPIRAL): # Crossover - need both
667
+ if self.raster_direction in (
668
+ RASTER_CROSSOVER,
669
+ RASTER_SPIRAL,
670
+ ): # Crossover - need both
625
671
  settings["raster_step_x"] = step_x
626
672
  settings["raster_step_y"] = step_y
627
- if self.raster_direction == RASTER_CROSSOVER and "split_crossover" in self._instructions:
673
+ if (
674
+ self.raster_direction == RASTER_CROSSOVER
675
+ and "split_crossover" in self._instructions
676
+ ):
628
677
  self._instructions["mode_filter"] = "ROW"
629
- horizontal=True
630
- bidirectional=True
678
+ horizontal = True
679
+ bidirectional = True
631
680
  start_on_top = True
632
681
  start_on_left = True
633
682
  if horizontal:
@@ -666,7 +715,7 @@ class ImageOpNode(Node, Parameters):
666
715
  cutcodes.append(cut)
667
716
 
668
717
  # Now set it for the next pass
669
- horizontal=False
718
+ horizontal = False
670
719
  if horizontal:
671
720
  # Raster step is only along y for horizontal raster
672
721
  settings["raster_step_x"] = 0
@@ -701,7 +750,7 @@ class ImageOpNode(Node, Parameters):
701
750
  )
702
751
  cut.path = path
703
752
  cut.original_op = self.type
704
- cutcodes.append(cut)
753
+ cutcodes.append(cut)
705
754
  if self.raster_direction == RASTER_HATCH:
706
755
  # Create optional crosshatch cut
707
756
  direction = RASTER_L2R if start_on_left else RASTER_R2L
@@ -750,6 +799,11 @@ class ImageOpNode(Node, Parameters):
750
799
  self._bounds = None
751
800
  if self.output:
752
801
  if self._children:
753
- self._bounds = Node.union_bounds(self._children, bounds=self._bounds, ignore_locked=False, ignore_hidden=True)
802
+ self._bounds = Node.union_bounds(
803
+ self._children,
804
+ bounds=self._bounds,
805
+ ignore_locked=False,
806
+ ignore_hidden=True,
807
+ )
754
808
  self._bounds_dirty = False
755
809
  return self._bounds
meerk40t/core/spoolers.py CHANGED
@@ -370,7 +370,7 @@ class SpoolerJob:
370
370
 
371
371
  @return:
372
372
  """
373
- if self.is_running:
373
+ if self.is_running():
374
374
  return "Running"
375
375
  else:
376
376
  return "Queued"
meerk40t/core/units.py CHANGED
@@ -106,7 +106,9 @@ class Length:
106
106
  unitless=1.0,
107
107
  preferred_units=None,
108
108
  digits=None,
109
+ settings=None,
109
110
  ):
111
+ self.settings = settings if settings is not None else {}
110
112
  self._digits = digits
111
113
  self._amount = amount
112
114
  if relative_length:
@@ -121,6 +123,8 @@ class Length:
121
123
  else:
122
124
  raise ValueError("Arguments not acceptable")
123
125
  s = str(value)
126
+ if s in self.settings:
127
+ s = str(self.settings[s])
124
128
  match = REGEX_LENGTH.match(s)
125
129
  if not match:
126
130
  raise ValueError(f"Length was not parsable: '{s}'.")
meerk40t/grbl/emulator.py CHANGED
@@ -368,6 +368,15 @@ class GRBLEmulator:
368
368
  else:
369
369
  self._buffer.append(chr(c))
370
370
 
371
+ def _home_device(self):
372
+ if hasattr(self.device.driver, "physical_home"):
373
+ # If the driver has a physical home, we can use that.
374
+ self.device.driver.physical_home()
375
+ elif hasattr(self.device.driver, "home"):
376
+ self.device.driver.home()
377
+ else:
378
+ self.device.driver.move_abs(0, 0)
379
+
371
380
  def _grbl_special(self, data):
372
381
  """
373
382
  GRBL special commands are commands beginning with $ that do purely grbl specific things.
@@ -458,14 +467,7 @@ class GRBLEmulator:
458
467
  elif data == "$H":
459
468
  if not self.settings["homing_cycle_enable"]:
460
469
  return 5 # Homing cycle not enabled by settings.
461
- try:
462
- self.device.driver.physical_home()
463
- except AttributeError:
464
- pass
465
- try:
466
- self.device.driver.move_abs(0, 0)
467
- except AttributeError:
468
- pass
470
+ self._home_device()
469
471
  return 0
470
472
  elif data.startswith("$J="):
471
473
  """
meerk40t/grbl/gcodejob.py CHANGED
@@ -574,8 +574,10 @@ class GcodeJob:
574
574
  # Numeric value format is not valid or missing an expected value.
575
575
  return ERROR_NUMERIC_VALUE_INVALID
576
576
  if 0.0 < v <= 1.0:
577
- v *= 1000 # numbers between 0-1 are taken to be in range 0-1.
577
+ v *= 1000 # numbers between 0-1 are taken to be in range 0-1000.
578
578
  if self.power != v:
579
+ # print(f"Setting power to {v}")
580
+ self.plot_commit() # Power change means plot change
579
581
  try:
580
582
  self._driver.set("power", v)
581
583
  except AttributeError:
@@ -707,12 +709,18 @@ class GcodeJob:
707
709
  if power is None:
708
710
  power = 1000
709
711
  power = min(1000, power)
712
+ # print (f"Plotting location: ({x}, {y}) with power {power} (self.power={self.power})")
710
713
  if self.plotcut is None:
711
714
  ox, oy = matrix.transform_point([self.x, self.y])
712
- self.plotcut = PlotCut(settings={"speed": self.speed})
715
+ p = power if power else self.power
716
+ if p is None:
717
+ p = 1000
718
+ settings = {"speed": self.speed, "power": p}
719
+ self.plotcut = PlotCut(settings=settings)
713
720
  self.plotcut.plot_init(int(round(ox)), int(round(oy)))
714
721
  tx, ty = matrix.transform_point([x, y])
715
- self.plotcut.plot_append(int(round(tx)), int(round(ty)), (power / 1000.0))
722
+ on = power / self.power if self.power else 0
723
+ self.plotcut.plot_append(int(round(tx)), int(round(ty)), on)
716
724
  if not self.program_mode:
717
725
  self.plot_commit()
718
726
  self.x = x
meerk40t/grbl/plugin.py CHANGED
@@ -175,7 +175,9 @@ def plugin(kernel, lifecycle=None):
175
175
  {
176
176
  "provider": "provider/device/grbl",
177
177
  "friendly_name": _("Longer Ray5 (GRBL)"),
178
- "extended_info": _("Longer-branded 5w/10w/20w grbl diode laser.\nMake sure you verify your bed size! This machine has several upgrade kits."),
178
+ "extended_info": _(
179
+ "Longer-branded 5w/10w/20w grbl diode laser.\nMake sure you verify your bed size! This machine has several upgrade kits."
180
+ ),
179
181
  "priority": 21,
180
182
  "family": _("Longer Diode-Laser"),
181
183
  "choices": [
@@ -229,9 +231,13 @@ def plugin(kernel, lifecycle=None):
229
231
  hidden=True,
230
232
  )
231
233
  def grblserver(
234
+ command,
235
+ channel,
236
+ _,
232
237
  port=23,
233
238
  verbose=False,
234
239
  quit=False,
240
+ remainder=None,
235
241
  **kwargs,
236
242
  ):
237
243
  """
@@ -239,10 +245,13 @@ def plugin(kernel, lifecycle=None):
239
245
  this emulates a grbl devices in order to be compatible with software that
240
246
  controls that type of device.
241
247
  """
248
+ if remainder and remainder.lower() in ("stop", "quit"):
249
+ quit = True
242
250
  root = kernel.root
243
251
  grblcontrol = root.device.lookup("grblcontrol")
244
252
  if grblcontrol is None:
245
253
  if quit:
254
+ channel(_("No control instance to stop."))
246
255
  return
247
256
  grblcontrol = GRBLControl(root)
248
257
  root.device.register("grblcontrol", grblcontrol)