There is a rendering limit - by DRAW_TIME in CNCCanvas.py, that result in an error, and limitations, also at modern hardware.
Detailed logging of the rendering and an escape from the limitations would be great for complex and big data above 1mb.
def drawPaths(self):
if not self.draw_paths:
print("DEBUG: draw_paths is disabled, skipping rendering")
for block in self.gcode.blocks:
block.resetPath()
return
print("=" * 60)
print("DEBUG: START drawPaths()")
print(f"DEBUG: Total blocks: {len(self.gcode.blocks)}")
print(f"DEBUG: DRAW_TIME setting: {DRAW_TIME} seconds")
total_lines = sum(len(block) for block in self.gcode.blocks)
print(f"DEBUG: Total lines to process: {total_lines}")
# Performance Monitoring
startTime = time.time()
lines_processed = 0
paths_created = 0
errors_encountered = 0
try:
# Adaptive performance settings based on file size
if total_lines > 50000:
update_frequency = 10000 # Very large files
gui_update_interval = 5.0 # Update GUI every 5 seconds
print("DEBUG: Using HIGH performance mode for large file")
elif total_lines > 10000:
update_frequency = 5000 # Large files
gui_update_interval = 3.0
print("DEBUG: Using MEDIUM performance mode")
else:
update_frequency = 1000 # Normal files
gui_update_interval = 1.0
print("DEBUG: Using NORMAL performance mode")
n = update_frequency
last_gui_update = time.time()
self.cnc.resetAllMargins()
drawG = self.draw_rapid or self.draw_paths or self.draw_margin
# Disable timeout completely - let it run as long as needed
# if time.time() - startTime > DRAW_TIME:
# raise AlarmException()
block_start_time = time.time()
for i, block in enumerate(self.gcode.blocks):
print(f"DEBUG: Processing block {i}: '{block.name()}' with {len(block)} lines")
start = True # start location found
block.resetPath()
block_lines_processed = 0
block_paths_created = 0
# Draw block
for j, line in enumerate(block):
lines_processed += 1
block_lines_processed += 1
n -= 1
current_time = time.time()
# Progress reporting for large files
if lines_processed % update_frequency == 0:
elapsed = current_time - startTime
rate = lines_processed / elapsed if elapsed > 0 else 0
print(f"DEBUG: Progress: {lines_processed}/{total_lines} lines "
f"({lines_processed/total_lines*100:.1f}%) - "
f"Rate: {rate:.1f} lines/sec - "
f"Paths: {paths_created} - "
f"Errors: {errors_encountered}")
# GUI update (less frequent for better performance)
if n == 0 or current_time - last_gui_update > gui_update_interval:
if current_time - last_gui_update > gui_update_interval:
try:
self.update()
last_gui_update = current_time
print(f"DEBUG: GUI updated - {lines_processed} lines processed")
except Exception as e:
print(f"DEBUG: GUI update failed: {e}")
# Reset counter with adaptive frequency
if total_lines - lines_processed > update_frequency * 10:
n = update_frequency
else:
n = max(100, update_frequency // 10) # More frequent near end
# Process line with comprehensive error handling
cmd = None
try:
# Compile and evaluate line
compiled_line = CNC.compileLine(line)
if compiled_line is not None:
cmd = self.gcode.evaluate(compiled_line, self.app)
# Handle different command types
if isinstance(cmd, tuple):
cmd = None # Special commands not for drawing
elif cmd is not None:
cmd = CNC.breakLine(cmd)
except AlarmException as ae:
print(f"DEBUG: AlarmException at block {i}, line {j}: {ae}")
errors_encountered += 1
# Continue with next line instead of stopping
continue
except MemoryError as me:
print(f"DEBUG: MEMORY ERROR at block {i}, line {j}: {me}")
errors_encountered += 1
# Try to recover by forcing garbage collection
import gc
gc.collect()
continue
except Exception as e:
print(f"DEBUG: Exception at block {i}, line {j}: {type(e).__name__}: {e}")
print(f"DEBUG: Problematic line: {line}")
errors_encountered += 1
# Continue with next line
continue
# Skip if no command to draw or drawing disabled
if cmd is None or not drawG:
block.addPath(None)
continue
# Create path with robust error handling
try:
path = self.drawPath(block, cmd)
if path is not None:
self._items[path] = (i, j)
block.addPath(path)
paths_created += 1
block_paths_created += 1
# Mark start position for first valid path in block
if start and self.cnc.gcode in (1, 2, 3):
block.startPath(self.cnc.x, self.cnc.y, self.cnc.z)
start = False
else:
block.addPath(None)
except MemoryError as me:
print(f"DEBUG: MEMORY ERROR in drawPath at block {i}, line {j}: {me}")
errors_encountered += 1
block.addPath(None)
# Force cleanup
import gc
gc.collect()
except Exception as e:
print(f"DEBUG: Error in drawPath at block {i}, line {j}: {type(e).__name__}: {e}")
errors_encountered += 1
block.addPath(None)
# Block completion stats
block_time = time.time() - block_start_time
print(f"DEBUG: Block {i} completed: {block_lines_processed} lines, "
f"{block_paths_created} paths, {block_time:.2f}s")
# Set end position for block
try:
block.endPath(self.cnc.x, self.cnc.y, self.cnc.z)
except Exception as e:
print(f"DEBUG: Error setting block end path: {e}")
block_start_time = time.time()
# Final statistics
total_time = time.time() - startTime
print("=" * 60)
print(f"DEBUG: RENDERING COMPLETED SUCCESSFULLY")
print(f"DEBUG: Total time: {total_time:.2f} seconds")
print(f"DEBUG: Lines processed: {lines_processed}")
print(f"DEBUG: Paths created: {paths_created}")
print(f"DEBUG: Errors encountered: {errors_encountered}")
print(f"DEBUG: Processing rate: {lines_processed/total_time:.1f} lines/sec")
print("=" * 60)
# Update status
self.status(_("Rendering completed: {} paths from {} lines in {:.1f}s").format(
paths_created, lines_processed, total_time))
except AlarmException as ae:
# This should never happen now, but just in case
total_time = time.time() - startTime
print(f"DEBUG: UNEXPECTED AlarmException after {total_time:.2f}s: {ae}")
print(f"DEBUG: Processed {lines_processed} lines before interruption")
self.status(_("Rendering interrupted after {:.1f}s ({} lines processed)").format(
total_time, lines_processed))
except Exception as e:
# Catch any other unexpected exceptions
total_time = time.time() - startTime
print(f"DEBUG: CRITICAL ERROR in drawPaths: {type(e).__name__}: {e}")
import traceback
traceback.print_exc()
self.status(_("Rendering failed after {:.1f}s: {}").format(total_time, str(e)))
finally:
# Always ensure GUI is updated
try:
self.update()
except:
pass
# ----------------------------------------------------------------------
# Create path for one g command - Optimized version
# ----------------------------------------------------------------------
def drawPath(self, block, cmds):
try:
self.cnc.motionStart(cmds)
xyz = self.cnc.motionPath()
self.cnc.motionEnd()
if not xyz:
return None
# Performance optimization: Simplify paths with many points
if len(xyz) > 500: # Very long paths
# Reduce point density while maintaining shape
step = max(1, len(xyz) // 200) # Target ~200 points max
simplified_xyz = xyz[::step]
# Always include first and last points
if simplified_xyz[-1] != xyz[-1]:
simplified_xyz.append(xyz[-1])
xyz = simplified_xyz
print(f"DEBUG: Simplified path from {len(xyz)*step} to {len(xyz)} points")
# Calculate statistics
self.cnc.pathLength(block, xyz)
if self.cnc.gcode in (1, 2, 3):
block.pathMargins(xyz)
self.cnc.pathMargins(block)
# Handle enable/disable state
if block.enable:
if self.cnc.gcode == 0 and self.draw_rapid:
xyz[0] = self._last
self._last = xyz[-1]
else:
if self.cnc.gcode == 0:
return None
# Convert to canvas coordinates
coords = self.plotCoords(xyz)
if not coords:
return None
# Create canvas line with appropriate style
if block.enable:
fill = block.color if block.color else ENABLE_COLOR
else:
fill = DISABLE_COLOR
if self.cnc.gcode == 0:
if self.draw_rapid:
return self.create_line(coords, fill=fill, width=0, dash=(4, 3))
else:
return None
elif self.draw_paths:
return self.create_line(coords, fill=fill, width=0, cap="projecting")
else:
return None
except MemoryError:
print("DEBUG: Memory error in drawPath - skipping")
raise
except Exception as e:
print(f"DEBUG: Error in drawPath: {e}")
return None
There is a rendering limit - by DRAW_TIME in CNCCanvas.py, that result in an error, and limitations, also at modern hardware.
Detailed logging of the rendering and an escape from the limitations would be great for complex and big data above 1mb.
DRAW_TIME = 180 # Maximum draw time permitted
`
----------------------------------------------------------------------
Draw the paths for the whole gcode file
----------------------------------------------------------------------
`