printrun-src/printrun/gcview.py

changeset 46
cce0af6351f0
parent 15
0bbb006204fc
--- a/printrun-src/printrun/gcview.py	Tue Jan 19 20:45:09 2021 +0100
+++ b/printrun-src/printrun/gcview.py	Wed Jan 20 10:15:13 2021 +0100
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # This file is part of the Printrun suite.
 #
@@ -51,8 +51,8 @@
             if hasattr(root, root_fieldname):
                 setattr(model, field, getattr(root, root_fieldname))
 
-def recreate_platform(self, build_dimensions, circular):
-    self.platform = actors.Platform(build_dimensions, circular = circular)
+def recreate_platform(self, build_dimensions, circular, grid):
+    self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid)
     self.objects[0].model = self.platform
     wx.CallAfter(self.Refresh)
 
@@ -66,32 +66,40 @@
             has_changed = True
     return has_changed
 
+# E selected for Up because is above D
+LAYER_UP_KEYS = ord('U'), ord('E'), wx.WXK_UP
+LAYER_DOWN_KEYS = ord('D'), wx.WXK_DOWN
+ZOOM_IN_KEYS = wx.WXK_PAGEDOWN, 388, wx.WXK_RIGHT, ord('=')
+ZOOM_OUT_KEYS = wx.WXK_PAGEUP, 390, wx.WXK_LEFT, ord('-')
+FIT_KEYS = [ord('F')]
+CURRENT_LAYER_KEYS = [ord('C')]
+RESET_KEYS = [ord('R')]
+
 class GcodeViewPanel(wxGLPanel):
 
-    def __init__(self, parent, id = wx.ID_ANY,
-                 build_dimensions = None, realparent = None,
-                 antialias_samples = 0):
-        super(GcodeViewPanel, self).__init__(parent, id, wx.DefaultPosition,
+    def __init__(self, parent,
+                 build_dimensions = (200, 200, 100, 0, 0, 0),
+                 realparent = None, antialias_samples = 0):
+        super().__init__(parent, wx.DefaultPosition,
                                              wx.DefaultSize, 0,
                                              antialias_samples = antialias_samples)
         self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move)
         self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double)
-        self.canvas.Bind(wx.EVT_KEY_DOWN, self.keypress)
+        # self.canvas.Bind(wx.EVT_KEY_DOWN, self.keypress)
+        # in Windows event inspector shows only EVT_CHAR_HOOK events
+        self.canvas.Bind(wx.EVT_CHAR_HOOK, self.keypress)
         self.initialized = 0
         self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel)
-        self.parent = realparent if realparent else parent
+        self.parent = realparent or parent
         self.initpos = None
-        if build_dimensions:
-            self.build_dimensions = build_dimensions
-        else:
-            self.build_dimensions = [200, 200, 100, 0, 0, 0]
-        self.dist = max(self.build_dimensions[0], self.build_dimensions[1])
+        self.build_dimensions = build_dimensions
+        self.dist = max(self.build_dimensions[:2])
         self.basequat = [0, 0, 0, 1]
         self.mousepos = [0, 0]
 
     def inject(self):
         l = self.parent.model.num_layers_to_draw
-        filtered = [k for k, v in self.parent.model.layer_idxs_map.iteritems() if v == l]
+        filtered = [k for k, v in self.parent.model.layer_idxs_map.items() if v == l]
         if filtered:
             injector(self.parent.model.gcode, l, filtered[0])
         else:
@@ -99,7 +107,7 @@
 
     def editlayer(self):
         l = self.parent.model.num_layers_to_draw
-        filtered = [k for k, v in self.parent.model.layer_idxs_map.iteritems() if v == l]
+        filtered = [k for k, v in self.parent.model.layer_idxs_map.items() if v == l]
         if filtered:
             injector_edit(self.parent.model.gcode, l, filtered[0])
         else:
@@ -109,13 +117,13 @@
         pass
 
     def OnInitGL(self, *args, **kwargs):
-        super(GcodeViewPanel, self).OnInitGL(*args, **kwargs)
-        if hasattr(self.parent, "filenames") and self.parent.filenames:
-            for filename in self.parent.filenames:
+        super().OnInitGL(*args, **kwargs)
+        filenames = getattr(self.parent, 'filenames', None)
+        if filenames:
+            for filename in filenames:
                 self.parent.load_file(filename)
             self.parent.autoplate()
-            if hasattr(self.parent, "loadcb"):
-                self.parent.loadcb()
+            getattr(self.parent, 'loadcb', bool)()
             self.parent.filenames = None
 
     def create_objects(self):
@@ -125,7 +133,7 @@
                 obj.model.init()
 
     def update_object_resize(self):
-        '''called when the window recieves only if opengl is initialized'''
+        '''called when the window receives only if opengl is initialized'''
         pass
 
     def draw_objects(self):
@@ -142,9 +150,14 @@
 
         for obj in self.parent.objects:
             if not obj.model \
-               or not obj.model.loaded \
-               or not obj.model.initialized:
+               or not obj.model.loaded:
                 continue
+            # Skip (comment out) initialized check, which safely causes empty 
+            # model during progressive load. This can cause exceptions/garbage
+            # render, but seems fine for now
+            # May need to lock init() and draw_objects() together
+            # if not obj.model.initialized:
+            #     continue
             glPushMatrix()
             glTranslatef(*(obj.offsets))
             glRotatef(obj.rot, 0.0, 0.0, 1.0)
@@ -175,8 +188,7 @@
         return mvmat
 
     def double(self, event):
-        if hasattr(self.parent, "clickcb") and self.parent.clickcb:
-            self.parent.clickcb(event)
+        getattr(self.parent, 'clickcb', bool)(event)
 
     def move(self, event):
         """react to mouse actions:
@@ -188,22 +200,18 @@
             self.canvas.SetFocus()
             event.Skip()
             return
-        if event.Dragging() and event.LeftIsDown():
-            self.handle_rotation(event)
-        elif event.Dragging() and event.RightIsDown():
-            self.handle_translation(event)
-        elif event.LeftUp():
+        if event.Dragging():
+            if event.LeftIsDown():
+                self.handle_rotation(event)
+            elif event.RightIsDown():
+                self.handle_translation(event)
+            self.Refresh(False)
+        elif event.LeftUp() or event.RightUp():
             self.initpos = None
-        elif event.RightUp():
-            self.initpos = None
-        else:
-            event.Skip()
-            return
         event.Skip()
-        wx.CallAfter(self.Refresh)
 
     def layerup(self):
-        if not hasattr(self.parent, "model") or not self.parent.model:
+        if not getattr(self.parent, 'model', False):
             return
         max_layers = self.parent.model.max_layers
         current_layer = self.parent.model.num_layers_to_draw
@@ -216,7 +224,7 @@
         wx.CallAfter(self.Refresh)
 
     def layerdown(self):
-        if not hasattr(self.parent, "model") or not self.parent.model:
+        if not getattr(self.parent, 'model', False):
             return
         current_layer = self.parent.model.num_layers_to_draw
         new_layer = max(1, current_layer - 1)
@@ -224,7 +232,14 @@
         self.parent.setlayercb(new_layer)
         wx.CallAfter(self.Refresh)
 
+    wheelTimestamp = None
     def handle_wheel(self, event):
+        if self.wheelTimestamp == event.Timestamp:
+            # filter duplicate event delivery in Ubuntu, Debian issue #1110
+            return  
+
+        self.wheelTimestamp = event.Timestamp
+
         delta = event.GetWheelRotation()
         factor = 1.05
         if event.ControlDown():
@@ -237,7 +252,7 @@
                 if delta > 0: self.layerup()
                 else: self.layerdown()
             return
-        x, y = event.GetPositionTuple()
+        x, y = event.GetPosition()
         x, y, _ = self.mouse_to_3d(x, y)
         if delta > 0:
             self.zoom(factor, (x, y))
@@ -269,35 +284,34 @@
         wx.CallAfter(self.Refresh)
 
     def keypress(self, event):
-        """gets keypress events and moves/rotates acive shape"""
-        step = 1.1
-        if event.ControlDown():
-            step = 1.05
-        kup = [85, 315]               # Up keys
-        kdo = [68, 317]               # Down Keys
-        kzi = [wx.WXK_PAGEDOWN, 388, 316, 61]        # Zoom In Keys
-        kzo = [wx.WXK_PAGEUP, 390, 314, 45]       # Zoom Out Keys
-        kfit = [70]       # Fit to print keys
-        kshowcurrent = [67]       # Show only current layer keys
-        kreset = [82]       # Reset keys
+        """gets keypress events and moves/rotates active shape"""
+        if event.HasModifiers():
+            # let alt+c bubble up
+            event.Skip()
+            return
+        step = event.ControlDown() and 1.05 or 1.1
         key = event.GetKeyCode()
-        if key in kup:
+        if key in LAYER_UP_KEYS:
             self.layerup()
-        if key in kdo:
+            return  # prevent shifting focus to other controls
+        elif key in LAYER_DOWN_KEYS:
             self.layerdown()
-        x, y, _ = self.mouse_to_3d(self.width / 2, self.height / 2)
-        if key in kzi:
+            return
+        # x, y, _ = self.mouse_to_3d(self.width / 2, self.height / 2)
+        elif key in ZOOM_IN_KEYS:
             self.zoom_to_center(step)
-        if key in kzo:
+            return
+        elif key in ZOOM_OUT_KEYS:
             self.zoom_to_center(1 / step)
-        if key in kfit:
+            return
+        elif key in FIT_KEYS:
             self.fit()
-        if key in kshowcurrent:
+        elif key in CURRENT_LAYER_KEYS:
             if not self.parent.model or not self.parent.model.loaded:
                 return
             self.parent.model.only_current = not self.parent.model.only_current
             wx.CallAfter(self.Refresh)
-        if key in kreset:
+        elif key in RESET_KEYS:
             self.resetview()
         event.Skip()
 
@@ -307,7 +321,7 @@
         self.basequat = [0, 0, 0, 1]
         wx.CallAfter(self.Refresh)
 
-class GCObject(object):
+class GCObject:
 
     def __init__(self, model):
         self.offsets = [0, 0, 0]
@@ -317,7 +331,7 @@
         self.scale = [1.0, 1.0, 1.0]
         self.model = model
 
-class GcodeViewLoader(object):
+class GcodeViewLoader:
 
     path_halfwidth = 0.2
     path_halfheight = 0.15
@@ -332,24 +346,25 @@
             set_model_colors(self.model, self.root)
         if gcode is not None:
             generator = self.model.load_data(gcode)
-            generator_output = generator.next()
+            generator_output = next(generator)
             while generator_output is not None:
                 yield generator_output
-                generator_output = generator.next()
+                generator_output = next(generator)
         wx.CallAfter(self.Refresh)
         yield None
 
     def addfile(self, gcode = None, showall = False):
         generator = self.addfile_perlayer(gcode, showall)
-        while generator.next() is not None:
+        while next(generator) is not None:
             continue
 
     def set_gcview_params(self, path_width, path_height):
         return set_gcview_params(self, path_width, path_height)
 
-class GcodeViewMainWrapper(GcodeViewLoader):
+from printrun.gviz import BaseViz
+class GcodeViewMainWrapper(GcodeViewLoader, BaseViz):
 
-    def __init__(self, parent, build_dimensions, root, circular, antialias_samples):
+    def __init__(self, parent, build_dimensions, root, circular, antialias_samples, grid):
         self.root = root
         self.glpanel = GcodeViewPanel(parent, realparent = self,
                                       build_dimensions = build_dimensions,
@@ -361,13 +376,21 @@
         self.widget = self.glpanel
         self.refresh_timer = wx.CallLater(100, self.Refresh)
         self.p = self  # Hack for backwards compatibility with gviz API
-        self.platform = actors.Platform(build_dimensions, circular = circular)
+        self.grid = grid
+        self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid)
         self.model = None
         self.objects = [GCObject(self.platform), GCObject(None)]
 
     def __getattr__(self, name):
         return getattr(self.glpanel, name)
 
+    def on_settings_change(self, changed_settings):
+        if self.model:
+            for s in changed_settings:
+                if s.name.startswith('gcview_color_'):
+                    self.model.update_colors()
+                    break
+
     def set_current_gline(self, gline):
         if gline.is_move and gline.gcview_end_vertex is not None \
            and self.model and self.model.loaded:
@@ -375,11 +398,8 @@
             if not self.refresh_timer.IsRunning():
                 self.refresh_timer.Start()
 
-    def recreate_platform(self, build_dimensions, circular):
-        return recreate_platform(self, build_dimensions, circular)
-
-    def addgcodehighlight(self, *a):
-        pass
+    def recreate_platform(self, build_dimensions, circular, grid):
+        return recreate_platform(self, build_dimensions, circular, grid)
 
     def setlayer(self, layer):
         if layer in self.model.layer_idxs_map:
@@ -398,7 +418,8 @@
     def __init__(self, parent, ID, title, build_dimensions, objects = None,
                  pos = wx.DefaultPosition, size = wx.DefaultSize,
                  style = wx.DEFAULT_FRAME_STYLE, root = None, circular = False,
-                 antialias_samples = 0):
+                 antialias_samples = 0,
+                 grid = (1, 10)):
         GvizBaseFrame.__init__(self, parent, ID, title,
                                pos, size, style)
         self.root = root
@@ -408,15 +429,12 @@
         self.refresh_timer = wx.CallLater(100, self.Refresh)
         self.p = self  # Hack for backwards compatibility with gviz API
         self.clonefrom = objects
-        self.platform = actors.Platform(build_dimensions, circular = circular)
-        if objects:
-            self.model = objects[1].model
-        else:
-            self.model = None
+        self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid)
+        self.model = objects[1].model if objects else None
         self.objects = [GCObject(self.platform), GCObject(None)]
 
         fit_image = wx.Image(imagefile('fit.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
-        self.toolbar.InsertLabelTool(6, 8, " " + _("Fit to plate"), fit_image,
+        self.toolbar.InsertTool(6, 8, " " + _("Fit to plate"), fit_image,
                                      shortHelp = _("Fit to plate [F]"),
                                      longHelp = '')
         self.toolbar.Realize()
@@ -441,7 +459,7 @@
 
     def update_status(self, extra):
         layer = self.model.num_layers_to_draw
-        filtered = [k for k, v in self.model.layer_idxs_map.iteritems() if v == layer]
+        filtered = [k for k, v in self.model.layer_idxs_map.items() if v == layer]
         if filtered:
             true_layer = filtered[0]
             z = self.model.gcode.all_layers[true_layer].z
@@ -465,8 +483,8 @@
             if not self.refresh_timer.IsRunning():
                 self.refresh_timer.Start()
 
-    def recreate_platform(self, build_dimensions, circular):
-        return recreate_platform(self, build_dimensions, circular)
+    def recreate_platform(self, build_dimensions, circular, grid):
+        return recreate_platform(self, build_dimensions, circular, grid)
 
     def addfile(self, gcode = None):
         if self.clonefrom:

mercurial