1 from __future__ import absolute_import
14 OpenGL.ERROR_CHECKING = False
15 from OpenGL.GLU import *
16 from OpenGL.GL import *
18 from Cura.gui.util import opengl
19 from Cura.gui.util import toolbarUtil
20 from Cura.gui import configBase
21 from Cura.gui import printWindow
22 from Cura.gui.util import dropTarget
23 from Cura.gui.util import taskbar
24 from Cura.gui.util import previewTools
25 from Cura.gui.util import openglGui
26 from Cura.util import validators
27 from Cura.util import profile
28 from Cura.util import util3d
29 from Cura.util import meshLoader
30 from Cura.util.meshLoaders import stl
31 from Cura.util import mesh
32 from Cura.util import sliceRun
33 from Cura.util import gcodeInterpreter
34 from Cura.util import explorer
39 class ProjectObject(object):
40 def __init__(self, parent, filename):
41 super(ProjectObject, self).__init__()
43 self.mesh = meshLoader.loadMesh(filename)
46 self.filename = filename
47 self.matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
50 self.modelDisplayList = None
51 self.modelDirty = True
53 self.centerX = self.getSize()[0]/2 + 5
54 self.centerY = self.getSize()[1]/2 + 5
58 def isSameExceptForPosition(self, other):
59 if self.filename != other.filename:
61 if self.matrix != other.matrix:
63 if self.profile != other.profile:
67 def updateMatrix(self):
68 self.mesh.matrix = self.matrix
69 self.mesh.processMatrix()
71 scaleX = numpy.linalg.norm(self.matrix[::,0].getA().flatten())
72 scaleY = numpy.linalg.norm(self.matrix[::,1].getA().flatten())
73 scaleZ = numpy.linalg.norm(self.matrix[::,2].getA().flatten())
74 self.parent.scaleXctrl.setValue(round(scaleX, 2))
75 self.parent.scaleYctrl.setValue(round(scaleY, 2))
76 self.parent.scaleZctrl.setValue(round(scaleZ, 2))
77 self.parent.scaleXmmctrl.setValue(round(self.getSize()[0], 2))
78 self.parent.scaleYmmctrl.setValue(round(self.getSize()[1], 2))
79 self.parent.scaleZmmctrl.setValue(round(self.getSize()[2], 2))
82 return self.mesh.getMinimum()
84 return self.mesh.getMaximum()
86 return self.mesh.getSize()
87 def getBoundaryCircle(self):
88 return self.mesh.boundaryCircleSize
91 p = ProjectObject(self.parent, self.filename)
93 p.centerX = self.centerX + 5
94 p.centerY = self.centerY + 5
96 p.filename = self.filename
97 p.matrix = self.matrix.copy()
98 p.profile = self.profile
105 size = self.getSize()
106 if self.centerX < size[0] / 2:
107 self.centerX = size[0] / 2
108 if self.centerY < size[1] / 2:
109 self.centerY = size[1] / 2
110 if self.centerX > self.parent.machineSize[0] - size[0] / 2:
111 self.centerX = self.parent.machineSize[0] - size[0] / 2
112 if self.centerY > self.parent.machineSize[1] - size[1] / 2:
113 self.centerY = self.parent.machineSize[1] - size[1] / 2
115 class projectPlanner(wx.Frame):
116 "Main user interface window"
118 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
120 wx.EVT_CLOSE(self, self.OnClose)
121 self.panel = wx.Panel(self, -1)
122 self.SetSizer(wx.BoxSizer(wx.VERTICAL))
123 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
125 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
128 self.selection = None
130 self.alwaysAutoPlace = profile.getPreference('planner_always_autoplace') == 'True'
132 self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
133 self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
134 self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
136 self.extruderOffset = [
137 numpy.array([0,0,0]),
138 numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),
139 numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),
140 numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]
142 self.toolbar = toolbarUtil.Toolbar(self.panel)
144 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
145 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
146 self.toolbar.AddSeparator()
148 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick).SetValue(self.alwaysAutoPlace)
149 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(not self.alwaysAutoPlace)
150 self.toolbar.AddSeparator()
151 toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
152 self.toolbar.AddSeparator()
153 toolbarUtil.NormalButton(self.toolbar, self.OnCutMesh, 'cut-mesh.png', 'Cut a plate STL into multiple STL files, and add those files to the project.\nNote: Splitting up plates sometimes takes a few minutes.')
154 toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')
155 self.toolbar.AddSeparator()
157 self.printOneAtATime = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Print one object at a time', callback=self.OnPrintTypeChange)
158 self.printAllAtOnce = toolbarUtil.RadioButton(self.toolbar, group, 'all-at-once-on.png', 'all-at-once-off.png', 'Print all the objects at once', callback=self.OnPrintTypeChange)
159 self.toolbar.AddSeparator()
160 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
162 self.toolbar.Realize()
164 self.toolbar2 = toolbarUtil.Toolbar(self.panel)
166 toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
167 toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
168 self.toolbar2.AddSeparator()
169 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
170 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
171 toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
172 toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to prepare a specific object.')
173 self.toolbar2.AddSeparator()
174 if not self.alwaysAutoPlace:
175 toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
176 toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Prepare to project into a gcode file.')
177 self.toolbar2.Realize()
179 sizer = wx.GridBagSizer(2,2)
180 self.panel.SetSizer(sizer)
181 self.glCanvas = PreviewGLCanvas(self.panel, self)
182 self.listbox = wx.ListBox(self.panel, -1, choices=[])
183 self.addButton = wx.Button(self.panel, -1, "Add")
184 self.remButton = wx.Button(self.panel, -1, "Remove")
185 self.sliceButton = wx.Button(self.panel, -1, "Prepare")
186 if not self.alwaysAutoPlace:
187 self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")
189 sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
190 sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
191 sizer.Add(self.glCanvas, (1,0), span=(5,1), flag=wx.EXPAND)
192 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
193 sizer.Add(self.addButton, (2,1), span=(1,1))
194 sizer.Add(self.remButton, (2,2), span=(1,1))
195 sizer.Add(self.sliceButton, (3,1), span=(1,1))
196 if not self.alwaysAutoPlace:
197 sizer.Add(self.autoPlaceButton, (3,2), span=(1,1))
198 sizer.AddGrowableCol(0)
199 sizer.AddGrowableRow(1)
201 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
202 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
203 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
204 if not self.alwaysAutoPlace:
205 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
206 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
208 panel = wx.Panel(self.panel, -1)
209 sizer.Add(panel, (5,1), span=(1,2))
211 sizer = wx.GridBagSizer(2,2)
212 panel.SetSizer(sizer)
215 self.rotateToolButton = openglGui.glRadioButton(self.glCanvas, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
216 self.scaleToolButton = openglGui.glRadioButton(self.glCanvas, 9, 'Scale', (1,-1), group, self.OnToolSelect)
217 self.mirrorToolButton = openglGui.glRadioButton(self.glCanvas, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
219 self.resetRotationButton = openglGui.glButton(self.glCanvas, 12, 'Reset', (0,-2), self.OnRotateReset)
220 self.layFlatButton = openglGui.glButton(self.glCanvas, 16, 'Lay flat', (0,-3), self.OnLayFlat)
222 self.resetScaleButton = openglGui.glButton(self.glCanvas, 13, 'Reset', (1,-2), self.OnScaleReset)
224 self.mirrorXButton = openglGui.glButton(self.glCanvas, 14, 'Mirror X', (2,-2), lambda : self.OnMirror(0))
225 self.mirrorYButton = openglGui.glButton(self.glCanvas, 18, 'Mirror Y', (2,-3), lambda : self.OnMirror(1))
226 self.mirrorZButton = openglGui.glButton(self.glCanvas, 22, 'Mirror Z', (2,-4), lambda : self.OnMirror(2))
228 self.scaleForm = openglGui.glFrame(self.glCanvas, (2, -2))
229 openglGui.glGuiLayoutGrid(self.scaleForm)
230 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
231 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
232 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
233 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
234 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
235 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
236 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
237 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
238 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
239 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
240 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
241 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
242 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
243 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
245 self.SetSize((800,600))
249 def OnToolSelect(self):
250 if self.rotateToolButton.getSelected():
251 self.tool = previewTools.toolRotate(self.glCanvas)
252 elif self.scaleToolButton.getSelected():
253 self.tool = previewTools.toolScale(self.glCanvas)
254 elif self.mirrorToolButton.getSelected():
255 self.tool = previewTools.toolNone(self.glCanvas)
257 self.tool = previewTools.toolNone(self.glCanvas)
258 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
259 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
260 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
261 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
262 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
263 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
264 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
265 self.glCanvas.Refresh()
267 def OnRotateReset(self):
268 if self.selection is None:
270 x = numpy.linalg.norm(self.selection.matrix[::,0].getA().flatten())
271 y = numpy.linalg.norm(self.selection.matrix[::,1].getA().flatten())
272 z = numpy.linalg.norm(self.selection.matrix[::,2].getA().flatten())
273 self.selection.matrix = numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
274 self.selection.updateMatrix()
277 if self.selection is None:
279 transformedVertexes = (numpy.matrix(self.selection.mesh.vertexes, copy = False) * self.selection.matrix).getA()
280 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
283 for v in transformedVertexes:
284 diff = v - minZvertex
285 len = math.sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])
288 dot = (diff[2] / len)
294 rad = -math.atan2(dotV[1], dotV[0])
295 self.selection.matrix *= numpy.matrix([[math.cos(rad), math.sin(rad), 0], [-math.sin(rad), math.cos(rad), 0], [0,0,1]], numpy.float64)
296 rad = -math.asin(dotMin)
297 self.selection.matrix *= numpy.matrix([[math.cos(rad), 0, math.sin(rad)], [0,1,0], [-math.sin(rad), 0, math.cos(rad)]], numpy.float64)
300 transformedVertexes = (numpy.matrix(self.selection.mesh.vertexes, copy = False) * self.selection.matrix).getA()
301 minZvertex = transformedVertexes[transformedVertexes.argmin(0)[2]]
304 for v in transformedVertexes:
305 diff = v - minZvertex
306 len = math.sqrt(diff[1] * diff[1] + diff[2] * diff[2])
309 dot = (diff[2] / len)
316 rad = math.asin(dotMin)
318 rad = -math.asin(dotMin)
319 self.selection.matrix *= numpy.matrix([[1,0,0], [0, math.cos(rad), math.sin(rad)], [0, -math.sin(rad), math.cos(rad)]], numpy.float64)
321 self.selection.updateMatrix()
323 def OnScaleReset(self):
324 if self.selection is None:
326 x = 1/numpy.linalg.norm(self.selection.matrix[::,0].getA().flatten())
327 y = 1/numpy.linalg.norm(self.selection.matrix[::,1].getA().flatten())
328 z = 1/numpy.linalg.norm(self.selection.matrix[::,2].getA().flatten())
329 self.selection.matrix *= numpy.matrix([[x,0,0],[0,y,0],[0,0,z]], numpy.float64)
330 self.selection.updateMatrix()
332 def OnMirror(self, axis):
333 if self.selection is None:
335 matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
336 matrix[axis][axis] = -1
337 self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
339 def OnScaleEntry(self, value, axis):
340 if self.selection is None:
346 scale = numpy.linalg.norm(self.selection.matrix[::,axis].getA().flatten())
347 scale = value / scale
350 if self.scaleUniform.getValue():
351 matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
353 matrix = [[1.0,0,0], [0, 1.0, 0], [0, 0, 1.0]]
354 matrix[axis][axis] = scale
355 self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
356 self.selection.updateMatrix()
358 def OnScaleEntryMM(self, value, axis):
363 scale = self.selection.getSize()[axis]
364 scale = value / scale
367 if self.scaleUniform.getValue():
368 matrix = [[scale,0,0], [0, scale, 0], [0, 0, scale]]
370 matrix = [[1,0,0], [0, 1, 0], [0, 0, 1]]
371 matrix[axis][axis] = scale
372 self.selection.matrix *= numpy.matrix(matrix, numpy.float64)
373 self.selection.updateMatrix()
375 def OnClose(self, e):
381 def OnPreferences(self, e):
382 prefDialog = preferencesDialog(self)
384 prefDialog.Show(True)
386 def OnCutMesh(self, e):
387 dlg=wx.FileDialog(self, "Open file to cut", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
388 dlg.SetWildcard(meshLoader.wildcardFilter())
389 if dlg.ShowModal() == wx.ID_OK:
390 filename = dlg.GetPath()
391 model = meshLoader.loadMesh(filename)
392 pd = wx.ProgressDialog('Splitting model.', 'Splitting model into multiple parts.', model.vertexCount, self, wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH)
393 parts = model.splitToParts(pd.Update)
395 partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))
396 stl.saveAsSTL(part, partFilename)
397 item = ProjectObject(self, partFilename)
398 self.list.append(item)
399 self.selection = item
400 self._updateListbox()
401 self.OnListSelect(None)
403 self.glCanvas.Refresh()
406 def OnDropFiles(self, filenames):
407 for filename in filenames:
408 item = ProjectObject(self, filename)
409 profile.putPreference('lastFile', item.filename)
410 self.list.append(item)
411 self.selection = item
412 self._updateListbox()
413 self.OnListSelect(None)
414 self.glCanvas.Refresh()
416 def OnPrintTypeChange(self):
418 if self.printAllAtOnce.GetValue():
420 if self.alwaysAutoPlace:
421 self.OnAutoPlace(None)
422 self.glCanvas.Refresh()
424 def OnSaveCombinedSTL(self, e):
425 dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
426 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
427 if dlg.ShowModal() == wx.ID_OK:
428 self._saveCombinedSTL(dlg.GetPath())
431 def _saveCombinedSTL(self, filename):
433 for item in self.list:
434 totalCount += item.mesh.vertexCount
436 output._prepareVertexCount(totalCount)
437 for item in self.list:
438 vMin = item.getMinimum()
439 vMax = item.getMaximum()
440 offset = - vMin - (vMax - vMin) / 2
441 offset += numpy.array([item.centerX, item.centerY, (vMax[2] - vMin[2]) / 2])
442 vertexes = (item.mesh.vertexes * item.matrix).getA() + offset
444 output.addVertex(v[0], v[1], v[2])
445 stl.saveAsSTL(output, filename)
447 def OnSaveProject(self, e):
448 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
449 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
450 if dlg.ShowModal() == wx.ID_OK:
451 cp = ConfigParser.ConfigParser()
453 for item in self.list:
454 section = 'model_%d' % (i)
455 cp.add_section(section)
456 cp.set(section, 'filename', item.filename.encode("utf-8"))
457 cp.set(section, 'centerX', str(item.centerX))
458 cp.set(section, 'centerY', str(item.centerY))
459 cp.set(section, 'scale', str(item.scale))
460 cp.set(section, 'rotate', str(item.rotate))
461 cp.set(section, 'flipX', str(item.flipX))
462 cp.set(section, 'flipY', str(item.flipY))
463 cp.set(section, 'flipZ', str(item.flipZ))
464 cp.set(section, 'swapXZ', str(item.swapXZ))
465 cp.set(section, 'swapYZ', str(item.swapYZ))
466 cp.set(section, 'extruder', str(item.extruder+1))
467 if item.profile != None:
468 cp.set(section, 'profile', item.profile)
470 cp.write(open(dlg.GetPath(), "w"))
473 def OnLoadProject(self, e):
474 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
475 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
476 if dlg.ShowModal() == wx.ID_OK:
477 cp = ConfigParser.ConfigParser()
478 cp.read(dlg.GetPath())
481 while cp.has_section('model_%d' % (i)):
482 section = 'model_%d' % (i)
484 item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
485 item.centerX = float(cp.get(section, 'centerX'))
486 item.centerY = float(cp.get(section, 'centerY'))
487 item.scale = float(cp.get(section, 'scale'))
488 item.rotate = float(cp.get(section, 'rotate'))
489 item.flipX = cp.get(section, 'flipX') == 'True'
490 item.flipY = cp.get(section, 'flipY') == 'True'
491 item.flipZ = cp.get(section, 'flipZ') == 'True'
492 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
493 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
494 if cp.has_option(section, 'extruder'):
495 item.extuder = int(cp.get(section, 'extruder')) - 1
496 if cp.has_option(section, 'profile'):
497 item.profile = cp.get(section, 'profile')
498 item.updateModelTransform()
501 self.list.append(item)
503 self.selected = self.list[0]
504 self._updateListbox()
505 self.OnListSelect(None)
506 self.glCanvas.Refresh()
511 self.glCanvas.yaw = 30
512 self.glCanvas.pitch = 60
513 self.glCanvas.zoom = 300
514 self.glCanvas.view3D = True
515 self.glCanvas.Refresh()
517 def OnTopClick(self):
518 self.glCanvas.view3D = False
519 self.glCanvas.zoom = self.machineSize[0] / 2 + 10
520 self.glCanvas.offsetX = 0
521 self.glCanvas.offsetY = 0
522 self.glCanvas.Refresh()
524 def OnListSelect(self, e):
525 if self.listbox.GetSelection() == -1:
527 self.selection = self.list[self.listbox.GetSelection()]
528 self.glCanvas.Refresh()
530 def OnAddModel(self, e):
531 dlg=wx.FileDialog(self, "Open file to print", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_MULTIPLE)
532 dlg.SetWildcard(meshLoader.wildcardFilter())
533 if dlg.ShowModal() == wx.ID_OK:
534 for filename in dlg.GetPaths():
535 item = ProjectObject(self, filename)
536 profile.putPreference('lastFile', item.filename)
537 self.list.append(item)
538 self.selection = item
539 self._updateListbox()
540 self.OnListSelect(None)
541 self.glCanvas.Refresh()
544 def OnRemModel(self, e):
545 if self.selection is None:
547 self.list.remove(self.selection)
548 self._updateListbox()
549 self.glCanvas.Refresh()
551 def OnMoveUp(self, e):
552 if self.selection is None:
554 i = self.listbox.GetSelection()
557 self.list.remove(self.selection)
558 self.list.insert(i-1, self.selection)
559 self._updateListbox()
560 self.glCanvas.Refresh()
562 def OnMoveDown(self, e):
563 if self.selection is None:
565 i = self.listbox.GetSelection()
566 if i == len(self.list) - 1:
568 self.list.remove(self.selection)
569 self.list.insert(i+1, self.selection)
570 self._updateListbox()
571 self.glCanvas.Refresh()
574 if self.selection is None:
577 item = self.selection.clone()
578 self.list.insert(self.list.index(self.selection), item)
579 self.selection = item
581 self._updateListbox()
582 self.glCanvas.Refresh()
584 def OnSetCustomProfile(self, e):
585 if self.selection is None:
588 dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
589 dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
590 if dlg.ShowModal() == wx.ID_OK:
591 self.selection.profile = dlg.GetPath()
593 self.selection.profile = None
594 self._updateListbox()
597 def _updateListbox(self):
599 for item in self.list:
600 if item.profile is not None:
601 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
603 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
604 if self.selection in self.list:
605 self.listbox.SetSelection(self.list.index(self.selection))
606 elif len(self.list) > 0:
607 self.selection = self.list[0]
608 self.listbox.SetSelection(0)
610 self.selection = None
611 self.listbox.SetSelection(-1)
612 if self.alwaysAutoPlace:
613 self.OnAutoPlace(None)
615 def OnAutoPlace(self, e):
616 bestAllowedSize = int(self.machineSize[1])
617 bestArea = self._doAutoPlace(bestAllowedSize)
618 for i in xrange(10, int(self.machineSize[1]), 10):
619 area = self._doAutoPlace(i)
623 self._doAutoPlace(bestAllowedSize)
624 if not self.alwaysAutoPlace:
625 for item in self.list:
627 self.glCanvas.Refresh()
629 def _doAutoPlace(self, allowedSizeY):
630 extraSizeMin, extraSizeMax = self.getExtraHeadSize()
632 if extraSizeMin[0] > extraSizeMax[0]:
633 posX = self.machineSize[0]
641 minX = self.machineSize[0]
642 minY = self.machineSize[1]
645 for item in self.list:
646 item.centerX = posX + item.getSize()[0] / 2 * dirX
647 item.centerY = posY + item.getSize()[1] / 2 * dirY
648 if item.centerY + item.getSize()[1] >= allowedSizeY:
650 posX = minX - extraSizeMax[0] - 1
652 posX = maxX + extraSizeMin[0] + 1
654 item.centerX = posX + item.getSize()[0] / 2 * dirX
655 item.centerY = posY + item.getSize()[1] / 2 * dirY
656 posY += item.getSize()[1] * dirY + extraSizeMin[1] + 1
657 minX = min(minX, item.centerX - item.getSize()[0] / 2)
658 minY = min(minY, item.centerY - item.getSize()[1] / 2)
659 maxX = max(maxX, item.centerX + item.getSize()[0] / 2)
660 maxY = max(maxY, item.centerY + item.getSize()[1] / 2)
662 for item in self.list:
664 item.centerX -= minX / 2
666 item.centerX += (self.machineSize[0] - maxX) / 2
667 item.centerY += (self.machineSize[1] - maxY) / 2
669 if minX < 0 or maxX > self.machineSize[0]:
670 return ((maxX - minX) + (maxY - minY)) * 100
672 return (maxX - minX) + (maxY - minY)
674 def OnSlice(self, e):
675 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
676 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
677 if dlg.ShowModal() != wx.ID_OK:
680 resultFilename = dlg.GetPath()
683 put = profile.setTempOverride
684 oldProfile = profile.getGlobalProfileString()
686 if self.printMode == 0:
689 for item in self.list:
690 fileList.append(item.filename)
691 if profile.getPreference('machine_center_is_zero') == 'True':
692 pos = [item.centerX - self.machineSize[0] / 2, item.centerY - self.machineSize[1] / 2]
694 pos = [item.centerX, item.centerY]
695 positionList.append(pos + item.matrix.getA().flatten().tolist())
697 sliceCommand = sliceRun.getSliceCommand(resultFilename, fileList, positionList)
699 self._saveCombinedSTL(resultFilename + "_temp_.stl")
700 sliceCommand = sliceRun.getSliceCommand(resultFilename, [resultFilename + "_temp_.stl"], [profile.getMachineCenterCoords()])
702 pspw = ProjectSliceProgressWindow(sliceCommand, resultFilename, len(self.list))
706 def getExtraHeadSize(self):
707 extraSizeMin = self.headSizeMin
708 extraSizeMax = self.headSizeMax
709 if profile.getProfileSettingFloat('skirt_line_count') > 0:
710 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
711 extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])
712 extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])
713 if profile.getProfileSetting('enable_raft') != 'False':
714 raftSize = profile.getProfileSettingFloat('raft_margin') * 2
715 extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])
716 extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])
717 if profile.getProfileSetting('support') != 'None':
718 extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])
719 extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])
721 if self.printMode == 1:
722 extraSizeMin = numpy.array([6.0, 6.0, 0])
723 extraSizeMax = numpy.array([6.0, 6.0, 0])
725 return extraSizeMin, extraSizeMax
727 class PreviewGLCanvas(openglGui.glGuiPanel):
728 def __init__(self, parent, projectPlannerWindow):
729 super(PreviewGLCanvas, self).__init__(parent)
730 self.parent = projectPlannerWindow
731 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
736 self.view3D = self.parent.alwaysAutoPlace
740 self.zoom = self.parent.machineSize[0] / 2 + 10
743 self.allowDrag = False
744 self.tempMatrix = None
746 self.objColor = profile.getPreferenceColour('model_colour')
748 def OnMouseLeftDown(self,e):
749 self.allowDrag = True
750 if not self.parent.alwaysAutoPlace and not self.view3D:
751 p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
752 p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
753 p0 -= self.viewTarget
754 p1 -= self.viewTarget
755 p0 -= self.getObjectCenterPos() - self.viewTarget
756 p1 -= self.getObjectCenterPos() - self.viewTarget
757 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
759 for item in self.parent.list:
760 iMin =-item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0])
761 iMax = item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0])
762 if iMin[0] <= cursorZ0[0] <= iMax[0] and iMin[1] <= cursorZ0[1] <= iMax[1]:
763 self.parent.selection = item
764 self.parent._updateListbox()
765 self.parent.OnListSelect(None)
767 def OnMouseMotion(self,e):
768 if self.viewport is not None:
769 p0 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
770 p1 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
771 p0 -= self.viewTarget
772 p1 -= self.viewTarget
773 p0 -= self.getObjectCenterPos() - self.viewTarget
774 p1 -= self.getObjectCenterPos() - self.viewTarget
775 if not e.Dragging() or self.dragType != 'tool':
776 self.parent.tool.OnMouseMove(p0, p1)
781 if self.allowDrag and e.Dragging() and e.LeftIsDown():
782 if self.dragType == '':
783 #Define the drag type depending on the cursor position.
784 self.dragType = 'viewRotate'
785 if self.parent.tool.OnDragStart(p0, p1):
786 self.dragType = 'tool'
787 if self.dragType == 'viewRotate':
789 self.yaw += e.GetX() - self.oldX
790 self.pitch -= e.GetY() - self.oldY
795 elif not self.parent.alwaysAutoPlace:
796 item = self.parent.selection
798 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
799 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
801 elif self.dragType == 'tool':
802 self.parent.tool.OnDrag(p0, p1)
804 if self.dragType != '':
805 if self.tempMatrix is not None:
806 self.parent.selection.matrix *= self.tempMatrix
807 self.parent.selection.updateMatrix()
808 self.tempMatrix = None
809 self.parent.tool.OnDragEnd()
811 self.allowDrag = False
812 if e.Dragging() and e.RightIsDown():
814 self.zoom += e.GetY() - self.oldY
821 def OnMouseWheel(self,e):
823 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
828 def OnEraseBackground(self,event):
829 #Workaround for windows background redraw flicker.
832 def OnSize(self,event):
835 def OnPaint(self,event):
836 opengl.InitGL(self, self.view3D, self.zoom)
838 glTranslate(0,0,-self.zoom)
839 glRotate(-self.pitch, 1,0,0)
840 glRotate(self.yaw, 0,0,1)
841 self.viewTarget = self.parent.machineSize / 2
842 self.viewTarget[2] = 0
843 glTranslate(-self.viewTarget[0], -self.viewTarget[1], -self.viewTarget[2])
845 self.viewport = glGetIntegerv(GL_VIEWPORT)
846 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
847 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
852 machineSize = self.parent.machineSize
853 extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()
855 for item in self.parent.list:
856 item.validPlacement = True
859 for idx1 in xrange(0, len(self.parent.list)):
860 item = self.parent.list[idx1]
861 iMin1 =-item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin #- self.parent.extruderOffset[item.extruder]
862 iMax1 = item.getSize() / 2 + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax #- self.parent.extruderOffset[item.extruder]
863 if iMin1[0] < -self.parent.headSizeMin[0] or iMin1[1] < -self.parent.headSizeMin[1]:
864 item.validPlacement = False
865 if iMax1[0] > machineSize[0] + self.parent.headSizeMax[0] or iMax1[1] > machineSize[1] + self.parent.headSizeMax[1]:
866 item.validPlacement = False
867 for idx2 in xrange(0, idx1):
868 item2 = self.parent.list[idx2]
869 iMin2 =-item2.getSize() / 2 + numpy.array([item2.centerX, item2.centerY, 0])
870 iMax2 = item2.getSize() / 2 + numpy.array([item2.centerX, item2.centerY, 0])
871 if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:
872 item.validPlacement = False
876 for item in self.parent.list:
877 if item == self.parent.selection:
879 if item.modelDisplayList is None:
880 item.modelDisplayList = glGenLists(1);
882 item.modelDirty = False
883 glNewList(item.modelDisplayList, GL_COMPILE)
884 opengl.DrawMesh(item.mesh)
887 if item.validPlacement:
888 if self.parent.selection == item:
889 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x + 0.2, self.objColor))
890 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
892 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor)
893 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
895 if self.parent.selection == item:
896 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
897 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
899 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
900 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
903 glEnable(GL_LIGHTING)
904 glTranslate(item.centerX, item.centerY, 0)
905 vMin = item.getMinimum()
906 vMax = item.getMaximum()
907 offset = - vMin - (vMax - vMin) / 2
908 matrix = opengl.convert3x3MatrixTo4x4(item.matrix)
910 glTranslate(0, 0, item.getSize()[2]/2)
911 if self.tempMatrix is not None and item == self.parent.selection:
912 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
913 glMultMatrixf(tempMatrix)
914 glTranslate(0, 0, -item.getSize()[2]/2)
915 glTranslate(offset[0], offset[1], -vMin[2])
916 glMultMatrixf(matrix)
917 glCallList(item.modelDisplayList)
920 vMin =-item.getSize() / 2
921 vMax = item.getSize() / 2
924 vMinHead = vMin - extraSizeMin# - self.parent.extruderOffset[item.extruder]
925 vMaxHead = vMax + extraSizeMax# - self.parent.extruderOffset[item.extruder]
927 glDisable(GL_LIGHTING)
929 if not self.parent.alwaysAutoPlace:
931 if self.parent.selection == item:
933 glColor3f(1.0,0.0,0.3)
935 glColor3f(1.0,0.0,1.0)
936 opengl.DrawBox(vMin, vMax)
938 glColor3f(1.0,0.3,0.0)
940 glColor3f(1.0,1.0,0.0)
941 opengl.DrawBox(vMinHead, vMaxHead)
944 glColor3f(0.5,0.0,0.1)
946 glColor3f(0.5,0.0,0.5)
947 opengl.DrawBox(vMinHead, vMaxHead)
950 glColor3f(0.7,0.1,0.0)
952 glColor3f(0.7,0.7,0.0)
953 opengl.DrawBox(vMin, vMax)
957 opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))
959 if self.parent.selection is not None:
961 glTranslate(self.parent.selection.centerX, self.parent.selection.centerY, self.parent.selection.getSize()[2]/2)
962 self.parent.tool.OnDraw()
965 def getObjectSize(self):
966 if self.parent.selection is not None:
967 return self.parent.selection.getSize()
969 def getObjectBoundaryCircle(self):
970 if self.parent.selection is not None:
971 return self.parent.selection.getBoundaryCircle()
973 def getObjectMatrix(self):
974 return self.parent.selection.matrix
975 def getObjectCenterPos(self):
976 if self.parent.selection is None:
978 return [self.parent.selection.centerX, self.parent.selection.centerY, self.getObjectSize()[2] / 2]
980 class ProjectSliceProgressWindow(wx.Frame):
981 def __init__(self, sliceCommand, resultFilename, fileCount):
982 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
983 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
985 self.sliceCommand = sliceCommand
986 self.resultFilename = resultFilename
987 self.fileCount = fileCount
989 self.prevStep = 'start'
990 self.totalDoneFactor = 0.0
991 self.startTime = time.time()
992 self.sliceStartTime = time.time()
994 self.sizer = wx.GridBagSizer(2, 2)
995 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
996 self.progressGauge = wx.Gauge(self, -1)
997 self.progressGauge.SetRange(10000)
998 self.progressGauge2 = wx.Gauge(self, -1)
999 self.progressGauge2.SetRange(self.fileCount)
1000 self.progressGauge2.SetValue(-1)
1001 self.abortButton = wx.Button(self, -1, "Abort")
1002 self.sizer.Add(self.statusText, (0,0), span=(1,5))
1003 self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)
1004 self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)
1006 self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)
1007 self.sizer.AddGrowableCol(0)
1008 self.sizer.AddGrowableRow(0)
1010 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
1011 self.SetSizer(self.sizer)
1015 threading.Thread(target=self.OnRun).start()
1017 def OnAbort(self, e):
1022 self.abortButton.SetLabel('Close')
1024 def SetProgress(self, stepName, layer, maxLayer):
1025 if self.prevStep != stepName:
1026 if stepName == 'slice':
1027 self.progressGauge2.SetValue(self.progressGauge2.GetValue() + 1)
1028 self.totalDoneFactor = 0
1029 self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
1030 newTime = time.time()
1031 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
1032 self.startTime = newTime
1033 self.prevStep = stepName
1035 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
1036 self.progressGauge.SetValue(int(progresValue))
1037 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
1038 taskbar.setProgress(self, 10000 * self.progressGauge2.GetValue() + int(progresValue), 10000 * self.fileCount)
1041 self.progressLog = []
1042 p = sliceRun.startSliceCommandProcess(self.sliceCommand)
1043 line = p.stdout.readline()
1044 while(len(line) > 0):
1045 line = line.rstrip()
1046 if line[0:9] == "Progress[" and line[-1:] == "]":
1047 progress = line[9:-1].split(":")
1048 if len(progress) > 2:
1049 maxValue = int(progress[2])
1050 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
1052 self.progressLog.append(line)
1053 wx.CallAfter(self.statusText.SetLabel, line)
1056 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
1058 line = p.stdout.readline()
1059 line = p.stderr.readline()
1060 while len(line) > 0:
1061 line = line.rstrip()
1062 self.progressLog.append(line)
1063 line = p.stderr.readline()
1064 self.returnCode = p.wait()
1065 self.progressGauge2.SetValue(self.fileCount)
1067 gcode = gcodeInterpreter.gcode()
1068 gcode.load(self.resultFilename)
1071 sliceTime = time.time() - self.sliceStartTime
1072 status = "Build: %s" % (self.resultFilename)
1073 status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
1074 status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
1075 status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
1076 cost = gcode.calculateCost()
1077 if cost is not None:
1078 status += "\nCost: %s" % (cost)
1079 profile.replaceGCodeTags(self.resultFilename, gcode)
1080 wx.CallAfter(self.statusText.SetLabel, status)
1081 wx.CallAfter(self.OnSliceDone)
1083 def _adjustNumberInLine(self, line, tag, f):
1084 m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)
1085 return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'
1087 def OnSliceDone(self):
1088 self.abortButton.Destroy()
1089 self.closeButton = wx.Button(self, -1, "Close")
1090 self.printButton = wx.Button(self, -1, "Print")
1091 self.logButton = wx.Button(self, -1, "Show log")
1092 self.sizer.Add(self.closeButton, (3,0), span=(1,1))
1093 self.sizer.Add(self.printButton, (3,1), span=(1,1))
1094 self.sizer.Add(self.logButton, (3,2), span=(1,1))
1095 if explorer.hasExplorer():
1096 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
1097 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
1098 self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))
1099 if profile.getPreference('sdpath') != '':
1100 self.copyToSDButton = wx.Button(self, -1, "To SDCard")
1101 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
1102 self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))
1103 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
1104 self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
1105 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
1108 taskbar.setBusy(self, False)
1110 def OnCopyToSD(self, e):
1111 filename = os.path.basename(self.resultFilename)
1112 if profile.getPreference('sdshortnames') == 'True':
1113 filename = sliceRun.getShortFilename(filename)
1114 shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))
1116 def OnOpenFileLocation(self, e):
1117 explorer.openExplorer(self.resultFilename)
1119 def OnPrint(self, e):
1120 printWindow.printFile(self.resultFilename)
1122 def OnShowLog(self, e):
1123 LogWindow('\n'.join(self.progressLog))
1125 class preferencesDialog(wx.Frame):
1126 def __init__(self, parent):
1127 super(preferencesDialog, self).__init__(None, title="Project Planner Preferences", style=wx.DEFAULT_DIALOG_STYLE)
1129 self.parent = parent
1130 wx.EVT_CLOSE(self, self.OnClose)
1132 self.panel = configBase.configPanelBase(self)
1133 extruderAmount = int(profile.getPreference('extruder_amount'))
1135 left, right, main = self.panel.CreateConfigPanel(self)
1136 configBase.TitleRow(left, 'User interface settings')
1137 c = configBase.SettingRow(left, 'Always auto place objects in planner', 'planner_always_autoplace', True, 'Disable this to allow manual placement in the project planner (requires restart).', type = 'preference')
1138 configBase.TitleRow(left, 'Machine head size')
1139 c = configBase.SettingRow(left, 'Head size - X towards home (mm)', 'extruder_head_size_min_x', '0', 'Size of your printer head in the X direction, on the Ultimaker your fan is in this direction.', type = 'preference')
1140 validators.validFloat(c, 0.1)
1141 c = configBase.SettingRow(left, 'Head size - X towards end (mm)', 'extruder_head_size_max_x', '0', 'Size of your printer head in the X direction.', type = 'preference')
1142 validators.validFloat(c, 0.1)
1143 c = configBase.SettingRow(left, 'Head size - Y towards home (mm)', 'extruder_head_size_min_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')
1144 validators.validFloat(c, 0.1)
1145 c = configBase.SettingRow(left, 'Head size - Y towards end (mm)', 'extruder_head_size_max_y', '0', 'Size of your printer head in the Y direction.', type = 'preference')
1146 validators.validFloat(c, 0.1)
1147 c = configBase.SettingRow(left, 'Head gantry height (mm)', 'extruder_head_size_height', '0', 'The tallest object height that will always fit under your printers gantry system when the printer head is at the lowest Z position.', type = 'preference')
1148 validators.validFloat(c)
1150 self.okButton = wx.Button(left, -1, 'Ok')
1151 left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
1152 self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
1154 self.MakeModal(True)
1158 def OnClose(self, e):
1159 self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
1160 self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
1161 self.parent.Refresh()
1163 self.MakeModal(False)
1166 class LogWindow(wx.Frame):
1167 def __init__(self, logText):
1168 super(LogWindow, self).__init__(None, title="Slice log")
1169 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
1170 self.SetSize((400,300))