49 if field.startswith("color_"): |
49 if field.startswith("color_"): |
50 root_fieldname = "gcview_" + field |
50 root_fieldname = "gcview_" + field |
51 if hasattr(root, root_fieldname): |
51 if hasattr(root, root_fieldname): |
52 setattr(model, field, getattr(root, root_fieldname)) |
52 setattr(model, field, getattr(root, root_fieldname)) |
53 |
53 |
54 def recreate_platform(self, build_dimensions, circular): |
54 def recreate_platform(self, build_dimensions, circular, grid): |
55 self.platform = actors.Platform(build_dimensions, circular = circular) |
55 self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid) |
56 self.objects[0].model = self.platform |
56 self.objects[0].model = self.platform |
57 wx.CallAfter(self.Refresh) |
57 wx.CallAfter(self.Refresh) |
58 |
58 |
59 def set_gcview_params(self, path_width, path_height): |
59 def set_gcview_params(self, path_width, path_height): |
60 self.path_halfwidth = path_width / 2 |
60 self.path_halfwidth = path_width / 2 |
64 if isinstance(obj.model, actors.GcodeModel): |
64 if isinstance(obj.model, actors.GcodeModel): |
65 obj.model.set_path_size(self.path_halfwidth, self.path_halfheight) |
65 obj.model.set_path_size(self.path_halfwidth, self.path_halfheight) |
66 has_changed = True |
66 has_changed = True |
67 return has_changed |
67 return has_changed |
68 |
68 |
|
69 # E selected for Up because is above D |
|
70 LAYER_UP_KEYS = ord('U'), ord('E'), wx.WXK_UP |
|
71 LAYER_DOWN_KEYS = ord('D'), wx.WXK_DOWN |
|
72 ZOOM_IN_KEYS = wx.WXK_PAGEDOWN, 388, wx.WXK_RIGHT, ord('=') |
|
73 ZOOM_OUT_KEYS = wx.WXK_PAGEUP, 390, wx.WXK_LEFT, ord('-') |
|
74 FIT_KEYS = [ord('F')] |
|
75 CURRENT_LAYER_KEYS = [ord('C')] |
|
76 RESET_KEYS = [ord('R')] |
|
77 |
69 class GcodeViewPanel(wxGLPanel): |
78 class GcodeViewPanel(wxGLPanel): |
70 |
79 |
71 def __init__(self, parent, id = wx.ID_ANY, |
80 def __init__(self, parent, |
72 build_dimensions = None, realparent = None, |
81 build_dimensions = (200, 200, 100, 0, 0, 0), |
73 antialias_samples = 0): |
82 realparent = None, antialias_samples = 0): |
74 super(GcodeViewPanel, self).__init__(parent, id, wx.DefaultPosition, |
83 super().__init__(parent, wx.DefaultPosition, |
75 wx.DefaultSize, 0, |
84 wx.DefaultSize, 0, |
76 antialias_samples = antialias_samples) |
85 antialias_samples = antialias_samples) |
77 self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move) |
86 self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move) |
78 self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double) |
87 self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double) |
79 self.canvas.Bind(wx.EVT_KEY_DOWN, self.keypress) |
88 # self.canvas.Bind(wx.EVT_KEY_DOWN, self.keypress) |
|
89 # in Windows event inspector shows only EVT_CHAR_HOOK events |
|
90 self.canvas.Bind(wx.EVT_CHAR_HOOK, self.keypress) |
80 self.initialized = 0 |
91 self.initialized = 0 |
81 self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel) |
92 self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel) |
82 self.parent = realparent if realparent else parent |
93 self.parent = realparent or parent |
83 self.initpos = None |
94 self.initpos = None |
84 if build_dimensions: |
95 self.build_dimensions = build_dimensions |
85 self.build_dimensions = build_dimensions |
96 self.dist = max(self.build_dimensions[:2]) |
86 else: |
|
87 self.build_dimensions = [200, 200, 100, 0, 0, 0] |
|
88 self.dist = max(self.build_dimensions[0], self.build_dimensions[1]) |
|
89 self.basequat = [0, 0, 0, 1] |
97 self.basequat = [0, 0, 0, 1] |
90 self.mousepos = [0, 0] |
98 self.mousepos = [0, 0] |
91 |
99 |
92 def inject(self): |
100 def inject(self): |
93 l = self.parent.model.num_layers_to_draw |
101 l = self.parent.model.num_layers_to_draw |
94 filtered = [k for k, v in self.parent.model.layer_idxs_map.iteritems() if v == l] |
102 filtered = [k for k, v in self.parent.model.layer_idxs_map.items() if v == l] |
95 if filtered: |
103 if filtered: |
96 injector(self.parent.model.gcode, l, filtered[0]) |
104 injector(self.parent.model.gcode, l, filtered[0]) |
97 else: |
105 else: |
98 logging.error(_("Invalid layer for injection")) |
106 logging.error(_("Invalid layer for injection")) |
99 |
107 |
100 def editlayer(self): |
108 def editlayer(self): |
101 l = self.parent.model.num_layers_to_draw |
109 l = self.parent.model.num_layers_to_draw |
102 filtered = [k for k, v in self.parent.model.layer_idxs_map.iteritems() if v == l] |
110 filtered = [k for k, v in self.parent.model.layer_idxs_map.items() if v == l] |
103 if filtered: |
111 if filtered: |
104 injector_edit(self.parent.model.gcode, l, filtered[0]) |
112 injector_edit(self.parent.model.gcode, l, filtered[0]) |
105 else: |
113 else: |
106 logging.error(_("Invalid layer for edition")) |
114 logging.error(_("Invalid layer for edition")) |
107 |
115 |
108 def setlayercb(self, layer): |
116 def setlayercb(self, layer): |
109 pass |
117 pass |
110 |
118 |
111 def OnInitGL(self, *args, **kwargs): |
119 def OnInitGL(self, *args, **kwargs): |
112 super(GcodeViewPanel, self).OnInitGL(*args, **kwargs) |
120 super().OnInitGL(*args, **kwargs) |
113 if hasattr(self.parent, "filenames") and self.parent.filenames: |
121 filenames = getattr(self.parent, 'filenames', None) |
114 for filename in self.parent.filenames: |
122 if filenames: |
|
123 for filename in filenames: |
115 self.parent.load_file(filename) |
124 self.parent.load_file(filename) |
116 self.parent.autoplate() |
125 self.parent.autoplate() |
117 if hasattr(self.parent, "loadcb"): |
126 getattr(self.parent, 'loadcb', bool)() |
118 self.parent.loadcb() |
|
119 self.parent.filenames = None |
127 self.parent.filenames = None |
120 |
128 |
121 def create_objects(self): |
129 def create_objects(self): |
122 '''create opengl objects when opengl is initialized''' |
130 '''create opengl objects when opengl is initialized''' |
123 for obj in self.parent.objects: |
131 for obj in self.parent.objects: |
124 if obj.model and obj.model.loaded and not obj.model.initialized: |
132 if obj.model and obj.model.loaded and not obj.model.initialized: |
125 obj.model.init() |
133 obj.model.init() |
126 |
134 |
127 def update_object_resize(self): |
135 def update_object_resize(self): |
128 '''called when the window recieves only if opengl is initialized''' |
136 '''called when the window receives only if opengl is initialized''' |
129 pass |
137 pass |
130 |
138 |
131 def draw_objects(self): |
139 def draw_objects(self): |
132 '''called in the middle of ondraw after the buffer has been cleared''' |
140 '''called in the middle of ondraw after the buffer has been cleared''' |
133 self.create_objects() |
141 self.create_objects() |
214 self.parent.model.num_layers_to_draw = new_layer |
222 self.parent.model.num_layers_to_draw = new_layer |
215 self.parent.setlayercb(new_layer) |
223 self.parent.setlayercb(new_layer) |
216 wx.CallAfter(self.Refresh) |
224 wx.CallAfter(self.Refresh) |
217 |
225 |
218 def layerdown(self): |
226 def layerdown(self): |
219 if not hasattr(self.parent, "model") or not self.parent.model: |
227 if not getattr(self.parent, 'model', False): |
220 return |
228 return |
221 current_layer = self.parent.model.num_layers_to_draw |
229 current_layer = self.parent.model.num_layers_to_draw |
222 new_layer = max(1, current_layer - 1) |
230 new_layer = max(1, current_layer - 1) |
223 self.parent.model.num_layers_to_draw = new_layer |
231 self.parent.model.num_layers_to_draw = new_layer |
224 self.parent.setlayercb(new_layer) |
232 self.parent.setlayercb(new_layer) |
225 wx.CallAfter(self.Refresh) |
233 wx.CallAfter(self.Refresh) |
226 |
234 |
|
235 wheelTimestamp = None |
227 def handle_wheel(self, event): |
236 def handle_wheel(self, event): |
|
237 if self.wheelTimestamp == event.Timestamp: |
|
238 # filter duplicate event delivery in Ubuntu, Debian issue #1110 |
|
239 return |
|
240 |
|
241 self.wheelTimestamp = event.Timestamp |
|
242 |
228 delta = event.GetWheelRotation() |
243 delta = event.GetWheelRotation() |
229 factor = 1.05 |
244 factor = 1.05 |
230 if event.ControlDown(): |
245 if event.ControlDown(): |
231 factor = 1.02 |
246 factor = 1.02 |
232 if hasattr(self.parent, "model") and event.ShiftDown(): |
247 if hasattr(self.parent, "model") and event.ShiftDown(): |
267 glScalef(ratio, ratio, 1) |
282 glScalef(ratio, ratio, 1) |
268 glTranslatef(center_x, center_y, 0) |
283 glTranslatef(center_x, center_y, 0) |
269 wx.CallAfter(self.Refresh) |
284 wx.CallAfter(self.Refresh) |
270 |
285 |
271 def keypress(self, event): |
286 def keypress(self, event): |
272 """gets keypress events and moves/rotates acive shape""" |
287 """gets keypress events and moves/rotates active shape""" |
273 step = 1.1 |
288 if event.HasModifiers(): |
274 if event.ControlDown(): |
289 # let alt+c bubble up |
275 step = 1.05 |
290 event.Skip() |
276 kup = [85, 315] # Up keys |
291 return |
277 kdo = [68, 317] # Down Keys |
292 step = event.ControlDown() and 1.05 or 1.1 |
278 kzi = [wx.WXK_PAGEDOWN, 388, 316, 61] # Zoom In Keys |
|
279 kzo = [wx.WXK_PAGEUP, 390, 314, 45] # Zoom Out Keys |
|
280 kfit = [70] # Fit to print keys |
|
281 kshowcurrent = [67] # Show only current layer keys |
|
282 kreset = [82] # Reset keys |
|
283 key = event.GetKeyCode() |
293 key = event.GetKeyCode() |
284 if key in kup: |
294 if key in LAYER_UP_KEYS: |
285 self.layerup() |
295 self.layerup() |
286 if key in kdo: |
296 return # prevent shifting focus to other controls |
|
297 elif key in LAYER_DOWN_KEYS: |
287 self.layerdown() |
298 self.layerdown() |
288 x, y, _ = self.mouse_to_3d(self.width / 2, self.height / 2) |
299 return |
289 if key in kzi: |
300 # x, y, _ = self.mouse_to_3d(self.width / 2, self.height / 2) |
|
301 elif key in ZOOM_IN_KEYS: |
290 self.zoom_to_center(step) |
302 self.zoom_to_center(step) |
291 if key in kzo: |
303 return |
|
304 elif key in ZOOM_OUT_KEYS: |
292 self.zoom_to_center(1 / step) |
305 self.zoom_to_center(1 / step) |
293 if key in kfit: |
306 return |
|
307 elif key in FIT_KEYS: |
294 self.fit() |
308 self.fit() |
295 if key in kshowcurrent: |
309 elif key in CURRENT_LAYER_KEYS: |
296 if not self.parent.model or not self.parent.model.loaded: |
310 if not self.parent.model or not self.parent.model.loaded: |
297 return |
311 return |
298 self.parent.model.only_current = not self.parent.model.only_current |
312 self.parent.model.only_current = not self.parent.model.only_current |
299 wx.CallAfter(self.Refresh) |
313 wx.CallAfter(self.Refresh) |
300 if key in kreset: |
314 elif key in RESET_KEYS: |
301 self.resetview() |
315 self.resetview() |
302 event.Skip() |
316 event.Skip() |
303 |
317 |
304 def resetview(self): |
318 def resetview(self): |
305 self.canvas.SetCurrent(self.context) |
319 self.canvas.SetCurrent(self.context) |
306 self.reset_mview(0.9) |
320 self.reset_mview(0.9) |
307 self.basequat = [0, 0, 0, 1] |
321 self.basequat = [0, 0, 0, 1] |
308 wx.CallAfter(self.Refresh) |
322 wx.CallAfter(self.Refresh) |
309 |
323 |
310 class GCObject(object): |
324 class GCObject: |
311 |
325 |
312 def __init__(self, model): |
326 def __init__(self, model): |
313 self.offsets = [0, 0, 0] |
327 self.offsets = [0, 0, 0] |
314 self.centeroffset = [0, 0, 0] |
328 self.centeroffset = [0, 0, 0] |
315 self.rot = 0 |
329 self.rot = 0 |
316 self.curlayer = 0.0 |
330 self.curlayer = 0.0 |
317 self.scale = [1.0, 1.0, 1.0] |
331 self.scale = [1.0, 1.0, 1.0] |
318 self.model = model |
332 self.model = model |
319 |
333 |
320 class GcodeViewLoader(object): |
334 class GcodeViewLoader: |
321 |
335 |
322 path_halfwidth = 0.2 |
336 path_halfwidth = 0.2 |
323 path_halfheight = 0.15 |
337 path_halfheight = 0.15 |
324 |
338 |
325 def addfile_perlayer(self, gcode = None, showall = False): |
339 def addfile_perlayer(self, gcode = None, showall = False): |
330 self.objects[-1].model = self.model |
344 self.objects[-1].model = self.model |
331 if self.root: |
345 if self.root: |
332 set_model_colors(self.model, self.root) |
346 set_model_colors(self.model, self.root) |
333 if gcode is not None: |
347 if gcode is not None: |
334 generator = self.model.load_data(gcode) |
348 generator = self.model.load_data(gcode) |
335 generator_output = generator.next() |
349 generator_output = next(generator) |
336 while generator_output is not None: |
350 while generator_output is not None: |
337 yield generator_output |
351 yield generator_output |
338 generator_output = generator.next() |
352 generator_output = next(generator) |
339 wx.CallAfter(self.Refresh) |
353 wx.CallAfter(self.Refresh) |
340 yield None |
354 yield None |
341 |
355 |
342 def addfile(self, gcode = None, showall = False): |
356 def addfile(self, gcode = None, showall = False): |
343 generator = self.addfile_perlayer(gcode, showall) |
357 generator = self.addfile_perlayer(gcode, showall) |
344 while generator.next() is not None: |
358 while next(generator) is not None: |
345 continue |
359 continue |
346 |
360 |
347 def set_gcview_params(self, path_width, path_height): |
361 def set_gcview_params(self, path_width, path_height): |
348 return set_gcview_params(self, path_width, path_height) |
362 return set_gcview_params(self, path_width, path_height) |
349 |
363 |
350 class GcodeViewMainWrapper(GcodeViewLoader): |
364 from printrun.gviz import BaseViz |
351 |
365 class GcodeViewMainWrapper(GcodeViewLoader, BaseViz): |
352 def __init__(self, parent, build_dimensions, root, circular, antialias_samples): |
366 |
|
367 def __init__(self, parent, build_dimensions, root, circular, antialias_samples, grid): |
353 self.root = root |
368 self.root = root |
354 self.glpanel = GcodeViewPanel(parent, realparent = self, |
369 self.glpanel = GcodeViewPanel(parent, realparent = self, |
355 build_dimensions = build_dimensions, |
370 build_dimensions = build_dimensions, |
356 antialias_samples = antialias_samples) |
371 antialias_samples = antialias_samples) |
357 self.glpanel.SetMinSize((150, 150)) |
372 self.glpanel.SetMinSize((150, 150)) |
359 self.glpanel.color_background = self.root.gcview_color_background |
374 self.glpanel.color_background = self.root.gcview_color_background |
360 self.clickcb = None |
375 self.clickcb = None |
361 self.widget = self.glpanel |
376 self.widget = self.glpanel |
362 self.refresh_timer = wx.CallLater(100, self.Refresh) |
377 self.refresh_timer = wx.CallLater(100, self.Refresh) |
363 self.p = self # Hack for backwards compatibility with gviz API |
378 self.p = self # Hack for backwards compatibility with gviz API |
364 self.platform = actors.Platform(build_dimensions, circular = circular) |
379 self.grid = grid |
|
380 self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid) |
365 self.model = None |
381 self.model = None |
366 self.objects = [GCObject(self.platform), GCObject(None)] |
382 self.objects = [GCObject(self.platform), GCObject(None)] |
367 |
383 |
368 def __getattr__(self, name): |
384 def __getattr__(self, name): |
369 return getattr(self.glpanel, name) |
385 return getattr(self.glpanel, name) |
|
386 |
|
387 def on_settings_change(self, changed_settings): |
|
388 if self.model: |
|
389 for s in changed_settings: |
|
390 if s.name.startswith('gcview_color_'): |
|
391 self.model.update_colors() |
|
392 break |
370 |
393 |
371 def set_current_gline(self, gline): |
394 def set_current_gline(self, gline): |
372 if gline.is_move and gline.gcview_end_vertex is not None \ |
395 if gline.is_move and gline.gcview_end_vertex is not None \ |
373 and self.model and self.model.loaded: |
396 and self.model and self.model.loaded: |
374 self.model.printed_until = gline.gcview_end_vertex |
397 self.model.printed_until = gline.gcview_end_vertex |
375 if not self.refresh_timer.IsRunning(): |
398 if not self.refresh_timer.IsRunning(): |
376 self.refresh_timer.Start() |
399 self.refresh_timer.Start() |
377 |
400 |
378 def recreate_platform(self, build_dimensions, circular): |
401 def recreate_platform(self, build_dimensions, circular, grid): |
379 return recreate_platform(self, build_dimensions, circular) |
402 return recreate_platform(self, build_dimensions, circular, grid) |
380 |
|
381 def addgcodehighlight(self, *a): |
|
382 pass |
|
383 |
403 |
384 def setlayer(self, layer): |
404 def setlayer(self, layer): |
385 if layer in self.model.layer_idxs_map: |
405 if layer in self.model.layer_idxs_map: |
386 viz_layer = self.model.layer_idxs_map[layer] |
406 viz_layer = self.model.layer_idxs_map[layer] |
387 self.parent.model.num_layers_to_draw = viz_layer |
407 self.parent.model.num_layers_to_draw = viz_layer |
396 '''A simple class for using OpenGL with wxPython.''' |
416 '''A simple class for using OpenGL with wxPython.''' |
397 |
417 |
398 def __init__(self, parent, ID, title, build_dimensions, objects = None, |
418 def __init__(self, parent, ID, title, build_dimensions, objects = None, |
399 pos = wx.DefaultPosition, size = wx.DefaultSize, |
419 pos = wx.DefaultPosition, size = wx.DefaultSize, |
400 style = wx.DEFAULT_FRAME_STYLE, root = None, circular = False, |
420 style = wx.DEFAULT_FRAME_STYLE, root = None, circular = False, |
401 antialias_samples = 0): |
421 antialias_samples = 0, |
|
422 grid = (1, 10)): |
402 GvizBaseFrame.__init__(self, parent, ID, title, |
423 GvizBaseFrame.__init__(self, parent, ID, title, |
403 pos, size, style) |
424 pos, size, style) |
404 self.root = root |
425 self.root = root |
405 |
426 |
406 panel, vbox = self.create_base_ui() |
427 panel, vbox = self.create_base_ui() |
407 |
428 |
408 self.refresh_timer = wx.CallLater(100, self.Refresh) |
429 self.refresh_timer = wx.CallLater(100, self.Refresh) |
409 self.p = self # Hack for backwards compatibility with gviz API |
430 self.p = self # Hack for backwards compatibility with gviz API |
410 self.clonefrom = objects |
431 self.clonefrom = objects |
411 self.platform = actors.Platform(build_dimensions, circular = circular) |
432 self.platform = actors.Platform(build_dimensions, circular = circular, grid = grid) |
412 if objects: |
433 self.model = objects[1].model if objects else None |
413 self.model = objects[1].model |
|
414 else: |
|
415 self.model = None |
|
416 self.objects = [GCObject(self.platform), GCObject(None)] |
434 self.objects = [GCObject(self.platform), GCObject(None)] |
417 |
435 |
418 fit_image = wx.Image(imagefile('fit.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap() |
436 fit_image = wx.Image(imagefile('fit.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap() |
419 self.toolbar.InsertLabelTool(6, 8, " " + _("Fit to plate"), fit_image, |
437 self.toolbar.InsertTool(6, 8, " " + _("Fit to plate"), fit_image, |
420 shortHelp = _("Fit to plate [F]"), |
438 shortHelp = _("Fit to plate [F]"), |
421 longHelp = '') |
439 longHelp = '') |
422 self.toolbar.Realize() |
440 self.toolbar.Realize() |
423 self.glpanel = GcodeViewPanel(panel, |
441 self.glpanel = GcodeViewPanel(panel, |
424 build_dimensions = build_dimensions, |
442 build_dimensions = build_dimensions, |