1 from __future__ import absolute_import
\r
4 import wx, os, platform, types, webbrowser, math, subprocess, threading, time, re, shutil
\r
8 from wx import glcanvas
\r
11 OpenGL.ERROR_CHECKING = False
\r
12 from OpenGL.GLU import *
\r
13 from OpenGL.GL import *
\r
14 hasOpenGLlibs = True
\r
16 print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
\r
17 hasOpenGLlibs = False
\r
19 from gui import opengl
\r
20 from gui import toolbarUtil
\r
21 from gui import icon
\r
22 from gui import configBase
\r
23 from gui import printWindow
\r
24 from gui import dropTarget
\r
25 from gui import taskbar
\r
26 from util import validators
\r
27 from util import profile
\r
28 from util import util3d
\r
29 from util import meshLoader
\r
30 from util import stl
\r
31 from util import mesh
\r
32 from util import sliceRun
\r
33 from util import gcodeInterpreter
\r
34 from util import exporer
\r
36 class Action(object):
\r
39 class ProjectObject(object):
\r
40 def __init__(self, parent, filename):
\r
41 super(ProjectObject, self).__init__()
\r
43 self.mesh = meshLoader.loadMesh(filename)
\r
45 self.parent = parent
\r
46 self.filename = filename
\r
57 self.modelDisplayList = None
\r
58 self.modelDirty = False
\r
60 self.mesh.getMinimumZ()
\r
62 self.centerX = -self.getMinimum()[0] + 5
\r
63 self.centerY = -self.getMinimum()[1] + 5
\r
65 self.updateModelTransform()
\r
67 self.centerX = -self.getMinimum()[0] + 5
\r
68 self.centerY = -self.getMinimum()[1] + 5
\r
70 def isSameExceptForPosition(self, other):
\r
71 if self.filename != other.filename:
\r
73 if self.scale != other.scale:
\r
75 if self.rotate != other.rotate:
\r
77 if self.flipX != other.flipX:
\r
79 if self.flipY != other.flipY:
\r
81 if self.flipZ != other.flipZ:
\r
83 if self.swapXZ != other.swapXZ:
\r
85 if self.swapYZ != other.swapYZ:
\r
87 if self.extruder != other.extruder:
\r
89 if self.profile != other.profile:
\r
93 def updateModelTransform(self):
\r
94 self.mesh.setRotateMirror(self.rotate, self.flipX, self.flipY, self.flipZ, self.swapXZ, self.swapYZ)
\r
95 minZ = self.mesh.getMinimumZ()
\r
96 minV = self.getMinimum()
\r
97 maxV = self.getMaximum()
\r
98 self.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minZ])
\r
99 minZ = self.mesh.getMinimumZ()
\r
100 self.modelDirty = True
\r
102 def getMinimum(self):
\r
103 return self.mesh.getMinimum()
\r
104 def getMaximum(self):
\r
105 return self.mesh.getMaximum()
\r
107 return self.mesh.getSize()
\r
110 p = ProjectObject(self.parent, self.filename)
\r
112 p.centerX = self.centerX + 5
\r
113 p.centerY = self.centerY + 5
\r
115 p.filename = self.filename
\r
116 p.scale = self.scale
\r
117 p.rotate = self.rotate
\r
118 p.flipX = self.flipX
\r
119 p.flipY = self.flipY
\r
120 p.flipZ = self.flipZ
\r
121 p.swapXZ = self.swapXZ
\r
122 p.swapYZ = self.swapYZ
\r
123 p.extruder = self.extruder
\r
124 p.profile = self.profile
\r
126 p.updateModelTransform()
\r
131 if self.centerX < -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]:
\r
132 self.centerX = -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]
\r
133 if self.centerY < -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]:
\r
134 self.centerY = -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]
\r
135 if self.centerX > self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale:
\r
136 self.centerX = self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale
\r
137 if self.centerY > self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale:
\r
138 self.centerY = self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale
\r
140 class projectPlanner(wx.Frame):
\r
141 "Main user interface window"
\r
142 def __init__(self):
\r
143 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
\r
145 wx.EVT_CLOSE(self, self.OnClose)
\r
146 self.panel = wx.Panel(self, -1)
\r
147 self.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
148 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
149 #self.SetIcon(icon.getMainIcon())
\r
151 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
\r
154 self.selection = None
\r
156 self.alwaysAutoPlace = True
\r
158 self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
\r
159 self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
160 self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
162 self.extruderOffset = [
\r
163 numpy.array([0,0,0]),
\r
164 numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),
\r
165 numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),
\r
166 numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]
\r
168 self.toolbar = toolbarUtil.Toolbar(self.panel)
\r
170 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
\r
171 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
\r
172 self.toolbar.AddSeparator()
\r
174 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick).SetValue(self.alwaysAutoPlace)
\r
175 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(not self.alwaysAutoPlace)
\r
176 self.toolbar.AddSeparator()
\r
177 toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
\r
178 self.toolbar.AddSeparator()
\r
179 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.')
\r
180 toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')
\r
181 self.toolbar.AddSeparator()
\r
183 self.printOneAtATime = toolbarUtil.RadioButton(self.toolbar, group, 'view-normal-on.png', 'view-normal-off.png', 'Print one object at a time', callback=self.OnPrintTypeChange)
\r
184 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)
\r
185 self.toolbar.AddSeparator()
\r
186 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
\r
188 self.toolbar.Realize()
\r
190 self.toolbar2 = toolbarUtil.Toolbar(self.panel)
\r
192 toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
\r
193 toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
\r
194 self.toolbar2.AddSeparator()
\r
195 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
\r
196 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
\r
197 toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
\r
198 toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to prepare a specific object.')
\r
199 self.toolbar2.AddSeparator()
\r
200 if not self.alwaysAutoPlace:
\r
201 toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
\r
202 toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Prepare to project into a gcode file.')
\r
203 self.toolbar2.Realize()
\r
205 self.toolbar3 = toolbarUtil.Toolbar(self.panel)
\r
206 self.mirrorX = toolbarUtil.ToggleButton(self.toolbar3, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.OnMirrorChange)
\r
207 self.mirrorY = toolbarUtil.ToggleButton(self.toolbar3, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.OnMirrorChange)
\r
208 self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar3, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.OnMirrorChange)
\r
209 self.toolbar3.AddSeparator()
\r
212 self.swapXZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.OnMirrorChange)
\r
213 self.swapYZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.OnMirrorChange)
\r
214 self.toolbar3.Realize()
\r
216 sizer = wx.GridBagSizer(2,2)
\r
217 self.panel.SetSizer(sizer)
\r
218 self.preview = PreviewGLCanvas(self.panel, self)
\r
219 self.listbox = wx.ListBox(self.panel, -1, choices=[])
\r
220 self.addButton = wx.Button(self.panel, -1, "Add")
\r
221 self.remButton = wx.Button(self.panel, -1, "Remove")
\r
222 self.sliceButton = wx.Button(self.panel, -1, "Prepare")
\r
223 if not self.alwaysAutoPlace:
\r
224 self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")
\r
226 sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
227 sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
228 sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)
\r
229 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
\r
230 sizer.Add(self.toolbar3, (2,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
231 sizer.Add(self.addButton, (3,1), span=(1,1))
\r
232 sizer.Add(self.remButton, (3,2), span=(1,1))
\r
233 sizer.Add(self.sliceButton, (4,1), span=(1,1))
\r
234 if not self.alwaysAutoPlace:
\r
235 sizer.Add(self.autoPlaceButton, (4,2), span=(1,1))
\r
236 sizer.AddGrowableCol(0)
\r
237 sizer.AddGrowableRow(1)
\r
239 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
\r
240 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
\r
241 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
\r
242 if not self.alwaysAutoPlace:
\r
243 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
\r
244 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
\r
246 panel = wx.Panel(self.panel, -1)
\r
247 sizer.Add(panel, (5,1), span=(1,2))
\r
249 sizer = wx.GridBagSizer(2,2)
\r
250 panel.SetSizer(sizer)
\r
252 self.scaleCtrl = wx.TextCtrl(panel, -1, '')
\r
253 self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
254 self.rotateCtrl.SetRange(0, 360)
\r
256 sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
257 sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
258 sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
259 sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
261 if int(profile.getPreference('extruder_amount')) > 1:
\r
262 self.extruderCtrl = wx.ComboBox(panel, -1, '1', choices=map(str, range(1, int(profile.getPreference('extruder_amount'))+1)), style=wx.CB_DROPDOWN|wx.CB_READONLY)
\r
263 sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
264 sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
265 self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
\r
267 self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
\r
268 self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
\r
270 self.SetSize((800,600))
\r
272 def OnClose(self, e):
\r
275 def OnQuit(self, e):
\r
278 def OnPreferences(self, e):
\r
279 prefDialog = preferencesDialog(self)
\r
280 prefDialog.Centre()
\r
281 prefDialog.Show(True)
\r
283 def OnCutMesh(self, e):
\r
284 dlg=wx.FileDialog(self, "Open file to cut", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
285 dlg.SetWildcard(meshLoader.wildcardFilter())
\r
286 if dlg.ShowModal() == wx.ID_OK:
\r
287 filename = dlg.GetPath()
\r
288 model = meshLoader.loadMesh(filename)
\r
289 pd = wx.ProgressDialog('Splitting model.', 'Splitting model into multiple parts.', model.vertexCount, self, wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME | wx.PD_SMOOTH)
\r
290 parts = model.splitToParts(pd.Update)
\r
292 partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))
\r
293 stl.saveAsSTL(part, partFilename)
\r
294 item = ProjectObject(self, partFilename)
\r
295 self.list.append(item)
\r
296 self.selection = item
\r
297 self._updateListbox()
\r
298 self.OnListSelect(None)
\r
300 self.preview.Refresh()
\r
303 def OnDropFiles(self, filenames):
\r
304 for filename in filenames:
\r
305 item = ProjectObject(self, filename)
\r
306 profile.putPreference('lastFile', item.filename)
\r
307 self.list.append(item)
\r
308 self.selection = item
\r
309 self._updateListbox()
\r
310 self.OnListSelect(None)
\r
311 self.preview.Refresh()
\r
313 def OnPrintTypeChange(self):
\r
315 if self.printAllAtOnce.GetValue():
\r
317 if self.alwaysAutoPlace:
\r
318 self.OnAutoPlace(None)
\r
319 self.preview.Refresh()
\r
321 def OnSaveCombinedSTL(self, e):
\r
322 dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
323 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
324 if dlg.ShowModal() == wx.ID_OK:
\r
325 self._saveCombinedSTL(dlg.GetPath())
\r
328 def _saveCombinedSTL(self, filename):
\r
330 for item in self.list:
\r
331 totalCount += item.mesh.vertexCount
\r
332 output = mesh.mesh()
\r
333 output._prepareVertexCount(totalCount)
\r
334 for item in self.list:
\r
335 offset = numpy.array([item.centerX, item.centerY, 0])
\r
336 for v in item.mesh.vertexes:
\r
337 v0 = v * item.scale + offset
\r
338 output.addVertex(v0[0], v0[1], v0[2])
\r
339 stl.saveAsSTL(output, filename)
\r
341 def OnSaveProject(self, e):
\r
342 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
343 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
344 if dlg.ShowModal() == wx.ID_OK:
\r
345 cp = ConfigParser.ConfigParser()
\r
347 for item in self.list:
\r
348 section = 'model_%d' % (i)
\r
349 cp.add_section(section)
\r
350 cp.set(section, 'filename', item.filename.encode("utf-8"))
\r
351 cp.set(section, 'centerX', str(item.centerX))
\r
352 cp.set(section, 'centerY', str(item.centerY))
\r
353 cp.set(section, 'scale', str(item.scale))
\r
354 cp.set(section, 'rotate', str(item.rotate))
\r
355 cp.set(section, 'flipX', str(item.flipX))
\r
356 cp.set(section, 'flipY', str(item.flipY))
\r
357 cp.set(section, 'flipZ', str(item.flipZ))
\r
358 cp.set(section, 'swapXZ', str(item.swapXZ))
\r
359 cp.set(section, 'swapYZ', str(item.swapYZ))
\r
360 cp.set(section, 'extruder', str(item.extruder+1))
\r
361 if item.profile != None:
\r
362 cp.set(section, 'profile', item.profile)
\r
364 cp.write(open(dlg.GetPath(), "w"))
\r
367 def OnLoadProject(self, e):
\r
368 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
369 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
370 if dlg.ShowModal() == wx.ID_OK:
\r
371 cp = ConfigParser.ConfigParser()
\r
372 cp.read(dlg.GetPath())
\r
375 while cp.has_section('model_%d' % (i)):
\r
376 section = 'model_%d' % (i)
\r
378 item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
\r
379 item.centerX = float(cp.get(section, 'centerX'))
\r
380 item.centerY = float(cp.get(section, 'centerY'))
\r
381 item.scale = float(cp.get(section, 'scale'))
\r
382 item.rotate = float(cp.get(section, 'rotate'))
\r
383 item.flipX = cp.get(section, 'flipX') == 'True'
\r
384 item.flipY = cp.get(section, 'flipY') == 'True'
\r
385 item.flipZ = cp.get(section, 'flipZ') == 'True'
\r
386 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
\r
387 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
\r
388 if cp.has_option(section, 'extruder'):
\r
389 item.extuder = int(cp.get(section, 'extruder')) - 1
\r
390 if cp.has_option(section, 'profile'):
\r
391 item.profile = cp.get(section, 'profile')
\r
392 item.updateModelTransform()
\r
395 self.list.append(item)
\r
397 self.selected = self.list[0]
\r
398 self._updateListbox()
\r
399 self.OnListSelect(None)
\r
400 self.preview.Refresh()
\r
404 def On3DClick(self):
\r
405 self.preview.yaw = 30
\r
406 self.preview.pitch = 60
\r
407 self.preview.zoom = 300
\r
408 self.preview.view3D = True
\r
409 self.preview.Refresh()
\r
411 def OnTopClick(self):
\r
412 self.preview.view3D = False
\r
413 self.preview.zoom = self.machineSize[0] / 2 + 10
\r
414 self.preview.offsetX = 0
\r
415 self.preview.offsetY = 0
\r
416 self.preview.Refresh()
\r
418 def OnListSelect(self, e):
\r
419 if self.listbox.GetSelection() == -1:
\r
421 self.selection = self.list[self.listbox.GetSelection()]
\r
422 self.scaleCtrl.SetValue(str(self.selection.scale))
\r
423 self.rotateCtrl.SetValue(int(self.selection.rotate))
\r
424 if int(profile.getPreference('extruder_amount')) > 1:
\r
426 self.extruderCtrl.SetValue(str(self.selection.extruder+1))
\r
428 self.mirrorX.SetValue(self.selection.flipX)
\r
429 self.mirrorY.SetValue(self.selection.flipY)
\r
430 self.mirrorZ.SetValue(self.selection.flipZ)
\r
431 self.swapXZ.SetValue(self.selection.swapXZ)
\r
432 self.swapYZ.SetValue(self.selection.swapYZ)
\r
434 self.preview.Refresh()
\r
436 def OnAddModel(self, e):
\r
437 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)
\r
438 dlg.SetWildcard(meshLoader.wildcardFilter())
\r
439 if dlg.ShowModal() == wx.ID_OK:
\r
440 for filename in dlg.GetPaths():
\r
441 item = ProjectObject(self, filename)
\r
442 profile.putPreference('lastFile', item.filename)
\r
443 self.list.append(item)
\r
444 self.selection = item
\r
445 self._updateListbox()
\r
446 self.OnListSelect(None)
\r
447 self.preview.Refresh()
\r
450 def OnRemModel(self, e):
\r
451 if self.selection == None:
\r
453 self.list.remove(self.selection)
\r
454 self._updateListbox()
\r
455 self.preview.Refresh()
\r
457 def OnMoveUp(self, e):
\r
458 if self.selection == None:
\r
460 i = self.listbox.GetSelection()
\r
463 self.list.remove(self.selection)
\r
464 self.list.insert(i-1, self.selection)
\r
465 self._updateListbox()
\r
466 self.preview.Refresh()
\r
468 def OnMoveDown(self, e):
\r
469 if self.selection == None:
\r
471 i = self.listbox.GetSelection()
\r
472 if i == len(self.list) - 1:
\r
474 self.list.remove(self.selection)
\r
475 self.list.insert(i+1, self.selection)
\r
476 self._updateListbox()
\r
477 self.preview.Refresh()
\r
479 def OnCopy(self, e):
\r
480 if self.selection == None:
\r
483 item = self.selection.clone()
\r
484 self.list.insert(self.list.index(self.selection), item)
\r
485 self.selection = item
\r
487 self._updateListbox()
\r
488 self.preview.Refresh()
\r
490 def OnSetCustomProfile(self, e):
\r
491 if self.selection == None:
\r
494 dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
495 dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
\r
496 if dlg.ShowModal() == wx.ID_OK:
\r
497 self.selection.profile = dlg.GetPath()
\r
499 self.selection.profile = None
\r
500 self._updateListbox()
\r
503 def _updateListbox(self):
\r
504 self.listbox.Clear()
\r
505 for item in self.list:
\r
506 if item.profile != None:
\r
507 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
\r
509 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
510 if self.selection in self.list:
\r
511 self.listbox.SetSelection(self.list.index(self.selection))
\r
512 elif len(self.list) > 0:
\r
513 self.selection = self.list[0]
\r
514 self.listbox.SetSelection(0)
\r
516 self.selection = None
\r
517 self.listbox.SetSelection(-1)
\r
518 if self.alwaysAutoPlace:
\r
519 self.OnAutoPlace(None)
\r
521 def OnAutoPlace(self, e):
\r
522 bestAllowedSize = int(self.machineSize[1])
\r
523 bestArea = self._doAutoPlace(bestAllowedSize)
\r
524 for i in xrange(10, int(self.machineSize[1]), 10):
\r
525 area = self._doAutoPlace(i)
\r
526 if area < bestArea:
\r
527 bestAllowedSize = i
\r
529 self._doAutoPlace(bestAllowedSize)
\r
530 for item in self.list:
\r
532 self.preview.Refresh()
\r
534 def _doAutoPlace(self, allowedSizeY):
\r
535 extraSizeMin, extraSizeMax = self.getExtraHeadSize()
\r
537 if extraSizeMin[0] > extraSizeMax[0]:
\r
538 posX = self.machineSize[0]
\r
546 minX = self.machineSize[0]
\r
547 minY = self.machineSize[1]
\r
550 for item in self.list:
\r
551 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
552 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
553 if item.centerY + item.getSize()[1] >= allowedSizeY:
\r
555 posX = minX - extraSizeMax[0] - 1
\r
557 posX = maxX + extraSizeMin[0] + 1
\r
559 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
560 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
561 posY += item.getSize()[1] * item.scale * dirY + extraSizeMin[1] + 1
\r
562 minX = min(minX, item.centerX - item.getSize()[0] * item.scale / 2)
\r
563 minY = min(minY, item.centerY - item.getSize()[1] * item.scale / 2)
\r
564 maxX = max(maxX, item.centerX + item.getSize()[0] * item.scale / 2)
\r
565 maxY = max(maxY, item.centerY + item.getSize()[1] * item.scale / 2)
\r
567 for item in self.list:
\r
569 item.centerX -= minX / 2
\r
571 item.centerX += (self.machineSize[0] - maxX) / 2
\r
572 item.centerY += (self.machineSize[1] - maxY) / 2
\r
574 if minX < 0 or maxX > self.machineSize[0]:
\r
575 return ((maxX - minX) + (maxY - minY)) * 100
\r
577 return (maxX - minX) + (maxY - minY)
\r
579 def OnSlice(self, e):
\r
580 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
581 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
\r
582 if dlg.ShowModal() != wx.ID_OK:
\r
585 resultFilename = dlg.GetPath()
\r
588 put = profile.setTempOverride
\r
589 oldProfile = profile.getGlobalProfileString()
\r
591 put('add_start_end_gcode', 'False')
\r
592 put('gcode_extension', 'project_tmp')
\r
593 if self.printMode == 0:
\r
596 for item in self.list:
\r
597 if item.profile != None and os.path.isfile(item.profile):
\r
598 profile.loadGlobalProfile(item.profile)
\r
599 put('machine_center_x', item.centerX - self.extruderOffset[item.extruder][0])
\r
600 put('machine_center_y', item.centerY - self.extruderOffset[item.extruder][1])
\r
601 put('model_scale', item.scale)
\r
602 put('flip_x', item.flipX)
\r
603 put('flip_y', item.flipY)
\r
604 put('flip_z', item.flipZ)
\r
605 put('model_rotate_base', item.rotate)
\r
606 put('swap_xz', item.swapXZ)
\r
607 put('swap_yz', item.swapYZ)
\r
610 action.sliceCmd = sliceRun.getSliceCommand(item.filename)
\r
611 action.centerX = item.centerX
\r
612 action.centerY = item.centerY
\r
613 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
614 action.extruder = item.extruder
\r
615 action.filename = item.filename
\r
616 clearZ = max(clearZ, item.getSize()[2] * item.scale + 5.0)
\r
617 action.clearZ = clearZ
\r
618 action.leaveResultForNextSlice = False
\r
619 action.usePreviousSlice = False
\r
620 actionList.append(action)
\r
622 if self.list.index(item) > 0 and item.isSameExceptForPosition(self.list[self.list.index(item)-1]):
\r
623 actionList[-2].leaveResultForNextSlice = True
\r
624 actionList[-1].usePreviousSlice = True
\r
626 if item.profile != None:
\r
627 profile.loadGlobalProfileFromString(oldProfile)
\r
630 self._saveCombinedSTL(resultFilename + "_temp_.stl")
\r
631 put('model_scale', 1.0)
\r
632 put('flip_x', False)
\r
633 put('flip_y', False)
\r
634 put('flip_z', False)
\r
635 put('model_rotate_base', 0)
\r
636 put('swap_xz', False)
\r
637 put('swap_yz', False)
\r
641 action.sliceCmd = sliceRun.getSliceCommand(resultFilename + "_temp_.stl")
\r
642 action.centerX = profile.getProfileSettingFloat('machine_center_x')
\r
643 action.centerY = profile.getProfileSettingFloat('machine_center_y')
\r
644 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
645 action.extruder = 0
\r
646 action.filename = resultFilename + "_temp_.stl"
\r
648 action.leaveResultForNextSlice = False
\r
649 action.usePreviousSlice = False
\r
651 actionList.append(action)
\r
653 #Restore the old profile.
\r
654 profile.resetTempOverride()
\r
656 pspw = ProjectSliceProgressWindow(actionList, resultFilename)
\r
657 pspw.extruderOffset = self.extruderOffset
\r
661 def OnScaleChange(self, e):
\r
662 if self.selection == None:
\r
665 self.selection.scale = float(self.scaleCtrl.GetValue())
\r
667 self.selection.scale = 1.0
\r
668 self.preview.Refresh()
\r
670 def OnRotateChange(self, e):
\r
671 if self.selection == None:
\r
673 self.selection.rotate = float(self.rotateCtrl.GetValue())
\r
674 self.selection.updateModelTransform()
\r
675 self.preview.Refresh()
\r
677 def OnExtruderChange(self, e):
\r
678 if self.selection == None:
\r
680 self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
\r
681 self.preview.Refresh()
\r
683 def OnMirrorChange(self):
\r
684 if self.selection == None:
\r
686 self.selection.flipX = self.mirrorX.GetValue()
\r
687 self.selection.flipY = self.mirrorY.GetValue()
\r
688 self.selection.flipZ = self.mirrorZ.GetValue()
\r
689 self.selection.swapXZ = self.swapXZ.GetValue()
\r
690 self.selection.swapYZ = self.swapYZ.GetValue()
\r
691 self.selection.updateModelTransform()
\r
692 self.preview.Refresh()
\r
694 def getExtraHeadSize(self):
\r
695 extraSizeMin = self.headSizeMin
\r
696 extraSizeMax = self.headSizeMax
\r
697 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
698 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
699 extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])
\r
700 extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])
\r
701 if profile.getProfileSetting('enable_raft') != 'False':
\r
702 raftSize = profile.getProfileSettingFloat('raft_margin') * 2
\r
703 extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])
\r
704 extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])
\r
705 if profile.getProfileSetting('support') != 'None':
\r
706 extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])
\r
707 extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])
\r
709 if self.printMode == 1:
\r
710 extraSizeMin = numpy.array([6.0, 6.0, 0])
\r
711 extraSizeMax = numpy.array([6.0, 6.0, 0])
\r
713 return extraSizeMin, extraSizeMax
\r
715 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
716 def __init__(self, parent, projectPlannerWindow):
\r
717 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
718 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
719 self.parent = projectPlannerWindow
\r
720 self.context = glcanvas.GLContext(self)
\r
721 wx.EVT_PAINT(self, self.OnPaint)
\r
722 wx.EVT_SIZE(self, self.OnSize)
\r
723 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
724 wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
\r
725 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
726 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
731 self.view3D = self.parent.alwaysAutoPlace
\r
735 self.zoom = self.parent.machineSize[0] / 2 + 10
\r
736 self.allowDrag = False
\r
738 self.objColor = profile.getPreferenceColour('model_colour')
\r
740 def OnMouseLeftDown(self,e):
\r
741 self.allowDrag = True
\r
742 if not self.parent.alwaysAutoPlace and not self.view3D:
\r
743 #TODO: Translate mouse X/Y to 3D X/Y/Z
\r
744 for item in self.parent.list:
\r
745 iMin = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]
\r
746 iMax = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - self.parent.extruderOffset[item.extruder]
\r
748 def OnMouseMotion(self,e):
\r
749 if self.allowDrag and e.Dragging() and e.LeftIsDown():
\r
751 self.yaw += e.GetX() - self.oldX
\r
752 self.pitch -= e.GetY() - self.oldY
\r
753 if self.pitch > 170:
\r
755 if self.pitch < 10:
\r
757 elif not self.parent.alwaysAutoPlace:
\r
758 item = self.parent.selection
\r
760 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
761 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
765 self.allowDrag = False
\r
766 if e.Dragging() and e.RightIsDown():
\r
768 self.zoom += e.GetY() - self.oldY
\r
772 self.oldX = e.GetX()
\r
773 self.oldY = e.GetY()
\r
775 def OnMouseWheel(self,e):
\r
777 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
778 if self.zoom < 1.0:
\r
782 def OnEraseBackground(self,event):
\r
783 #Workaround for windows background redraw flicker.
\r
786 def OnSize(self,event):
\r
789 def OnPaint(self,event):
\r
790 dc = wx.PaintDC(self)
\r
791 if not hasOpenGLlibs:
\r
793 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
795 self.SetCurrent(self.context)
\r
796 opengl.InitGL(self, self.view3D, self.zoom)
\r
798 glTranslate(0,0,-self.zoom)
\r
799 glRotate(-self.pitch, 1,0,0)
\r
800 glRotate(self.yaw, 0,0,1)
\r
802 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
803 glTranslate(-self.parent.machineSize[0]/2, -self.parent.machineSize[1]/2, 0)
\r
809 machineSize = self.parent.machineSize
\r
810 extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()
\r
812 for item in self.parent.list:
\r
813 item.validPlacement = True
\r
814 item.gotHit = False
\r
816 for idx1 in xrange(0, len(self.parent.list)):
\r
817 item = self.parent.list[idx1]
\r
818 iMin1 = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
819 iMax1 = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
820 for idx2 in xrange(0, idx1):
\r
821 item2 = self.parent.list[idx2]
\r
822 iMin2 = (item2.getMinimum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
823 iMax2 = (item2.getMaximum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
824 if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:
\r
825 item.validPlacement = False
\r
826 item2.gotHit = True
\r
828 seenSelected = False
\r
829 for item in self.parent.list:
\r
830 if item == self.parent.selection:
\r
831 seenSelected = True
\r
832 if item.modelDisplayList == None:
\r
833 item.modelDisplayList = glGenLists(1);
\r
834 if item.modelDirty:
\r
835 item.modelDirty = False
\r
836 modelSize = item.getMaximum() - item.getMinimum()
\r
837 glNewList(item.modelDisplayList, GL_COMPILE)
\r
838 opengl.DrawMesh(item.mesh)
\r
841 if item.validPlacement:
\r
842 if self.parent.selection == item:
\r
843 glLightfv(GL_LIGHT0, GL_DIFFUSE, map(lambda x: x + 0.2, self.objColor))
\r
844 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
\r
846 glLightfv(GL_LIGHT0, GL_DIFFUSE, self.objColor)
\r
847 glLightfv(GL_LIGHT0, GL_AMBIENT, map(lambda x: x / 2, self.objColor))
\r
849 if self.parent.selection == item:
\r
850 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
851 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
853 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
854 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
857 glEnable(GL_LIGHTING)
\r
858 glTranslate(item.centerX, item.centerY, 0)
\r
860 glScalef(item.scale, item.scale, item.scale)
\r
861 glCallList(item.modelDisplayList)
\r
864 vMin = item.getMinimum() * item.scale
\r
865 vMax = item.getMaximum() * item.scale
\r
866 vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
867 vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
869 glDisable(GL_LIGHTING)
\r
871 if not self.parent.alwaysAutoPlace:
\r
872 if self.parent.selection == item:
\r
874 glColor3f(1.0,0.0,0.3)
\r
876 glColor3f(1.0,0.0,1.0)
\r
877 opengl.DrawBox(vMin, vMax)
\r
879 glColor3f(1.0,0.3,0.0)
\r
881 glColor3f(1.0,1.0,0.0)
\r
882 opengl.DrawBox(vMinHead, vMaxHead)
\r
885 glColor3f(0.5,0.0,0.1)
\r
887 glColor3f(0.5,0.0,0.5)
\r
888 opengl.DrawBox(vMinHead, vMaxHead)
\r
891 glColor3f(0.7,0.1,0.0)
\r
893 glColor3f(0.7,0.7,0.0)
\r
894 opengl.DrawBox(vMin, vMax)
\r
898 opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))
\r
901 class ProjectSliceProgressWindow(wx.Frame):
\r
902 def __init__(self, actionList, resultFilename):
\r
903 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
\r
904 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
906 self.actionList = actionList
\r
907 self.resultFilename = resultFilename
\r
909 self.prevStep = 'start'
\r
910 self.totalDoneFactor = 0.0
\r
911 self.startTime = time.time()
\r
912 self.sliceStartTime = time.time()
\r
914 self.sizer = wx.GridBagSizer(2, 2)
\r
915 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
\r
916 self.progressGauge = wx.Gauge(self, -1)
\r
917 self.progressGauge.SetRange(10000)
\r
918 self.progressGauge2 = wx.Gauge(self, -1)
\r
919 self.progressGauge2.SetRange(len(self.actionList))
\r
920 self.abortButton = wx.Button(self, -1, "Abort")
\r
921 self.sizer.Add(self.statusText, (0,0), span=(1,5))
\r
922 self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)
\r
923 self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)
\r
925 self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)
\r
926 self.sizer.AddGrowableCol(0)
\r
927 self.sizer.AddGrowableRow(0)
\r
929 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
\r
930 self.SetSizer(self.sizer)
\r
934 threading.Thread(target=self.OnRun).start()
\r
936 def OnAbort(self, e):
\r
941 self.abortButton.SetLabel('Close')
\r
943 def SetProgress(self, stepName, layer, maxLayer):
\r
944 if self.prevStep != stepName:
\r
945 self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
\r
946 newTime = time.time()
\r
947 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
\r
948 self.startTime = newTime
\r
949 self.prevStep = stepName
\r
951 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
\r
952 self.progressGauge.SetValue(int(progresValue))
\r
953 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
\r
954 taskbar.setProgress(self, 10000 * self.progressGauge2.GetValue() + int(progresValue), 10000 * len(self.actionList))
\r
957 resultFile = open(self.resultFilename, "w")
\r
958 put = profile.setTempOverride
\r
959 self.progressLog = []
\r
960 for action in self.actionList:
\r
961 wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))
\r
962 if not action.usePreviousSlice:
\r
963 p = sliceRun.startSliceCommandProcess(action.sliceCmd)
\r
964 line = p.stdout.readline()
\r
967 while(len(line) > 0):
\r
968 line = line.rstrip()
\r
969 if line[0:9] == "Progress[" and line[-1:] == "]":
\r
970 progress = line[9:-1].split(":")
\r
971 if len(progress) > 2:
\r
972 maxValue = int(progress[2])
\r
973 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
\r
975 self.progressLog.append(line)
\r
976 wx.CallAfter(self.statusText.SetLabel, line)
\r
979 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
\r
982 line = p.stdout.readline()
\r
983 self.returnCode = p.wait()
\r
985 put('machine_center_x', action.centerX - self.extruderOffset[action.extruder][0])
\r
986 put('machine_center_y', action.centerY - self.extruderOffset[action.extruder][1])
\r
987 put('clear_z', action.clearZ)
\r
988 put('extruder', action.extruder)
\r
989 put('print_temperature', action.temperature)
\r
991 if action == self.actionList[0]:
\r
992 resultFile.write(';TYPE:CUSTOM\n')
\r
993 resultFile.write('T%d\n' % (action.extruder))
\r
994 currentExtruder = action.extruder
\r
995 prevTemp = action.temperature
\r
996 startGCode = profile.getAlterationFileContents('start.gcode')
\r
997 startGCode = startGCode.replace('?filename?', 'Multiple files')
\r
998 resultFile.write(startGCode)
\r
1000 #reset the extrusion length, and move to the next object center.
\r
1001 resultFile.write(';TYPE:CUSTOM\n')
\r
1002 if prevTemp != action.temperature and action.temperature > 0:
\r
1003 resultFile.write('M104 S%d\n' % (int(action.temperature)))
\r
1004 prevTemp = action.temperature
\r
1005 resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
\r
1006 resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
\r
1007 profile.resetTempOverride()
\r
1009 if not action.usePreviousSlice:
\r
1010 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
1011 data = f.read(4096)
\r
1013 resultFile.write(data)
\r
1014 data = f.read(4096)
\r
1016 savedCenterX = action.centerX
\r
1017 savedCenterY = action.centerY
\r
1019 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
1021 if line[0] != ';':
\r
1023 line = self._adjustNumberInLine(line, 'X', action.centerX - savedCenterX)
\r
1025 line = self._adjustNumberInLine(line, 'Y', action.centerY - savedCenterY)
\r
1026 resultFile.write(line)
\r
1029 if not action.leaveResultForNextSlice:
\r
1030 os.remove(sliceRun.getExportFilename(action.filename, "project_tmp"))
\r
1032 wx.CallAfter(self.progressGauge.SetValue, 10000)
\r
1033 self.totalDoneFactor = 0.0
\r
1034 wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
\r
1036 resultFile.write(';TYPE:CUSTOM\n')
\r
1037 if len(self.actionList) > 1 and self.actionList[-1].clearZ > 1:
\r
1038 #only move to higher Z if we have sliced more then 1 object. This solves the "move into print after printing" problem with the print-all-at-once option.
\r
1039 resultFile.write('G1 Z%f F%f\n' % (self.actionList[-1].clearZ, profile.getProfileSettingFloat('max_z_speed') * 60))
\r
1040 resultFile.write(profile.getAlterationFileContents('end.gcode'))
\r
1041 resultFile.close()
\r
1043 gcode = gcodeInterpreter.gcode()
\r
1044 gcode.load(self.resultFilename)
\r
1047 sliceTime = time.time() - self.sliceStartTime
\r
1048 status = "Build: %s" % (self.resultFilename)
\r
1049 status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
\r
1050 status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
\r
1051 status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
\r
1052 cost = gcode.calculateCost()
\r
1054 status += "\nCost: %s" % (cost)
\r
1055 profile.replaceGCodeTags(self.resultFilename, gcode)
\r
1056 wx.CallAfter(self.statusText.SetLabel, status)
\r
1057 wx.CallAfter(self.OnSliceDone)
\r
1059 def _adjustNumberInLine(self, line, tag, f):
\r
1060 m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)
\r
1061 return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'
\r
1063 def OnSliceDone(self):
\r
1064 self.abortButton.Destroy()
\r
1065 self.closeButton = wx.Button(self, -1, "Close")
\r
1066 self.printButton = wx.Button(self, -1, "Print")
\r
1067 self.logButton = wx.Button(self, -1, "Show log")
\r
1068 self.sizer.Add(self.closeButton, (3,0), span=(1,1))
\r
1069 self.sizer.Add(self.printButton, (3,1), span=(1,1))
\r
1070 self.sizer.Add(self.logButton, (3,2), span=(1,1))
\r
1071 if exporer.hasExporer():
\r
1072 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
\r
1073 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
\r
1074 self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))
\r
1075 if profile.getPreference('sdpath') != '':
\r
1076 self.copyToSDButton = wx.Button(self, -1, "To SDCard")
\r
1077 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
\r
1078 self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))
\r
1079 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
\r
1080 self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
\r
1081 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
\r
1084 taskbar.setBusy(self, False)
\r
1086 def OnCopyToSD(self, e):
\r
1087 filename = os.path.basename(self.resultFilename)
\r
1088 if profile.getPreference('sdshortnames') == 'True':
\r
1089 filename = sliceRun.getShortFilename(filename)
\r
1090 shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))
\r
1092 def OnOpenFileLocation(self, e):
\r
1093 exporer.openExporer(self.resultFilename)
\r
1095 def OnPrint(self, e):
\r
1096 printWindow.printFile(self.resultFilename)
\r
1098 def OnShowLog(self, e):
\r
1099 LogWindow('\n'.join(self.progressLog))
\r
1101 class preferencesDialog(configBase.configWindowBase):
\r
1102 def __init__(self, parent):
\r
1103 super(preferencesDialog, self).__init__(title="Project Planner Preferences")
\r
1105 self.parent = parent
\r
1106 wx.EVT_CLOSE(self, self.OnClose)
\r
1108 extruderAmount = int(profile.getPreference('extruder_amount'))
\r
1110 left, right, main = self.CreateConfigPanel(self)
\r
1111 configBase.TitleRow(left, 'Machine head size')
\r
1112 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')
\r
1113 validators.validFloat(c, 0.1)
\r
1114 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')
\r
1115 validators.validFloat(c, 0.1)
\r
1116 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')
\r
1117 validators.validFloat(c, 0.1)
\r
1118 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')
\r
1119 validators.validFloat(c, 0.1)
\r
1120 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')
\r
1121 validators.validFloat(c)
\r
1123 self.okButton = wx.Button(left, -1, 'Ok')
\r
1124 left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
\r
1125 self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
\r
1127 self.MakeModal(True)
\r
1131 def OnClose(self, e):
\r
1132 self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
1133 self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
1134 self.parent.Refresh()
\r
1136 self.MakeModal(False)
\r
1139 class LogWindow(wx.Frame):
\r
1140 def __init__(self, logText):
\r
1141 super(LogWindow, self).__init__(None, title="Slice log")
\r
1142 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
1143 self.SetSize((400,300))
\r
1148 app = wx.App(False)
\r
1149 projectPlanner().Show(True)
\r
1152 if __name__ == '__main__':
\r