chiark / gitweb /
Follow the object when centering.
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import numpy
6 import time
7 import os
8 import traceback
9 import threading
10 import math
11 import platform
12
13 import OpenGL
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
17
18 from Cura.gui import printWindow
19 from Cura.util import profile
20 from Cura.util import meshLoader
21 from Cura.util import objectScene
22 from Cura.util import resources
23 from Cura.util import sliceEngine
24 from Cura.util import machineCom
25 from Cura.util import removableStorage
26 from Cura.util import gcodeInterpreter
27 from Cura.gui.util import previewTools
28 from Cura.gui.util import opengl
29 from Cura.gui.util import openglGui
30 from Cura.gui.tools import youmagineGui
31 from Cura.gui.tools import imageToMesh
32
33 class SceneView(openglGui.glGuiPanel):
34         def __init__(self, parent):
35                 super(SceneView, self).__init__(parent)
36
37                 self._yaw = 30
38                 self._pitch = 60
39                 self._zoom = 300
40                 self._scene = objectScene.Scene()
41                 self._gcode = None
42                 self._gcodeVBOs = []
43                 self._gcodeFilename = None
44                 self._gcodeLoadThread = None
45                 self._objectShader = None
46                 self._objectLoadShader = None
47                 self._focusObj = None
48                 self._selectedObj = None
49                 self._objColors = [None,None,None,None]
50                 self._mouseX = -1
51                 self._mouseY = -1
52                 self._mouseState = None
53                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
54                 self._animView = None
55                 self._animZoom = None
56                 self._platformMesh = {}
57                 self._isSimpleMode = True
58                 self._usbPrintMonitor = printWindow.printProcessMonitor(lambda : self._queueRefresh())
59
60                 self._viewport = None
61                 self._modelMatrix = None
62                 self._projMatrix = None
63                 self.tempMatrix = None
64
65                 self.openFileButton      = openglGui.glButton(self, 4, _("Load"), (0,0), self.showLoadModel)
66                 self.printButton         = openglGui.glButton(self, 6, _("Print"), (1,0), self.OnPrintButton)
67                 self.printButton.setDisabled(True)
68
69                 group = []
70                 self.rotateToolButton = openglGui.glRadioButton(self, 8, _("Rotate"), (0,-1), group, self.OnToolSelect)
71                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, _("Scale"), (1,-1), group, self.OnToolSelect)
72                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, _("Mirror"), (2,-1), group, self.OnToolSelect)
73
74                 self.resetRotationButton = openglGui.glButton(self, 12, _("Reset"), (0,-2), self.OnRotateReset)
75                 self.layFlatButton       = openglGui.glButton(self, 16, _("Lay flat"), (0,-3), self.OnLayFlat)
76
77                 self.resetScaleButton    = openglGui.glButton(self, 13, _("Reset"), (1,-2), self.OnScaleReset)
78                 self.scaleMaxButton      = openglGui.glButton(self, 17, _("To max"), (1,-3), self.OnScaleMax)
79
80                 self.mirrorXButton       = openglGui.glButton(self, 14, _("Mirror X"), (2,-2), lambda button: self.OnMirror(0))
81                 self.mirrorYButton       = openglGui.glButton(self, 18, _("Mirror Y"), (2,-3), lambda button: self.OnMirror(1))
82                 self.mirrorZButton       = openglGui.glButton(self, 22, _("Mirror Z"), (2,-4), lambda button: self.OnMirror(2))
83
84                 self.rotateToolButton.setExpandArrow(True)
85                 self.scaleToolButton.setExpandArrow(True)
86                 self.mirrorToolButton.setExpandArrow(True)
87
88                 self.scaleForm = openglGui.glFrame(self, (2, -2))
89                 openglGui.glGuiLayoutGrid(self.scaleForm)
90                 openglGui.glLabel(self.scaleForm, _("Scale X"), (0,0))
91                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
92                 openglGui.glLabel(self.scaleForm, _("Scale Y"), (0,1))
93                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
94                 openglGui.glLabel(self.scaleForm, _("Scale Z"), (0,2))
95                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
96                 openglGui.glLabel(self.scaleForm, _("Size X (mm)"), (0,4))
97                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
98                 openglGui.glLabel(self.scaleForm, _("Size Y (mm)"), (0,5))
99                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
100                 openglGui.glLabel(self.scaleForm, _("Size Z (mm)"), (0,6))
101                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
102                 openglGui.glLabel(self.scaleForm, _("Uniform scale"), (0,8))
103                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
104
105                 self.viewSelection = openglGui.glComboButton(self, _("View mode"), [7,19,11,15,23], [_("Normal"), _("Overhang"), _("Transparent"), _("X-Ray"), _("Layers")], (-1,0), self.OnViewChange)
106                 self.layerSelect = openglGui.glSlider(self, 10000, 0, 1, (-1,-2), lambda : self.QueueRefresh())
107
108                 self.youMagineButton = openglGui.glButton(self, 26, _("Share on YouMagine"), (2,0), lambda button: youmagineGui.youmagineManager(self.GetTopLevelParent(), self._scene))
109                 self.youMagineButton.setDisabled(True)
110
111                 self.notification = openglGui.glNotification(self, (0, 0))
112
113                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
114                 self._sceneUpdateTimer = wx.Timer(self)
115                 self.Bind(wx.EVT_TIMER, self._onRunSlicer, self._sceneUpdateTimer)
116                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
117                 self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
118
119                 self.OnViewChange()
120                 self.OnToolSelect(0)
121                 self.updateToolButtons()
122                 self.updateProfileToControls()
123
124         def loadGCodeFile(self, filename):
125                 self.OnDeleteAll(None)
126                 if self._gcode is not None:
127                         self._gcode = None
128                         for layerVBOlist in self._gcodeVBOs:
129                                 for vbo in layerVBOlist:
130                                         self.glReleaseList.append(vbo)
131                         self._gcodeVBOs = []
132                 self._gcode = gcodeInterpreter.gcode()
133                 self._gcodeFilename = filename
134                 self.printButton.setBottomText('')
135                 self.viewSelection.setValue(4)
136                 self.printButton.setDisabled(False)
137                 self.youMagineButton.setDisabled(True)
138                 self.OnViewChange()
139
140         def loadSceneFiles(self, filenames):
141                 self.youMagineButton.setDisabled(False)
142                 #if self.viewSelection.getValue() == 4:
143                 #       self.viewSelection.setValue(0)
144                 #       self.OnViewChange()
145                 self.loadScene(filenames)
146
147         def loadFiles(self, filenames):
148                 mainWindow = self.GetParent().GetParent().GetParent()
149                 # only one GCODE file can be active
150                 # so if single gcode file, process this
151                 # otherwise ignore all gcode files
152                 gcodeFilename = None
153                 if len(filenames) == 1:
154                         filename = filenames[0]
155                         ext = os.path.splitext(filename)[1].lower()
156                         if ext == '.g' or ext == '.gcode':
157                                 gcodeFilename = filename
158                                 mainWindow.addToModelMRU(filename)
159                 if gcodeFilename is not None:
160                         self.loadGCodeFile(gcodeFilename)
161                 else:
162                         # process directories and special file types
163                         # and keep scene files for later processing
164                         scene_filenames = []
165                         ignored_types = dict()
166                         # use file list as queue
167                         # pop first entry for processing and append new files at end
168                         while filenames:
169                                 filename = filenames.pop(0)
170                                 if os.path.isdir(filename):
171                                         # directory: queue all included files and directories
172                                         filenames.extend(os.path.join(filename, f) for f in os.listdir(filename))
173                                 else:
174                                         ext = os.path.splitext(filename)[1].lower()
175                                         if ext == '.ini':
176                                                 profile.loadProfile(filename)
177                                                 mainWindow.addToProfileMRU(filename)
178                                         elif ext in meshLoader.loadSupportedExtensions() or ext in imageToMesh.supportedExtensions():
179                                                 scene_filenames.append(filename)
180                                                 mainWindow.addToModelMRU(filename)
181                                         else:
182                                                 ignored_types[ext] = 1
183                         if ignored_types:
184                                 ignored_types = ignored_types.keys()
185                                 ignored_types.sort()
186                                 self.notification.message("ignored: " + " ".join("*" + type for type in ignored_types))
187                         mainWindow.updateProfileToAllControls()
188                         # now process all the scene files
189                         if scene_filenames:
190                                 self.loadSceneFiles(scene_filenames)
191                                 self._selectObject(None)
192                                 self.sceneUpdated()
193                                 newZoom = numpy.max(self._machineSize)
194                                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
195                                 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
196
197         def showLoadModel(self, button = 1):
198                 if button == 1:
199                         dlg=wx.FileDialog(self, _("Open 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
200                         dlg.SetWildcard(meshLoader.loadWildcardFilter() + imageToMesh.wildcardList() + "|GCode file (*.gcode)|*.g;*.gcode;*.G;*.GCODE")
201                         if dlg.ShowModal() != wx.ID_OK:
202                                 dlg.Destroy()
203                                 return
204                         filenames = dlg.GetPaths()
205                         dlg.Destroy()
206                         if len(filenames) < 1:
207                                 return False
208                         profile.putPreference('lastFile', filenames[0])
209                         self.loadFiles(filenames)
210
211         def showSaveModel(self):
212                 if len(self._scene.objects()) < 1:
213                         return
214                 dlg=wx.FileDialog(self, _("Save 3D model"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
215                 dlg.SetWildcard(meshLoader.saveWildcardFilter())
216                 if dlg.ShowModal() != wx.ID_OK:
217                         dlg.Destroy()
218                         return
219                 filename = dlg.GetPath()
220                 dlg.Destroy()
221                 meshLoader.saveMeshes(filename, self._scene.objects())
222
223         def OnPrintButton(self, button):
224                 if button == 1:
225                         if machineCom.machineIsConnected():
226                                 self.showPrintWindow()
227                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
228                                 drives = removableStorage.getPossibleSDcardDrives()
229                                 if len(drives) > 1:
230                                         dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
231                                         if dlg.ShowModal() != wx.ID_OK:
232                                                 dlg.Destroy()
233                                                 return
234                                         drive = drives[dlg.GetSelection()]
235                                         dlg.Destroy()
236                                 else:
237                                         drive = drives[0]
238                                 filename = self._scene._objectList[0].getName() + '.gcode'
239                                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, drive[1] + filename, drive[1])).start()
240                         else:
241                                 self.showSaveGCode()
242                 if button == 3:
243                         menu = wx.Menu()
244                         self.Bind(wx.EVT_MENU, lambda e: self.showPrintWindow(), menu.Append(-1, _("Print with USB")))
245                         self.Bind(wx.EVT_MENU, lambda e: self.showSaveGCode(), menu.Append(-1, _("Save GCode...")))
246                         self.Bind(wx.EVT_MENU, lambda e: self._showSliceLog(), menu.Append(-1, _("Slice engine log...")))
247                         self.PopupMenu(menu)
248                         menu.Destroy()
249
250         def showPrintWindow(self):
251                 if self._gcodeFilename is None:
252                         return
253                 self._usbPrintMonitor.loadFile(self._gcodeFilename, self._slicer.getID())
254                 if self._gcodeFilename == self._slicer.getGCodeFilename():
255                         self._slicer.submitSliceInfoOnline()
256
257         def showSaveGCode(self):
258                 if len(self._scene._objectList) < 1:
259                         return
260                 dlg=wx.FileDialog(self, _("Save toolpath"), os.path.dirname(profile.getPreference('lastFile')), style=wx.FD_SAVE)
261                 filename = self._scene._objectList[0].getName() + '.gcode'
262                 dlg.SetFilename(filename)
263                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
264                 if dlg.ShowModal() != wx.ID_OK:
265                         dlg.Destroy()
266                         return
267                 filename = dlg.GetPath()
268                 dlg.Destroy()
269
270                 threading.Thread(target=self._copyFile,args=(self._gcodeFilename, filename)).start()
271
272         def _copyFile(self, fileA, fileB, allowEject = False):
273                 try:
274                         size = float(os.stat(fileA).st_size)
275                         with open(fileA, 'rb') as fsrc:
276                                 with open(fileB, 'wb') as fdst:
277                                         while 1:
278                                                 buf = fsrc.read(16*1024)
279                                                 if not buf:
280                                                         break
281                                                 fdst.write(buf)
282                                                 self.printButton.setProgressBar(float(fsrc.tell()) / size)
283                                                 self._queueRefresh()
284                 except:
285                         import sys
286                         print sys.exc_info()
287                         self.notification.message("Failed to save")
288                 else:
289                         if allowEject:
290                                 self.notification.message("Saved as %s" % (fileB), lambda : self.notification.message('You can now eject the card.') if removableStorage.ejectDrive(allowEject) else self.notification.message('Safe remove failed...'))
291                         else:
292                                 self.notification.message("Saved as %s" % (fileB))
293                 self.printButton.setProgressBar(None)
294                 if fileA == self._slicer.getGCodeFilename():
295                         self._slicer.submitSliceInfoOnline()
296
297         def _showSliceLog(self):
298                 dlg = wx.TextEntryDialog(self, _("The slicing engine reported the following"), _("Engine log..."), '\n'.join(self._slicer.getSliceLog()), wx.TE_MULTILINE | wx.OK | wx.CENTRE)
299                 dlg.ShowModal()
300                 dlg.Destroy()
301
302         def OnToolSelect(self, button):
303                 if self.rotateToolButton.getSelected():
304                         self.tool = previewTools.toolRotate(self)
305                 elif self.scaleToolButton.getSelected():
306                         self.tool = previewTools.toolScale(self)
307                 elif self.mirrorToolButton.getSelected():
308                         self.tool = previewTools.toolNone(self)
309                 else:
310                         self.tool = previewTools.toolNone(self)
311                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
312                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
313                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
314                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
315                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
316                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
317                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
318                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
319
320         def updateToolButtons(self):
321                 if self._selectedObj is None:
322                         hidden = True
323                 else:
324                         hidden = False
325                 self.rotateToolButton.setHidden(hidden)
326                 self.scaleToolButton.setHidden(hidden)
327                 self.mirrorToolButton.setHidden(hidden)
328                 if hidden:
329                         self.rotateToolButton.setSelected(False)
330                         self.scaleToolButton.setSelected(False)
331                         self.mirrorToolButton.setSelected(False)
332                         self.OnToolSelect(0)
333
334         def OnViewChange(self):
335                 if self.viewSelection.getValue() == 4:
336                         self.viewMode = 'gcode'
337                         if self._gcode is not None and self._gcode.layerList is not None:
338                                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
339                         self._selectObject(None)
340                 elif self.viewSelection.getValue() == 1:
341                         self.viewMode = 'overhang'
342                 elif self.viewSelection.getValue() == 2:
343                         self.viewMode = 'transparent'
344                 elif self.viewSelection.getValue() == 3:
345                         self.viewMode = 'xray'
346                 else:
347                         self.viewMode = 'normal'
348                 self.layerSelect.setHidden(self.viewMode != 'gcode')
349                 self.QueueRefresh()
350
351         def OnRotateReset(self, button):
352                 if self._selectedObj is None:
353                         return
354                 self._selectedObj.resetRotation()
355                 self._scene.pushFree()
356                 self._selectObject(self._selectedObj)
357                 self.sceneUpdated()
358
359         def OnLayFlat(self, button):
360                 if self._selectedObj is None:
361                         return
362                 self._selectedObj.layFlat()
363                 self._scene.pushFree()
364                 self._selectObject(self._selectedObj)
365                 self.sceneUpdated()
366
367         def OnScaleReset(self, button):
368                 if self._selectedObj is None:
369                         return
370                 self._selectedObj.resetScale()
371                 self._selectObject(self._selectedObj)
372                 self.updateProfileToControls()
373                 self.sceneUpdated()
374
375         def OnScaleMax(self, button):
376                 if self._selectedObj is None:
377                         return
378                 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2 - numpy.array([1,1,1], numpy.float32))
379                 self._scene.pushFree()
380                 self._selectObject(self._selectedObj)
381                 self.updateProfileToControls()
382                 self.sceneUpdated()
383
384         def OnMirror(self, axis):
385                 if self._selectedObj is None:
386                         return
387                 self._selectedObj.mirror(axis)
388                 self.sceneUpdated()
389
390         def OnScaleEntry(self, value, axis):
391                 if self._selectedObj is None:
392                         return
393                 try:
394                         value = float(value)
395                 except:
396                         return
397                 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
398                 self.updateProfileToControls()
399                 self._scene.pushFree()
400                 self._selectObject(self._selectedObj)
401                 self.sceneUpdated()
402
403         def OnScaleEntryMM(self, value, axis):
404                 if self._selectedObj is None:
405                         return
406                 try:
407                         value = float(value)
408                 except:
409                         return
410                 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
411                 self.updateProfileToControls()
412                 self._scene.pushFree()
413                 self._selectObject(self._selectedObj)
414                 self.sceneUpdated()
415
416         def OnDeleteAll(self, e):
417                 while len(self._scene.objects()) > 0:
418                         self._deleteObject(self._scene.objects()[0])
419                 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
420
421         def OnMultiply(self, e):
422                 if self._focusObj is None:
423                         return
424                 obj = self._focusObj
425                 dlg = wx.NumberEntryDialog(self, _("How many copies do you want?"), _("Number of copies"), _("Multiply"), 1, 1, 100)
426                 if dlg.ShowModal() != wx.ID_OK:
427                         dlg.Destroy()
428                         return
429                 cnt = dlg.GetValue()
430                 dlg.Destroy()
431                 n = 0
432                 while True:
433                         n += 1
434                         newObj = obj.copy()
435                         self._scene.add(newObj)
436                         self._scene.centerAll()
437                         if not self._scene.checkPlatform(newObj):
438                                 break
439                         if n > cnt:
440                                 break
441                 if n <= cnt:
442                         self.notification.message("Could not create more then %d items" % (n - 1))
443                 self._scene.remove(newObj)
444                 self._scene.centerAll()
445                 self.sceneUpdated()
446
447         def OnSplitObject(self, e):
448                 if self._focusObj is None:
449                         return
450                 self._scene.remove(self._focusObj)
451                 for obj in self._focusObj.split(self._splitCallback):
452                         if numpy.max(obj.getSize()) > 2.0:
453                                 self._scene.add(obj)
454                 self._scene.centerAll()
455                 self._selectObject(None)
456                 self.sceneUpdated()
457
458         def OnCenter(self, e):
459                 if self._focusObj is None:
460                         return
461                 self._focusObj.setPosition(numpy.array([0.0, 0.0]))
462                 self._scene.pushFree()
463                 newViewPos = numpy.array([self._focusObj.getPosition()[0], self._focusObj.getPosition()[1], self._focusObj.getSize()[2] / 2])
464                 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
465
466         def _splitCallback(self, progress):
467                 print progress
468
469         def OnMergeObjects(self, e):
470                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
471                         if len(self._scene.objects()) == 2:
472                                 self._scene.merge(self._scene.objects()[0], self._scene.objects()[1])
473                                 self.sceneUpdated()
474                         return
475                 self._scene.merge(self._selectedObj, self._focusObj)
476                 self.sceneUpdated()
477
478         def sceneUpdated(self):
479                 self._sceneUpdateTimer.Start(500, True)
480                 self._slicer.abortSlicer()
481                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
482                 self.QueueRefresh()
483
484         def _onRunSlicer(self, e):
485                 if self._isSimpleMode:
486                         self.GetTopLevelParent().simpleSettingsPanel.setupSlice()
487                 self._slicer.runSlicer(self._scene)
488                 if self._isSimpleMode:
489                         profile.resetTempOverride()
490
491         def _updateSliceProgress(self, progressValue, ready):
492                 if not ready:
493                         if self.printButton.getProgressBar() is not None and progressValue >= 0.0 and abs(self.printButton.getProgressBar() - progressValue) < 0.01:
494                                 return
495                 self.printButton.setDisabled(not ready)
496                 if progressValue >= 0.0:
497                         self.printButton.setProgressBar(progressValue)
498                 else:
499                         self.printButton.setProgressBar(None)
500                 if self._gcode is not None:
501                         self._gcode = None
502                         for layerVBOlist in self._gcodeVBOs:
503                                 for vbo in layerVBOlist:
504                                         self.glReleaseList.append(vbo)
505                         self._gcodeVBOs = []
506                 if ready:
507                         self.printButton.setProgressBar(None)
508                         cost = self._slicer.getFilamentCost()
509                         if cost is not None:
510                                 self.printButton.setBottomText('%s\n%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount(), cost))
511                         else:
512                                 self.printButton.setBottomText('%s\n%s' % (self._slicer.getPrintTime(), self._slicer.getFilamentAmount()))
513                         self._gcode = gcodeInterpreter.gcode()
514                         self._gcodeFilename = self._slicer.getGCodeFilename()
515                 else:
516                         self.printButton.setBottomText('')
517                 self.QueueRefresh()
518
519         def _loadGCode(self):
520                 self._gcode.progressCallback = self._gcodeLoadCallback
521                 self._gcode.load(self._gcodeFilename)
522
523         def _gcodeLoadCallback(self, progress):
524                 if self._gcode is None:
525                         return True
526                 if len(self._gcode.layerList) % 15 == 0:
527                         time.sleep(0.1)
528                 if self._gcode is None:
529                         return True
530                 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
531                 if self.viewMode == 'gcode':
532                         self._queueRefresh()
533                 return False
534
535         def loadScene(self, fileList):
536                 for filename in fileList:
537                         try:
538                                 ext = os.path.splitext(filename)[1].lower()
539                                 if ext in imageToMesh.supportedExtensions():
540                                         imageToMesh.convertImageDialog(self, filename).Show()
541                                         objList = []
542                                 else:
543                                         objList = meshLoader.loadMeshes(filename)
544                         except:
545                                 traceback.print_exc()
546                         else:
547                                 for obj in objList:
548                                         if self._objectLoadShader is not None:
549                                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
550                                         else:
551                                                 obj._loadAnim = None
552                                         self._scene.add(obj)
553                                         self._scene.centerAll()
554                                         self._selectObject(obj)
555                                         if obj.getScale()[0] < 1.0:
556                                                 self.notification.message("Warning: Object scaled down.")
557                 self.sceneUpdated()
558
559         def _deleteObject(self, obj):
560                 if obj == self._selectedObj:
561                         self._selectObject(None)
562                 if obj == self._focusObj:
563                         self._focusObj = None
564                 self._scene.remove(obj)
565                 for m in obj._meshList:
566                         if m.vbo is not None and m.vbo.decRef():
567                                 self.glReleaseList.append(m.vbo)
568                 import gc
569                 gc.collect()
570                 self.sceneUpdated()
571
572         def _selectObject(self, obj, zoom = True):
573                 if obj != self._selectedObj:
574                         self._selectedObj = obj
575                         self.updateProfileToControls()
576                         self.updateToolButtons()
577                 if zoom and obj is not None:
578                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2])
579                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
580                         newZoom = obj.getBoundaryCircle() * 6
581                         if newZoom > numpy.max(self._machineSize) * 3:
582                                 newZoom = numpy.max(self._machineSize) * 3
583                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
584
585         def updateProfileToControls(self):
586                 oldSimpleMode = self._isSimpleMode
587                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
588                 if self._isSimpleMode != oldSimpleMode:
589                         self._scene.arrangeAll()
590                         self.sceneUpdated()
591                 self._machineSize = numpy.array([profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')])
592                 self._objColors[0] = profile.getPreferenceColour('model_colour')
593                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
594                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
595                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
596                 self._scene.setMachineSize(self._machineSize)
597                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
598                 self._scene.setHeadSize(profile.getMachineSettingFloat('extruder_head_size_min_x'), profile.getMachineSettingFloat('extruder_head_size_max_x'), profile.getMachineSettingFloat('extruder_head_size_min_y'), profile.getMachineSettingFloat('extruder_head_size_max_y'), profile.getMachineSettingFloat('extruder_head_size_height'))
599
600                 if self._selectedObj is not None:
601                         scale = self._selectedObj.getScale()
602                         size = self._selectedObj.getSize()
603                         self.scaleXctrl.setValue(round(scale[0], 2))
604                         self.scaleYctrl.setValue(round(scale[1], 2))
605                         self.scaleZctrl.setValue(round(scale[2], 2))
606                         self.scaleXmmctrl.setValue(round(size[0], 2))
607                         self.scaleYmmctrl.setValue(round(size[1], 2))
608                         self.scaleZmmctrl.setValue(round(size[2], 2))
609
610         def OnKeyChar(self, keyCode):
611                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE or (keyCode == wx.WXK_BACK and platform.system() == "Darwin"):
612                         if self._selectedObj is not None:
613                                 self._deleteObject(self._selectedObj)
614                                 self.QueueRefresh()
615                 if keyCode == wx.WXK_UP:
616                         self.layerSelect.setValue(self.layerSelect.getValue() + 1)
617                         self.QueueRefresh()
618                 elif keyCode == wx.WXK_DOWN:
619                         self.layerSelect.setValue(self.layerSelect.getValue() - 1)
620                         self.QueueRefresh()
621                 elif keyCode == wx.WXK_PAGEUP:
622                         self.layerSelect.setValue(self.layerSelect.getValue() + 10)
623                         self.QueueRefresh()
624                 elif keyCode == wx.WXK_PAGEDOWN:
625                         self.layerSelect.setValue(self.layerSelect.getValue() - 10)
626                         self.QueueRefresh()
627
628                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
629                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
630                 if keyCode == wx.WXK_F4 and wx.GetKeyState(wx.WXK_SHIFT):
631                         from collections import defaultdict
632                         from gc import get_objects
633                         self._beforeLeakTest = defaultdict(int)
634                         for i in get_objects():
635                                 self._beforeLeakTest[type(i)] += 1
636                 if keyCode == wx.WXK_F5 and wx.GetKeyState(wx.WXK_SHIFT):
637                         from collections import defaultdict
638                         from gc import get_objects
639                         self._afterLeakTest = defaultdict(int)
640                         for i in get_objects():
641                                 self._afterLeakTest[type(i)] += 1
642                         for k in self._afterLeakTest:
643                                 if self._afterLeakTest[k]-self._beforeLeakTest[k]:
644                                         print k, self._afterLeakTest[k], self._beforeLeakTest[k], self._afterLeakTest[k] - self._beforeLeakTest[k]
645
646         def ShaderUpdate(self, v, f):
647                 s = opengl.GLShader(v, f)
648                 if s.isValid():
649                         self._objectLoadShader.release()
650                         self._objectLoadShader = s
651                         for obj in self._scene.objects():
652                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
653                         self.QueueRefresh()
654
655         def OnMouseDown(self,e):
656                 self._mouseX = e.GetX()
657                 self._mouseY = e.GetY()
658                 self._mouseClick3DPos = self._mouse3Dpos
659                 self._mouseClickFocus = self._focusObj
660                 if e.ButtonDClick():
661                         self._mouseState = 'doubleClick'
662                 else:
663                         self._mouseState = 'dragOrClick'
664                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
665                 p0 -= self.getObjectCenterPos() - self._viewTarget
666                 p1 -= self.getObjectCenterPos() - self._viewTarget
667                 if self.tool.OnDragStart(p0, p1):
668                         self._mouseState = 'tool'
669                 if self._mouseState == 'dragOrClick':
670                         if e.GetButton() == 1:
671                                 if self._focusObj is not None:
672                                         self._selectObject(self._focusObj, False)
673                                         self.QueueRefresh()
674
675         def OnMouseUp(self, e):
676                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
677                         return
678                 if self._mouseState == 'dragOrClick':
679                         if e.GetButton() == 1:
680                                 self._selectObject(self._focusObj)
681                         if e.GetButton() == 3:
682                                         menu = wx.Menu()
683                                         if self._focusObj is not None:
684                                                 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, _("Delete object")))
685                                                 self.Bind(wx.EVT_MENU, self.OnCenter, menu.Append(-1, _("Center on platform")))
686                                                 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, _("Multiply object")))
687                                                 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, _("Split object into parts")))
688                                         if ((self._selectedObj != self._focusObj and self._focusObj is not None and self._selectedObj is not None) or len(self._scene.objects()) == 2) and int(profile.getMachineSetting('extruder_amount')) > 1:
689                                                 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, _("Dual extrusion merge")))
690                                         if len(self._scene.objects()) > 0:
691                                                 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, _("Delete all objects")))
692                                         if menu.MenuItemCount > 0:
693                                                 self.PopupMenu(menu)
694                                         menu.Destroy()
695                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
696                         self._scene.pushFree()
697                         self.sceneUpdated()
698                 elif self._mouseState == 'tool':
699                         if self.tempMatrix is not None and self._selectedObj is not None:
700                                 self._selectedObj.applyMatrix(self.tempMatrix)
701                                 self._scene.pushFree()
702                                 self._selectObject(self._selectedObj)
703                         self.tempMatrix = None
704                         self.tool.OnDragEnd()
705                         self.sceneUpdated()
706                 self._mouseState = None
707
708         def OnMouseMotion(self,e):
709                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
710                 p0 -= self.getObjectCenterPos() - self._viewTarget
711                 p1 -= self.getObjectCenterPos() - self._viewTarget
712
713                 if e.Dragging() and self._mouseState is not None:
714                         if self._mouseState == 'tool':
715                                 self.tool.OnDrag(p0, p1)
716                         elif not e.LeftIsDown() and e.RightIsDown():
717                                 self._mouseState = 'drag'
718                                 if wx.GetKeyState(wx.WXK_SHIFT):
719                                         a = math.cos(math.radians(self._yaw)) / 3.0
720                                         b = math.sin(math.radians(self._yaw)) / 3.0
721                                         self._viewTarget[0] += float(e.GetX() - self._mouseX) * -a
722                                         self._viewTarget[1] += float(e.GetX() - self._mouseX) * b
723                                         self._viewTarget[0] += float(e.GetY() - self._mouseY) * b
724                                         self._viewTarget[1] += float(e.GetY() - self._mouseY) * a
725                                 else:
726                                         self._yaw += e.GetX() - self._mouseX
727                                         self._pitch -= e.GetY() - self._mouseY
728                                 if self._pitch > 170:
729                                         self._pitch = 170
730                                 if self._pitch < 10:
731                                         self._pitch = 10
732                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
733                                 self._mouseState = 'drag'
734                                 self._zoom += e.GetY() - self._mouseY
735                                 if self._zoom < 1:
736                                         self._zoom = 1
737                                 if self._zoom > numpy.max(self._machineSize) * 3:
738                                         self._zoom = numpy.max(self._machineSize) * 3
739                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus:
740                                 self._mouseState = 'dragObject'
741                                 z = max(0, self._mouseClick3DPos[2])
742                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
743                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
744                                 p0[2] -= z
745                                 p1[2] -= z
746                                 p2[2] -= z
747                                 p3[2] -= z
748                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
749                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
750                                 diff = cursorZ1 - cursorZ0
751                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
752                 if not e.Dragging() or self._mouseState != 'tool':
753                         self.tool.OnMouseMove(p0, p1)
754
755                 self._mouseX = e.GetX()
756                 self._mouseY = e.GetY()
757
758         def OnMouseWheel(self, e):
759                 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
760                 delta = max(min(delta,4),-4)
761                 self._zoom *= 1.0 - delta / 10.0
762                 if self._zoom < 1.0:
763                         self._zoom = 1.0
764                 if self._zoom > numpy.max(self._machineSize) * 3:
765                         self._zoom = numpy.max(self._machineSize) * 3
766                 self.Refresh()
767
768         def OnMouseLeave(self, e):
769                 #self._mouseX = -1
770                 pass
771
772         def getMouseRay(self, x, y):
773                 if self._viewport is None:
774                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
775                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
776                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
777                 p0 -= self._viewTarget
778                 p1 -= self._viewTarget
779                 return p0, p1
780
781         def _init3DView(self):
782                 # set viewing projection
783                 size = self.GetSize()
784                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
785                 glLoadIdentity()
786
787                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
788
789                 glDisable(GL_RESCALE_NORMAL)
790                 glDisable(GL_LIGHTING)
791                 glDisable(GL_LIGHT0)
792                 glEnable(GL_DEPTH_TEST)
793                 glDisable(GL_CULL_FACE)
794                 glDisable(GL_BLEND)
795                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
796
797                 glClearColor(0.8, 0.8, 0.8, 1.0)
798                 glClearStencil(0)
799                 glClearDepth(1.0)
800
801                 glMatrixMode(GL_PROJECTION)
802                 glLoadIdentity()
803                 aspect = float(size.GetWidth()) / float(size.GetHeight())
804                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
805
806                 glMatrixMode(GL_MODELVIEW)
807                 glLoadIdentity()
808                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
809
810         def OnPaint(self,e):
811                 if machineCom.machineIsConnected():
812                         self.printButton._imageID = 6
813                         self.printButton._tooltip = _("Print")
814                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
815                         self.printButton._imageID = 2
816                         self.printButton._tooltip = _("Toolpath to SD")
817                 else:
818                         self.printButton._imageID = 3
819                         self.printButton._tooltip = _("Save toolpath")
820
821                 if self._animView is not None:
822                         self._viewTarget = self._animView.getPosition()
823                         if self._animView.isDone():
824                                 self._animView = None
825                 if self._animZoom is not None:
826                         self._zoom = self._animZoom.getPosition()
827                         if self._animZoom.isDone():
828                                 self._animZoom = None
829                 if self.viewMode == 'gcode' and self._gcode is not None:
830                         try:
831                                 self._viewTarget[2] = self._gcode.layerList[self.layerSelect.getValue()][-1]['points'][0][2]
832                         except:
833                                 pass
834                 if self._objectShader is None:
835                         if opengl.hasShaderSupport():
836                                 self._objectShader = opengl.GLShader("""
837 varying float light_amount;
838
839 void main(void)
840 {
841     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
842     gl_FrontColor = gl_Color;
843
844         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
845         light_amount += 0.2;
846 }
847                                 ""","""
848 varying float light_amount;
849
850 void main(void)
851 {
852         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
853 }
854                                 """)
855                                 self._objectOverhangShader = opengl.GLShader("""
856 uniform float cosAngle;
857 uniform mat3 rotMatrix;
858 varying float light_amount;
859
860 void main(void)
861 {
862     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
863     gl_FrontColor = gl_Color;
864
865         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
866         light_amount += 0.2;
867         if (normalize(rotMatrix * gl_Normal).z < -cosAngle)
868         {
869                 light_amount = -10.0;
870         }
871 }
872                                 ""","""
873 varying float light_amount;
874
875 void main(void)
876 {
877         if (light_amount == -10.0)
878         {
879                 gl_FragColor = vec4(1.0, 0.0, 0.0, gl_Color[3]);
880         }else{
881                 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
882         }
883 }
884                                 """)
885                                 self._objectLoadShader = opengl.GLShader("""
886 uniform float intensity;
887 uniform float scale;
888 varying float light_amount;
889
890 void main(void)
891 {
892         vec4 tmp = gl_Vertex;
893     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
894     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
895     gl_Position = gl_ModelViewProjectionMatrix * tmp;
896     gl_FrontColor = gl_Color;
897
898         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
899         light_amount += 0.2;
900 }
901                         ""","""
902 uniform float intensity;
903 varying float light_amount;
904
905 void main(void)
906 {
907         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
908 }
909                                 """)
910                         if self._objectShader is None or not self._objectShader.isValid():
911                                 self._objectShader = opengl.GLFakeShader()
912                                 self._objectOverhangShader = opengl.GLFakeShader()
913                                 self._objectLoadShader = None
914                 self._init3DView()
915                 glTranslate(0,0,-self._zoom)
916                 glRotate(-self._pitch, 1,0,0)
917                 glRotate(self._yaw, 0,0,1)
918                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
919
920                 self._viewport = glGetIntegerv(GL_VIEWPORT)
921                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
922                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
923
924                 glClearColor(1,1,1,1)
925                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
926
927                 if self.viewMode != 'gcode':
928                         for n in xrange(0, len(self._scene.objects())):
929                                 obj = self._scene.objects()[n]
930                                 glColor4ub((n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF, 0xFF)
931                                 self._renderObject(obj)
932
933                 if self._mouseX > -1:
934                         glFlush()
935                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0] >> 8
936                         if n < len(self._scene.objects()):
937                                 self._focusObj = self._scene.objects()[n]
938                         else:
939                                 self._focusObj = None
940                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
941                         #self.GetTopLevelParent().SetTitle(hex(n) + " " + str(f))
942                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
943                         self._mouse3Dpos -= self._viewTarget
944
945                 self._init3DView()
946                 glTranslate(0,0,-self._zoom)
947                 glRotate(-self._pitch, 1,0,0)
948                 glRotate(self._yaw, 0,0,1)
949                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
950
951                 if self.viewMode == 'gcode':
952                         if self._gcode is not None and self._gcode.layerList is None:
953                                 self._gcodeLoadThread = threading.Thread(target=self._loadGCode)
954                                 self._gcodeLoadThread.daemon = True
955                                 self._gcodeLoadThread.start()
956                         if self._gcode is not None and self._gcode.layerList is not None:
957                                 glPushMatrix()
958                                 if profile.getMachineSetting('machine_center_is_zero') != 'True':
959                                         glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
960                                 t = time.time()
961                                 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
962                                 for n in xrange(0, drawUpTill):
963                                         c = 1.0 - float(drawUpTill - n) / 15
964                                         c = max(0.3, c)
965                                         if len(self._gcodeVBOs) < n + 1:
966                                                 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
967                                                 if time.time() - t > 0.5:
968                                                         self.QueueRefresh()
969                                                         break
970                                         #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
971                                         if n == drawUpTill - 1:
972                                                 if len(self._gcodeVBOs[n]) < 9:
973                                                         self._gcodeVBOs[n] += self._generateGCodeVBOs2(self._gcode.layerList[n])
974                                                 glColor3f(c, 0, 0)
975                                                 self._gcodeVBOs[n][8].render(GL_QUADS)
976                                                 glColor3f(c/2, 0, c)
977                                                 self._gcodeVBOs[n][9].render(GL_QUADS)
978                                                 glColor3f(0, c, c/2)
979                                                 self._gcodeVBOs[n][10].render(GL_QUADS)
980                                                 glColor3f(c, 0, 0)
981                                                 self._gcodeVBOs[n][11].render(GL_QUADS)
982
983                                                 glColor3f(0, c, 0)
984                                                 self._gcodeVBOs[n][12].render(GL_QUADS)
985                                                 glColor3f(c/2, c/2, 0.0)
986                                                 self._gcodeVBOs[n][13].render(GL_QUADS)
987                                                 glColor3f(0, c, c)
988                                                 self._gcodeVBOs[n][14].render(GL_QUADS)
989                                                 self._gcodeVBOs[n][15].render(GL_QUADS)
990                                                 glColor3f(0, 0, c)
991                                                 self._gcodeVBOs[n][16].render(GL_LINES)
992                                         else:
993                                                 glColor3f(c, 0, 0)
994                                                 self._gcodeVBOs[n][0].render(GL_LINES)
995                                                 glColor3f(c/2, 0, c)
996                                                 self._gcodeVBOs[n][1].render(GL_LINES)
997                                                 glColor3f(0, c, c/2)
998                                                 self._gcodeVBOs[n][2].render(GL_LINES)
999                                                 glColor3f(c, 0, 0)
1000                                                 self._gcodeVBOs[n][3].render(GL_LINES)
1001
1002                                                 glColor3f(0, c, 0)
1003                                                 self._gcodeVBOs[n][4].render(GL_LINES)
1004                                                 glColor3f(c/2, c/2, 0.0)
1005                                                 self._gcodeVBOs[n][5].render(GL_LINES)
1006                                                 glColor3f(0, c, c)
1007                                                 self._gcodeVBOs[n][6].render(GL_LINES)
1008                                                 self._gcodeVBOs[n][7].render(GL_LINES)
1009                                 glPopMatrix()
1010                 else:
1011                         glStencilFunc(GL_ALWAYS, 1, 1)
1012                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1013
1014                         if self.viewMode == 'overhang':
1015                                 self._objectOverhangShader.bind()
1016                                 self._objectOverhangShader.setUniform('cosAngle', math.cos(math.radians(90 - 60)))
1017                         else:
1018                                 self._objectShader.bind()
1019                         for obj in self._scene.objects():
1020                                 if obj._loadAnim is not None:
1021                                         if obj._loadAnim.isDone():
1022                                                 obj._loadAnim = None
1023                                         else:
1024                                                 continue
1025                                 brightness = 1.0
1026                                 if self._focusObj == obj:
1027                                         brightness = 1.2
1028                                 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
1029                                         brightness = 0.8
1030
1031                                 if self._selectedObj == obj or self._selectedObj is None:
1032                                         #If we want transparent, then first render a solid black model to remove the printer size lines.
1033                                         if self.viewMode == 'transparent':
1034                                                 glColor4f(0, 0, 0, 0)
1035                                                 self._renderObject(obj)
1036                                                 glEnable(GL_BLEND)
1037                                                 glBlendFunc(GL_ONE, GL_ONE)
1038                                                 glDisable(GL_DEPTH_TEST)
1039                                                 brightness *= 0.5
1040                                         if self.viewMode == 'xray':
1041                                                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
1042                                         glStencilOp(GL_INCR, GL_INCR, GL_INCR)
1043                                         glEnable(GL_STENCIL_TEST)
1044
1045                                 if self.viewMode == 'overhang':
1046                                         if self._selectedObj == obj and self.tempMatrix is not None:
1047                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix() * self.tempMatrix)
1048                                         else:
1049                                                 self._objectOverhangShader.setUniform('rotMatrix', obj.getMatrix())
1050
1051                                 if not self._scene.checkPlatform(obj):
1052                                         glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
1053                                         self._renderObject(obj)
1054                                 else:
1055                                         self._renderObject(obj, brightness)
1056                                 glDisable(GL_STENCIL_TEST)
1057                                 glDisable(GL_BLEND)
1058                                 glEnable(GL_DEPTH_TEST)
1059                                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
1060
1061                         if self.viewMode == 'xray':
1062                                 glPushMatrix()
1063                                 glLoadIdentity()
1064                                 glEnable(GL_STENCIL_TEST)
1065                                 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
1066                                 glDisable(GL_DEPTH_TEST)
1067                                 for i in xrange(2, 15, 2):
1068                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1069                                         glColor(float(i)/10, float(i)/10, float(i)/5)
1070                                         glBegin(GL_QUADS)
1071                                         glVertex3f(-1000,-1000,-10)
1072                                         glVertex3f( 1000,-1000,-10)
1073                                         glVertex3f( 1000, 1000,-10)
1074                                         glVertex3f(-1000, 1000,-10)
1075                                         glEnd()
1076                                 for i in xrange(1, 15, 2):
1077                                         glStencilFunc(GL_EQUAL, i, 0xFF)
1078                                         glColor(float(i)/10, 0, 0)
1079                                         glBegin(GL_QUADS)
1080                                         glVertex3f(-1000,-1000,-10)
1081                                         glVertex3f( 1000,-1000,-10)
1082                                         glVertex3f( 1000, 1000,-10)
1083                                         glVertex3f(-1000, 1000,-10)
1084                                         glEnd()
1085                                 glPopMatrix()
1086                                 glDisable(GL_STENCIL_TEST)
1087                                 glEnable(GL_DEPTH_TEST)
1088
1089                         self._objectShader.unbind()
1090
1091                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1092                         glEnable(GL_BLEND)
1093                         if self._objectLoadShader is not None:
1094                                 self._objectLoadShader.bind()
1095                                 glColor4f(0.2, 0.6, 1.0, 1.0)
1096                                 for obj in self._scene.objects():
1097                                         if obj._loadAnim is None:
1098                                                 continue
1099                                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
1100                                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
1101                                         self._renderObject(obj)
1102                                 self._objectLoadShader.unbind()
1103                                 glDisable(GL_BLEND)
1104
1105                 self._drawMachine()
1106
1107                 if self._usbPrintMonitor.getState() == 'PRINTING' and self._usbPrintMonitor.getID() == self._slicer.getID():
1108                         glEnable(GL_BLEND)
1109                         z = self._usbPrintMonitor.getZ()
1110                         size = self._machineSize
1111                         glColor4ub(255,255,0,128)
1112                         glBegin(GL_QUADS)
1113                         glVertex3f(-size[0]/2,-size[1]/2, z)
1114                         glVertex3f( size[0]/2,-size[1]/2, z)
1115                         glVertex3f( size[0]/2, size[1]/2, z)
1116                         glVertex3f(-size[0]/2, size[1]/2, z)
1117                         glEnd()
1118
1119                 if self.viewMode == 'gcode':
1120                         if self._gcodeLoadThread is not None and self._gcodeLoadThread.isAlive():
1121                                 glDisable(GL_DEPTH_TEST)
1122                                 glPushMatrix()
1123                                 glLoadIdentity()
1124                                 glTranslate(0,-4,-10)
1125                                 glColor4ub(60,60,60,255)
1126                                 opengl.glDrawStringCenter(_("Loading toolpath for visualization..."))
1127                                 glPopMatrix()
1128                 else:
1129                         #Draw the object box-shadow, so you can see where it will collide with other objects.
1130                         if self._selectedObj is not None and len(self._scene.objects()) > 1:
1131                                 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
1132                                 glPushMatrix()
1133                                 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0)
1134                                 glEnable(GL_BLEND)
1135                                 glEnable(GL_CULL_FACE)
1136                                 glColor4f(0,0,0,0.12)
1137                                 glBegin(GL_QUADS)
1138                                 glVertex3f(-size[0],  size[1], 0.1)
1139                                 glVertex3f(-size[0], -size[1], 0.1)
1140                                 glVertex3f( size[0], -size[1], 0.1)
1141                                 glVertex3f( size[0],  size[1], 0.1)
1142                                 glEnd()
1143                                 glDisable(GL_CULL_FACE)
1144                                 glPopMatrix()
1145
1146                         #Draw the outline of the selected object, on top of everything else except the GUI.
1147                         if self._selectedObj is not None and self._selectedObj._loadAnim is None:
1148                                 glDisable(GL_DEPTH_TEST)
1149                                 glEnable(GL_CULL_FACE)
1150                                 glEnable(GL_STENCIL_TEST)
1151                                 glDisable(GL_BLEND)
1152                                 glStencilFunc(GL_EQUAL, 0, 255)
1153
1154                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
1155                                 glLineWidth(2)
1156                                 glColor4f(1,1,1,0.5)
1157                                 self._renderObject(self._selectedObj)
1158                                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
1159
1160                                 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
1161                                 glDisable(GL_STENCIL_TEST)
1162                                 glDisable(GL_CULL_FACE)
1163                                 glEnable(GL_DEPTH_TEST)
1164
1165                         if self._selectedObj is not None:
1166                                 glPushMatrix()
1167                                 pos = self.getObjectCenterPos()
1168                                 glTranslate(pos[0], pos[1], pos[2])
1169                                 self.tool.OnDraw()
1170                                 glPopMatrix()
1171                 if self.viewMode == 'overhang' and not opengl.hasShaderSupport():
1172                         glDisable(GL_DEPTH_TEST)
1173                         glPushMatrix()
1174                         glLoadIdentity()
1175                         glTranslate(0,-4,-10)
1176                         glColor4ub(60,60,60,255)
1177                         opengl.glDrawStringCenter(_("Overhang view not working due to lack of OpenGL shaders support."))
1178                         glPopMatrix()
1179
1180         def _renderObject(self, obj, brightness = False, addSink = True):
1181                 glPushMatrix()
1182                 if addSink:
1183                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2 - profile.getProfileSettingFloat('object_sink'))
1184                 else:
1185                         glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
1186
1187                 if self.tempMatrix is not None and obj == self._selectedObj:
1188                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
1189                         glMultMatrixf(tempMatrix)
1190
1191                 offset = obj.getDrawOffset()
1192                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
1193
1194                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
1195                 glMultMatrixf(tempMatrix)
1196
1197                 n = 0
1198                 for m in obj._meshList:
1199                         if m.vbo is None:
1200                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
1201                         if brightness:
1202                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
1203                                 n += 1
1204                         m.vbo.render()
1205                 glPopMatrix()
1206
1207         def _drawMachine(self):
1208                 glEnable(GL_CULL_FACE)
1209                 glEnable(GL_BLEND)
1210
1211                 size = [profile.getMachineSettingFloat('machine_width'), profile.getMachineSettingFloat('machine_depth'), profile.getMachineSettingFloat('machine_height')]
1212
1213                 machine = profile.getMachineSetting('machine_type')
1214                 if profile.getMachineSetting('machine_type').startswith('ultimaker'):
1215                         if machine not in self._platformMesh:
1216                                 meshes = meshLoader.loadMeshes(resources.getPathForMesh(machine + '_platform.stl'))
1217                                 if len(meshes) > 0:
1218                                         self._platformMesh[machine] = meshes[0]
1219                                 else:
1220                                         self._platformMesh[machine] = None
1221                                 if machine == 'ultimaker2':
1222                                         self._platformMesh[machine]._drawOffset = numpy.array([0,-37,145], numpy.float32)
1223                                 else:
1224                                         self._platformMesh[machine]._drawOffset = numpy.array([0,0,2.5], numpy.float32)
1225                         glColor4f(1,1,1,0.5)
1226                         self._objectShader.bind()
1227                         self._renderObject(self._platformMesh[machine], False, False)
1228                         self._objectShader.unbind()
1229
1230                         #For the Ultimaker 2 render the texture on the back plate to show the Ultimaker2 text.
1231                         if machine == 'ultimaker2':
1232                                 if not hasattr(self._platformMesh[machine], 'texture'):
1233                                         self._platformMesh[machine].texture = opengl.loadGLTexture('Ultimaker2backplate.png')
1234                                 glBindTexture(GL_TEXTURE_2D, self._platformMesh[machine].texture)
1235                                 glEnable(GL_TEXTURE_2D)
1236                                 glPushMatrix()
1237                                 glColor4f(1,1,1,1)
1238
1239                                 glTranslate(0,150,-5)
1240                                 h = 50
1241                                 d = 8
1242                                 w = 100
1243                                 glEnable(GL_BLEND)
1244                                 glBlendFunc(GL_DST_COLOR, GL_ZERO)
1245                                 glBegin(GL_QUADS)
1246                                 glTexCoord2f(1, 0)
1247                                 glVertex3f( w, 0, h)
1248                                 glTexCoord2f(0, 0)
1249                                 glVertex3f(-w, 0, h)
1250                                 glTexCoord2f(0, 1)
1251                                 glVertex3f(-w, 0, 0)
1252                                 glTexCoord2f(1, 1)
1253                                 glVertex3f( w, 0, 0)
1254
1255                                 glTexCoord2f(1, 0)
1256                                 glVertex3f(-w, d, h)
1257                                 glTexCoord2f(0, 0)
1258                                 glVertex3f( w, d, h)
1259                                 glTexCoord2f(0, 1)
1260                                 glVertex3f( w, d, 0)
1261                                 glTexCoord2f(1, 1)
1262                                 glVertex3f(-w, d, 0)
1263                                 glEnd()
1264                                 glDisable(GL_TEXTURE_2D)
1265                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
1266                                 glPopMatrix()
1267                 else:
1268                         glColor4f(0,0,0,1)
1269                         glLineWidth(3)
1270                         glBegin(GL_LINES)
1271                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1272                         glVertex3f(-size[0] / 2, -size[1] / 2, 10)
1273                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1274                         glVertex3f(-size[0] / 2+10, -size[1] / 2, 0)
1275                         glVertex3f(-size[0] / 2, -size[1] / 2, 0)
1276                         glVertex3f(-size[0] / 2, -size[1] / 2+10, 0)
1277                         glEnd()
1278
1279                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
1280                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
1281                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
1282                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
1283                 v4 = [ size[0] / 2, size[1] / 2, 0]
1284                 v5 = [ size[0] / 2,-size[1] / 2, 0]
1285                 v6 = [-size[0] / 2, size[1] / 2, 0]
1286                 v7 = [-size[0] / 2,-size[1] / 2, 0]
1287
1288                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
1289                 glEnableClientState(GL_VERTEX_ARRAY)
1290                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
1291
1292                 glColor4ub(5, 171, 231, 64)
1293                 glDrawArrays(GL_QUADS, 0, 4)
1294                 glColor4ub(5, 171, 231, 96)
1295                 glDrawArrays(GL_QUADS, 4, 8)
1296                 glColor4ub(5, 171, 231, 128)
1297                 glDrawArrays(GL_QUADS, 12, 8)
1298                 glDisableClientState(GL_VERTEX_ARRAY)
1299
1300                 sx = self._machineSize[0]
1301                 sy = self._machineSize[1]
1302                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
1303                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
1304                                 x1 = x * 10
1305                                 x2 = x1 + 10
1306                                 y1 = y * 10
1307                                 y2 = y1 + 10
1308                                 x1 = max(min(x1, sx/2), -sx/2)
1309                                 y1 = max(min(y1, sy/2), -sy/2)
1310                                 x2 = max(min(x2, sx/2), -sx/2)
1311                                 y2 = max(min(y2, sy/2), -sy/2)
1312                                 if (x & 1) == (y & 1):
1313                                         glColor4ub(5, 171, 231, 127)
1314                                 else:
1315                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
1316                                 glBegin(GL_QUADS)
1317                                 glVertex3f(x1, y1, -0.02)
1318                                 glVertex3f(x2, y1, -0.02)
1319                                 glVertex3f(x2, y2, -0.02)
1320                                 glVertex3f(x1, y2, -0.02)
1321                                 glEnd()
1322
1323                 glDisable(GL_BLEND)
1324                 glDisable(GL_CULL_FACE)
1325
1326         def _generateGCodeVBOs(self, layer):
1327                 ret = []
1328                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1329                         if ':' in extrudeType:
1330                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1331                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1332                         else:
1333                                 extruder = None
1334                         pointList = numpy.zeros((0,3), numpy.float32)
1335                         for path in layer:
1336                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1337                                         a = path['points']
1338                                         a = numpy.concatenate((a[:-1], a[1:]), 1)
1339                                         a = a.reshape((len(a) * 2, 3))
1340                                         pointList = numpy.concatenate((pointList, a))
1341                         ret.append(opengl.GLVBO(pointList))
1342                 return ret
1343
1344         def _generateGCodeVBOs2(self, layer):
1345                 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
1346                 filamentArea = math.pi * filamentRadius * filamentRadius
1347                 useFilamentArea = profile.getMachineSetting('gcode_flavor') == 'UltiGCode'
1348
1349                 ret = []
1350                 for extrudeType in ['WALL-OUTER:0', 'WALL-OUTER:1', 'WALL-OUTER:2', 'WALL-OUTER:3', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
1351                         if ':' in extrudeType:
1352                                 extruder = int(extrudeType[extrudeType.find(':')+1:])
1353                                 extrudeType = extrudeType[0:extrudeType.find(':')]
1354                         else:
1355                                 extruder = None
1356                         pointList = numpy.zeros((0,3), numpy.float32)
1357                         for path in layer:
1358                                 if path['type'] == 'extrude' and path['pathType'] == extrudeType and (extruder is None or path['extruder'] == extruder):
1359                                         a = path['points']
1360                                         if extrudeType == 'FILL':
1361                                                 a[:,2] += 0.01
1362
1363                                         normal = a[1:] - a[:-1]
1364                                         lens = numpy.sqrt(normal[:,0]**2 + normal[:,1]**2)
1365                                         normal[:,0], normal[:,1] = -normal[:,1] / lens, normal[:,0] / lens
1366                                         normal[:,2] /= lens
1367
1368                                         ePerDist = path['extrusion'][1:] / lens
1369                                         if useFilamentArea:
1370                                                 lineWidth = ePerDist / path['layerThickness'] / 2.0
1371                                         else:
1372                                                 lineWidth = ePerDist * (filamentArea / path['layerThickness'] / 2)
1373
1374                                         normal[:,0] *= lineWidth
1375                                         normal[:,1] *= lineWidth
1376
1377                                         b = numpy.zeros((len(a)-1, 0), numpy.float32)
1378                                         b = numpy.concatenate((b, a[1:] + normal), 1)
1379                                         b = numpy.concatenate((b, a[1:] - normal), 1)
1380                                         b = numpy.concatenate((b, a[:-1] - normal), 1)
1381                                         b = numpy.concatenate((b, a[:-1] + normal), 1)
1382                                         b = b.reshape((len(b) * 4, 3))
1383
1384                                         if len(a) > 2:
1385                                                 normal2 = normal[:-1] + normal[1:]
1386                                                 lens2 = numpy.sqrt(normal2[:,0]**2 + normal2[:,1]**2)
1387                                                 normal2[:,0] /= lens2
1388                                                 normal2[:,1] /= lens2
1389                                                 normal2[:,0] *= lineWidth[:-1]
1390                                                 normal2[:,1] *= lineWidth[:-1]
1391
1392                                                 c = numpy.zeros((len(a)-2, 0), numpy.float32)
1393                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1394                                                 c = numpy.concatenate((c, a[1:-1]+normal[1:]), 1)
1395                                                 c = numpy.concatenate((c, a[1:-1]+normal2), 1)
1396                                                 c = numpy.concatenate((c, a[1:-1]+normal[:-1]), 1)
1397
1398                                                 c = numpy.concatenate((c, a[1:-1]), 1)
1399                                                 c = numpy.concatenate((c, a[1:-1]-normal[1:]), 1)
1400                                                 c = numpy.concatenate((c, a[1:-1]-normal2), 1)
1401                                                 c = numpy.concatenate((c, a[1:-1]-normal[:-1]), 1)
1402
1403                                                 c = c.reshape((len(c) * 8, 3))
1404
1405                                                 pointList = numpy.concatenate((pointList, b, c))
1406                                         else:
1407                                                 pointList = numpy.concatenate((pointList, b))
1408                         ret.append(opengl.GLVBO(pointList))
1409
1410                 pointList = numpy.zeros((0,3), numpy.float32)
1411                 for path in layer:
1412                         if path['type'] == 'move':
1413                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1414                                 a = numpy.concatenate((a[:-1], a[1:]), 1)
1415                                 a = a.reshape((len(a) * 2, 3))
1416                                 pointList = numpy.concatenate((pointList, a))
1417                         if path['type'] == 'retract':
1418                                 a = path['points'] + numpy.array([0,0,0.01], numpy.float32)
1419                                 a = numpy.concatenate((a[:-1], a[1:] + numpy.array([0,0,1], numpy.float32)), 1)
1420                                 a = a.reshape((len(a) * 2, 3))
1421                                 pointList = numpy.concatenate((pointList, a))
1422                 ret.append(opengl.GLVBO(pointList))
1423
1424                 return ret
1425
1426         def getObjectCenterPos(self):
1427                 if self._selectedObj is None:
1428                         return [0.0, 0.0, 0.0]
1429                 pos = self._selectedObj.getPosition()
1430                 size = self._selectedObj.getSize()
1431                 return [pos[0], pos[1], size[2]/2 - profile.getProfileSettingFloat('object_sink')]
1432
1433         def getObjectBoundaryCircle(self):
1434                 if self._selectedObj is None:
1435                         return 0.0
1436                 return self._selectedObj.getBoundaryCircle()
1437
1438         def getObjectSize(self):
1439                 if self._selectedObj is None:
1440                         return [0.0, 0.0, 0.0]
1441                 return self._selectedObj.getSize()
1442
1443         def getObjectMatrix(self):
1444                 if self._selectedObj is None:
1445                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
1446                 return self._selectedObj.getMatrix()
1447
1448 class shaderEditor(wx.Dialog):
1449         def __init__(self, parent, callback, v, f):
1450                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
1451                 self._callback = callback
1452                 s = wx.BoxSizer(wx.VERTICAL)
1453                 self.SetSizer(s)
1454                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
1455                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
1456                 s.Add(self._vertex, 1, flag=wx.EXPAND)
1457                 s.Add(self._fragment, 1, flag=wx.EXPAND)
1458
1459                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
1460                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
1461
1462                 self.SetPosition(self.GetParent().GetPosition())
1463                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
1464                 self.Show()
1465
1466         def OnText(self, e):
1467                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())