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 util import validators
\r
26 from util import profile
\r
27 from util import util3d
\r
28 from util import meshLoader
\r
29 from util import stl
\r
30 from util import mesh
\r
31 from util import sliceRun
\r
32 from util import gcodeInterpreter
\r
33 from util import exporer
\r
35 class Action(object):
\r
38 class ProjectObject(object):
\r
39 def __init__(self, parent, filename):
\r
40 super(ProjectObject, self).__init__()
\r
42 self.mesh = meshLoader.loadMesh(filename)
\r
44 self.parent = parent
\r
45 self.filename = filename
\r
56 self.modelDisplayList = None
\r
57 self.modelDirty = False
\r
59 self.mesh.getMinimumZ()
\r
61 self.centerX = -self.getMinimum()[0] + 5
\r
62 self.centerY = -self.getMinimum()[1] + 5
\r
64 self.updateModelTransform()
\r
66 self.centerX = -self.getMinimum()[0] + 5
\r
67 self.centerY = -self.getMinimum()[1] + 5
\r
69 def isSameExceptForPosition(self, other):
\r
70 if self.filename != other.filename:
\r
72 if self.scale != other.scale:
\r
74 if self.rotate != other.rotate:
\r
76 if self.flipX != other.flipX:
\r
78 if self.flipY != other.flipY:
\r
80 if self.flipZ != other.flipZ:
\r
82 if self.swapXZ != other.swapXZ:
\r
84 if self.swapYZ != other.swapYZ:
\r
86 if self.extruder != other.extruder:
\r
88 if self.profile != other.profile:
\r
92 def updateModelTransform(self):
\r
93 self.mesh.setRotateMirror(self.rotate, self.flipX, self.flipY, self.flipZ, self.swapXZ, self.swapYZ)
\r
94 minZ = self.mesh.getMinimumZ()
\r
95 minV = self.getMinimum()
\r
96 maxV = self.getMaximum()
\r
97 self.mesh.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minZ])
\r
98 minZ = self.mesh.getMinimumZ()
\r
99 self.modelDirty = True
\r
101 def getMinimum(self):
\r
102 return self.mesh.getMinimum()
\r
103 def getMaximum(self):
\r
104 return self.mesh.getMaximum()
\r
106 return self.mesh.getSize()
\r
109 p = ProjectObject(self.parent, self.filename)
\r
111 p.centerX = self.centerX + 5
\r
112 p.centerY = self.centerY + 5
\r
114 p.filename = self.filename
\r
115 p.scale = self.scale
\r
116 p.rotate = self.rotate
\r
117 p.flipX = self.flipX
\r
118 p.flipY = self.flipY
\r
119 p.flipZ = self.flipZ
\r
120 p.swapXZ = self.swapXZ
\r
121 p.swapYZ = self.swapYZ
\r
122 p.extruder = self.extruder
\r
123 p.profile = self.profile
\r
125 p.updateModelTransform()
\r
130 if self.centerX < -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]:
\r
131 self.centerX = -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]
\r
132 if self.centerY < -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]:
\r
133 self.centerY = -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]
\r
134 if self.centerX > self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale:
\r
135 self.centerX = self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale
\r
136 if self.centerY > self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale:
\r
137 self.centerY = self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale
\r
139 class projectPlanner(wx.Frame):
\r
140 "Main user interface window"
\r
141 def __init__(self):
\r
142 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
\r
144 wx.EVT_CLOSE(self, self.OnClose)
\r
145 self.panel = wx.Panel(self, -1)
\r
146 self.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
147 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
148 #self.SetIcon(icon.getMainIcon())
\r
150 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
\r
153 self.selection = None
\r
156 self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
\r
157 self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
158 self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
160 self.extruderOffset = [
\r
161 numpy.array([0,0,0]),
\r
162 numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),
\r
163 numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),
\r
164 numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]
\r
166 self.toolbar = toolbarUtil.Toolbar(self.panel)
\r
168 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
\r
169 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
\r
170 self.toolbar.AddSeparator()
\r
172 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
173 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
\r
174 self.toolbar.AddSeparator()
\r
175 toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
\r
176 self.toolbar.AddSeparator()
\r
177 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
178 toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')
\r
179 self.toolbar.AddSeparator()
\r
181 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
182 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
183 self.toolbar.AddSeparator()
\r
184 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
\r
186 self.toolbar.Realize()
\r
188 self.toolbar2 = toolbarUtil.Toolbar(self.panel)
\r
190 toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
\r
191 toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
\r
192 self.toolbar2.AddSeparator()
\r
193 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
\r
194 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
\r
195 toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
\r
196 toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to slice a specific object.')
\r
197 self.toolbar2.AddSeparator()
\r
198 toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
\r
199 toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Slice to project into a gcode file.')
\r
200 self.toolbar2.Realize()
\r
202 self.toolbar3 = toolbarUtil.Toolbar(self.panel)
\r
203 self.mirrorX = toolbarUtil.ToggleButton(self.toolbar3, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.OnMirrorChange)
\r
204 self.mirrorY = toolbarUtil.ToggleButton(self.toolbar3, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.OnMirrorChange)
\r
205 self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar3, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.OnMirrorChange)
\r
206 self.toolbar3.AddSeparator()
\r
209 self.swapXZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.OnMirrorChange)
\r
210 self.swapYZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.OnMirrorChange)
\r
211 self.toolbar3.Realize()
\r
213 sizer = wx.GridBagSizer(2,2)
\r
214 self.panel.SetSizer(sizer)
\r
215 self.preview = PreviewGLCanvas(self.panel, self)
\r
216 self.listbox = wx.ListBox(self.panel, -1, choices=[])
\r
217 self.addButton = wx.Button(self.panel, -1, "Add")
\r
218 self.remButton = wx.Button(self.panel, -1, "Remove")
\r
219 self.sliceButton = wx.Button(self.panel, -1, "Slice")
\r
220 self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")
\r
222 sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
223 sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
224 sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)
\r
225 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
\r
226 sizer.Add(self.toolbar3, (2,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
227 sizer.Add(self.addButton, (3,1), span=(1,1))
\r
228 sizer.Add(self.remButton, (3,2), span=(1,1))
\r
229 sizer.Add(self.sliceButton, (4,1), span=(1,1))
\r
230 sizer.Add(self.autoPlaceButton, (4,2), span=(1,1))
\r
231 sizer.AddGrowableCol(0)
\r
232 sizer.AddGrowableRow(1)
\r
234 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
\r
235 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
\r
236 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
\r
237 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
\r
238 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
\r
240 panel = wx.Panel(self.panel, -1)
\r
241 sizer.Add(panel, (5,1), span=(1,2))
\r
243 sizer = wx.GridBagSizer(2,2)
\r
244 panel.SetSizer(sizer)
\r
246 self.scaleCtrl = wx.TextCtrl(panel, -1, '')
\r
247 self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
248 self.rotateCtrl.SetRange(0, 360)
\r
250 sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
251 sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
252 sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
253 sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
255 if int(profile.getPreference('extruder_amount')) > 1:
\r
256 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
257 sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
258 sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
259 self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
\r
261 self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
\r
262 self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
\r
264 self.SetSize((800,600))
\r
266 def OnClose(self, e):
\r
269 def OnQuit(self, e):
\r
272 def OnPreferences(self, e):
\r
273 prefDialog = preferencesDialog(self)
\r
274 prefDialog.Centre()
\r
275 prefDialog.Show(True)
\r
277 def OnCutMesh(self, e):
\r
278 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
279 dlg.SetWildcard(meshLoader.wildcardFilter())
\r
280 if dlg.ShowModal() == wx.ID_OK:
\r
281 filename = dlg.GetPath()
\r
282 model = meshLoader.loadMesh(filename)
\r
283 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
284 parts = model.splitToParts(pd.Update)
\r
286 partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))
\r
287 stl.saveAsSTL(part, partFilename)
\r
288 item = ProjectObject(self, partFilename)
\r
289 self.list.append(item)
\r
290 self.selection = item
\r
291 self._updateListbox()
\r
292 self.OnListSelect(None)
\r
294 self.preview.Refresh()
\r
297 def OnDropFiles(self, filenames):
\r
298 for filename in filenames:
\r
299 item = ProjectObject(self, filename)
\r
300 profile.putPreference('lastFile', item.filename)
\r
301 self.list.append(item)
\r
302 self.selection = item
\r
303 self._updateListbox()
\r
304 self.OnListSelect(None)
\r
305 self.preview.Refresh()
\r
307 def OnPrintTypeChange(self):
\r
309 if self.printAllAtOnce.GetValue():
\r
311 self.preview.Refresh()
\r
313 def OnSaveCombinedSTL(self, e):
\r
314 dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
315 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
316 if dlg.ShowModal() == wx.ID_OK:
\r
317 self._saveCombinedSTL(dlg.GetPath())
\r
320 def _saveCombinedSTL(self, filename):
\r
322 for item in self.list:
\r
323 totalCount += item.mesh.vertexCount
\r
324 output = mesh.mesh()
\r
325 output._prepareVertexCount(totalCount)
\r
326 for item in self.list:
\r
327 offset = numpy.array([item.centerX, item.centerY, 0])
\r
328 for v in item.mesh.vertexes:
\r
329 v0 = v * item.scale + offset
\r
330 output.addVertex(v0[0], v0[1], v0[2])
\r
331 stl.saveAsSTL(output, filename)
\r
333 def OnSaveProject(self, e):
\r
334 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
335 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
336 if dlg.ShowModal() == wx.ID_OK:
\r
337 cp = ConfigParser.ConfigParser()
\r
339 for item in self.list:
\r
340 section = 'model_%d' % (i)
\r
341 cp.add_section(section)
\r
342 cp.set(section, 'filename', item.filename.encode("utf-8"))
\r
343 cp.set(section, 'centerX', str(item.centerX))
\r
344 cp.set(section, 'centerY', str(item.centerY))
\r
345 cp.set(section, 'scale', str(item.scale))
\r
346 cp.set(section, 'rotate', str(item.rotate))
\r
347 cp.set(section, 'flipX', str(item.flipX))
\r
348 cp.set(section, 'flipY', str(item.flipY))
\r
349 cp.set(section, 'flipZ', str(item.flipZ))
\r
350 cp.set(section, 'swapXZ', str(item.swapXZ))
\r
351 cp.set(section, 'swapYZ', str(item.swapYZ))
\r
352 cp.set(section, 'extruder', str(item.extruder+1))
\r
353 if item.profile != None:
\r
354 cp.set(section, 'profile', item.profile)
\r
356 cp.write(open(dlg.GetPath(), "w"))
\r
359 def OnLoadProject(self, e):
\r
360 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
361 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
362 if dlg.ShowModal() == wx.ID_OK:
\r
363 cp = ConfigParser.ConfigParser()
\r
364 cp.read(dlg.GetPath())
\r
367 while cp.has_section('model_%d' % (i)):
\r
368 section = 'model_%d' % (i)
\r
370 item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
\r
371 item.centerX = float(cp.get(section, 'centerX'))
\r
372 item.centerY = float(cp.get(section, 'centerY'))
\r
373 item.scale = float(cp.get(section, 'scale'))
\r
374 item.rotate = float(cp.get(section, 'rotate'))
\r
375 item.flipX = cp.get(section, 'flipX') == 'True'
\r
376 item.flipY = cp.get(section, 'flipY') == 'True'
\r
377 item.flipZ = cp.get(section, 'flipZ') == 'True'
\r
378 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
\r
379 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
\r
380 if cp.has_option(section, 'extruder'):
\r
381 item.extuder = int(cp.get(section, 'extruder')) - 1
\r
382 if cp.has_option(section, 'profile'):
\r
383 item.profile = cp.get(section, 'profile')
\r
384 item.updateModelTransform()
\r
387 self.list.append(item)
\r
389 self.selected = self.list[0]
\r
390 self._updateListbox()
\r
391 self.OnListSelect(None)
\r
392 self.preview.Refresh()
\r
396 def On3DClick(self):
\r
397 self.preview.yaw = 30
\r
398 self.preview.pitch = 60
\r
399 self.preview.zoom = 300
\r
400 self.preview.view3D = True
\r
401 self.preview.Refresh()
\r
403 def OnTopClick(self):
\r
404 self.preview.view3D = False
\r
405 self.preview.zoom = self.machineSize[0] / 2 + 10
\r
406 self.preview.offsetX = 0
\r
407 self.preview.offsetY = 0
\r
408 self.preview.Refresh()
\r
410 def OnListSelect(self, e):
\r
411 if self.listbox.GetSelection() == -1:
\r
413 self.selection = self.list[self.listbox.GetSelection()]
\r
414 self.scaleCtrl.SetValue(str(self.selection.scale))
\r
415 self.rotateCtrl.SetValue(int(self.selection.rotate))
\r
416 if int(profile.getPreference('extruder_amount')) > 1:
\r
418 self.extruderCtrl.SetValue(str(self.selection.extruder+1))
\r
420 self.mirrorX.SetValue(self.selection.flipX)
\r
421 self.mirrorY.SetValue(self.selection.flipY)
\r
422 self.mirrorZ.SetValue(self.selection.flipZ)
\r
423 self.swapXZ.SetValue(self.selection.swapXZ)
\r
424 self.swapYZ.SetValue(self.selection.swapYZ)
\r
426 self.preview.Refresh()
\r
428 def OnAddModel(self, e):
\r
429 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
430 dlg.SetWildcard(meshLoader.wildcardFilter())
\r
431 if dlg.ShowModal() == wx.ID_OK:
\r
432 for filename in dlg.GetPaths():
\r
433 item = ProjectObject(self, filename)
\r
434 profile.putPreference('lastFile', item.filename)
\r
435 self.list.append(item)
\r
436 self.selection = item
\r
437 self._updateListbox()
\r
438 self.OnListSelect(None)
\r
439 self.preview.Refresh()
\r
442 def OnRemModel(self, e):
\r
443 if self.selection == None:
\r
445 self.list.remove(self.selection)
\r
446 self._updateListbox()
\r
447 self.preview.Refresh()
\r
449 def OnMoveUp(self, e):
\r
450 if self.selection == None:
\r
452 i = self.listbox.GetSelection()
\r
455 self.list.remove(self.selection)
\r
456 self.list.insert(i-1, self.selection)
\r
457 self._updateListbox()
\r
458 self.preview.Refresh()
\r
460 def OnMoveDown(self, e):
\r
461 if self.selection == None:
\r
463 i = self.listbox.GetSelection()
\r
464 if i == len(self.list) - 1:
\r
466 self.list.remove(self.selection)
\r
467 self.list.insert(i+1, self.selection)
\r
468 self._updateListbox()
\r
469 self.preview.Refresh()
\r
471 def OnCopy(self, e):
\r
472 if self.selection == None:
\r
475 item = self.selection.clone()
\r
476 self.list.insert(self.list.index(self.selection), item)
\r
477 self.selection = item
\r
479 self._updateListbox()
\r
480 self.preview.Refresh()
\r
482 def OnSetCustomProfile(self, e):
\r
483 if self.selection == None:
\r
486 dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
487 dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
\r
488 if dlg.ShowModal() == wx.ID_OK:
\r
489 self.selection.profile = dlg.GetPath()
\r
491 self.selection.profile = None
\r
492 self._updateListbox()
\r
495 def _updateListbox(self):
\r
496 self.listbox.Clear()
\r
497 for item in self.list:
\r
498 if item.profile != None:
\r
499 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
\r
501 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
502 if self.selection in self.list:
\r
503 self.listbox.SetSelection(self.list.index(self.selection))
\r
504 elif len(self.list) > 0:
\r
505 self.selection = self.list[0]
\r
506 self.listbox.SetSelection(0)
\r
508 self.selection = None
\r
509 self.listbox.SetSelection(-1)
\r
511 def OnAutoPlace(self, e):
\r
512 bestAllowedSize = int(self.machineSize[1])
\r
513 bestArea = self._doAutoPlace(bestAllowedSize)
\r
514 for i in xrange(10, int(self.machineSize[1]), 10):
\r
515 area = self._doAutoPlace(i)
\r
516 if area < bestArea:
\r
517 bestAllowedSize = i
\r
519 self._doAutoPlace(bestAllowedSize)
\r
520 for item in self.list:
\r
522 self.preview.Refresh()
\r
524 def _doAutoPlace(self, allowedSizeY):
\r
525 extraSizeMin, extraSizeMax = self.getExtraHeadSize()
\r
527 if extraSizeMin[0] > extraSizeMax[0]:
\r
528 posX = self.machineSize[0]
\r
536 minX = self.machineSize[0]
\r
537 minY = self.machineSize[1]
\r
540 for item in self.list:
\r
541 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
542 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
543 if item.centerY + item.getSize()[1] >= allowedSizeY:
\r
545 posX = minX - extraSizeMax[0] - 1
\r
547 posX = maxX + extraSizeMin[0] + 1
\r
549 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
550 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
551 posY += item.getSize()[1] * item.scale * dirY + extraSizeMin[1] + 1
\r
552 minX = min(minX, item.centerX - item.getSize()[0] * item.scale / 2)
\r
553 minY = min(minY, item.centerY - item.getSize()[1] * item.scale / 2)
\r
554 maxX = max(maxX, item.centerX + item.getSize()[0] * item.scale / 2)
\r
555 maxY = max(maxY, item.centerY + item.getSize()[1] * item.scale / 2)
\r
557 for item in self.list:
\r
559 item.centerX -= minX / 2
\r
561 item.centerX += (self.machineSize[0] - maxX) / 2
\r
562 item.centerY += (self.machineSize[1] - maxY) / 2
\r
564 if minX < 0 or maxX > self.machineSize[0]:
\r
565 return ((maxX - minX) + (maxY - minY)) * 100
\r
567 return (maxX - minX) + (maxY - minY)
\r
569 def OnSlice(self, e):
\r
570 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
571 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
\r
572 if dlg.ShowModal() != wx.ID_OK:
\r
575 resultFilename = dlg.GetPath()
\r
578 put = profile.setTempOverride
\r
579 oldProfile = profile.getGlobalProfileString()
\r
581 put('add_start_end_gcode', 'False')
\r
582 put('gcode_extension', 'project_tmp')
\r
583 if self.printMode == 0:
\r
586 for item in self.list:
\r
587 if item.profile != None and os.path.isfile(item.profile):
\r
588 profile.loadGlobalProfile(item.profile)
\r
589 put('machine_center_x', item.centerX - self.extruderOffset[item.extruder][0])
\r
590 put('machine_center_y', item.centerY - self.extruderOffset[item.extruder][1])
\r
591 put('model_scale', item.scale)
\r
592 put('flip_x', item.flipX)
\r
593 put('flip_y', item.flipY)
\r
594 put('flip_z', item.flipZ)
\r
595 put('model_rotate_base', item.rotate)
\r
596 put('swap_xz', item.swapXZ)
\r
597 put('swap_yz', item.swapYZ)
\r
600 action.sliceCmd = sliceRun.getSliceCommand(item.filename)
\r
601 action.centerX = item.centerX
\r
602 action.centerY = item.centerY
\r
603 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
604 action.extruder = item.extruder
\r
605 action.filename = item.filename
\r
606 clearZ = max(clearZ, item.getSize()[2] * item.scale + 5.0)
\r
607 action.clearZ = clearZ
\r
608 action.leaveResultForNextSlice = False
\r
609 action.usePreviousSlice = False
\r
610 actionList.append(action)
\r
612 if self.list.index(item) > 0 and item.isSameExceptForPosition(self.list[self.list.index(item)-1]):
\r
613 actionList[-2].leaveResultForNextSlice = True
\r
614 actionList[-1].usePreviousSlice = True
\r
616 if item.profile != None:
\r
617 profile.loadGlobalProfileFromString(oldProfile)
\r
620 self._saveCombinedSTL(resultFilename + "_temp_.stl")
\r
621 put('model_scale', 1.0)
\r
622 put('flip_x', False)
\r
623 put('flip_y', False)
\r
624 put('flip_z', False)
\r
625 put('model_rotate_base', 0)
\r
626 put('swap_xz', False)
\r
627 put('swap_yz', False)
\r
631 action.sliceCmd = sliceRun.getSliceCommand(resultFilename + "_temp_.stl")
\r
632 action.centerX = profile.getProfileSettingFloat('machine_center_x')
\r
633 action.centerY = profile.getProfileSettingFloat('machine_center_y')
\r
634 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
635 action.extruder = 0
\r
636 action.filename = resultFilename + "_temp_.stl"
\r
638 action.leaveResultForNextSlice = False
\r
639 action.usePreviousSlice = False
\r
641 actionList.append(action)
\r
643 #Restore the old profile.
\r
644 profile.resetTempOverride()
\r
646 pspw = ProjectSliceProgressWindow(actionList, resultFilename)
\r
647 pspw.extruderOffset = self.extruderOffset
\r
651 def OnScaleChange(self, e):
\r
652 if self.selection == None:
\r
655 self.selection.scale = float(self.scaleCtrl.GetValue())
\r
657 self.selection.scale = 1.0
\r
658 self.preview.Refresh()
\r
660 def OnRotateChange(self, e):
\r
661 if self.selection == None:
\r
663 self.selection.rotate = float(self.rotateCtrl.GetValue())
\r
664 self.selection.updateModelTransform()
\r
665 self.preview.Refresh()
\r
667 def OnExtruderChange(self, e):
\r
668 if self.selection == None:
\r
670 self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
\r
671 self.preview.Refresh()
\r
673 def OnMirrorChange(self):
\r
674 if self.selection == None:
\r
676 self.selection.flipX = self.mirrorX.GetValue()
\r
677 self.selection.flipY = self.mirrorY.GetValue()
\r
678 self.selection.flipZ = self.mirrorZ.GetValue()
\r
679 self.selection.swapXZ = self.swapXZ.GetValue()
\r
680 self.selection.swapYZ = self.swapYZ.GetValue()
\r
681 self.selection.updateModelTransform()
\r
682 self.preview.Refresh()
\r
684 def getExtraHeadSize(self):
\r
685 extraSizeMin = self.headSizeMin
\r
686 extraSizeMax = self.headSizeMax
\r
687 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
688 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
689 extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])
\r
690 extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])
\r
691 if profile.getProfileSetting('enable_raft') != 'False':
\r
692 raftSize = profile.getProfileSettingFloat('raft_margin') * 2
\r
693 extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])
\r
694 extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])
\r
695 if profile.getProfileSetting('support') != 'None':
\r
696 extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])
\r
697 extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])
\r
699 if self.printMode == 1:
\r
700 extraSizeMin = numpy.array([6.0, 6.0, 0])
\r
701 extraSizeMax = numpy.array([6.0, 6.0, 0])
\r
703 return extraSizeMin, extraSizeMax
\r
705 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
706 def __init__(self, parent, projectPlannerWindow):
\r
707 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
708 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
709 self.parent = projectPlannerWindow
\r
710 self.context = glcanvas.GLContext(self)
\r
711 wx.EVT_PAINT(self, self.OnPaint)
\r
712 wx.EVT_SIZE(self, self.OnSize)
\r
713 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
714 wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
\r
715 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
716 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
719 self.zoom = self.parent.machineSize[0] / 2 + 10
\r
722 self.view3D = False
\r
723 self.allowDrag = False
\r
725 def OnMouseLeftDown(self,e):
\r
726 self.allowDrag = True
\r
728 def OnMouseMotion(self,e):
\r
729 if self.allowDrag and e.Dragging() and e.LeftIsDown():
\r
731 self.yaw += e.GetX() - self.oldX
\r
732 self.pitch -= e.GetY() - self.oldY
\r
733 if self.pitch > 170:
\r
735 if self.pitch < 10:
\r
738 item = self.parent.selection
\r
740 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
741 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
745 self.allowDrag = False
\r
746 if e.Dragging() and e.RightIsDown():
\r
748 self.zoom += e.GetY() - self.oldY
\r
752 self.oldX = e.GetX()
\r
753 self.oldY = e.GetY()
\r
755 def OnMouseWheel(self,e):
\r
757 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
758 if self.zoom < 1.0:
\r
762 def OnEraseBackground(self,event):
\r
763 #Workaround for windows background redraw flicker.
\r
766 def OnSize(self,event):
\r
769 def OnPaint(self,event):
\r
770 dc = wx.PaintDC(self)
\r
771 if not hasOpenGLlibs:
\r
773 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
775 self.SetCurrent(self.context)
\r
776 opengl.InitGL(self, self.view3D, self.zoom)
\r
778 glTranslate(0,0,-self.zoom)
\r
779 glRotate(-self.pitch, 1,0,0)
\r
780 glRotate(self.yaw, 0,0,1)
\r
782 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
783 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
784 glTranslate(-self.parent.machineSize[0]/2, -self.parent.machineSize[1]/2, 0)
\r
790 machineSize = self.parent.machineSize
\r
791 extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()
\r
793 for item in self.parent.list:
\r
794 item.validPlacement = True
\r
795 item.gotHit = False
\r
797 for idx1 in xrange(0, len(self.parent.list)):
\r
798 item = self.parent.list[idx1]
\r
799 iMin1 = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
800 iMax1 = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
801 for idx2 in xrange(0, idx1):
\r
802 item2 = self.parent.list[idx2]
\r
803 iMin2 = (item2.getMinimum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
804 iMax2 = (item2.getMaximum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
805 if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:
\r
806 item.validPlacement = False
\r
807 item2.gotHit = True
\r
809 seenSelected = False
\r
810 for item in self.parent.list:
\r
811 if item == self.parent.selection:
\r
812 seenSelected = True
\r
813 if item.modelDisplayList == None:
\r
814 item.modelDisplayList = glGenLists(1);
\r
815 if item.modelDirty:
\r
816 item.modelDirty = False
\r
817 modelSize = item.getMaximum() - item.getMinimum()
\r
818 glNewList(item.modelDisplayList, GL_COMPILE)
\r
819 opengl.DrawMesh(item.mesh)
\r
822 if item.validPlacement:
\r
823 if self.parent.selection == item:
\r
824 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.9, 0.7, 1.0])
\r
825 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.3, 0.2, 0.0])
\r
827 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.8, 0.6, 1.0])
\r
828 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 0.0])
\r
830 if self.parent.selection == item:
\r
831 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
832 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
834 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
835 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
838 glEnable(GL_LIGHTING)
\r
839 glTranslate(item.centerX, item.centerY, 0)
\r
841 glScalef(item.scale, item.scale, item.scale)
\r
842 glCallList(item.modelDisplayList)
\r
845 vMin = item.getMinimum() * item.scale
\r
846 vMax = item.getMaximum() * item.scale
\r
847 vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
848 vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
850 glDisable(GL_LIGHTING)
\r
852 if self.parent.selection == item:
\r
854 glColor3f(1.0,0.0,0.3)
\r
856 glColor3f(1.0,0.0,1.0)
\r
857 opengl.DrawBox(vMin, vMax)
\r
859 glColor3f(1.0,0.3,0.0)
\r
861 glColor3f(1.0,1.0,0.0)
\r
862 opengl.DrawBox(vMinHead, vMaxHead)
\r
865 glColor3f(0.5,0.0,0.1)
\r
867 glColor3f(0.5,0.0,0.5)
\r
868 opengl.DrawBox(vMinHead, vMaxHead)
\r
871 glColor3f(0.7,0.1,0.0)
\r
873 glColor3f(0.7,0.7,0.0)
\r
874 opengl.DrawBox(vMin, vMax)
\r
878 opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))
\r
881 class ProjectSliceProgressWindow(wx.Frame):
\r
882 def __init__(self, actionList, resultFilename):
\r
883 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
\r
884 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
886 self.actionList = actionList
\r
887 self.resultFilename = resultFilename
\r
889 self.prevStep = 'start'
\r
890 self.totalDoneFactor = 0.0
\r
891 self.startTime = time.time()
\r
892 self.sliceStartTime = time.time()
\r
894 self.sizer = wx.GridBagSizer(2, 2)
\r
895 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
\r
896 self.progressGauge = wx.Gauge(self, -1)
\r
897 self.progressGauge.SetRange(10000)
\r
898 self.progressGauge2 = wx.Gauge(self, -1)
\r
899 self.progressGauge2.SetRange(len(self.actionList))
\r
900 self.abortButton = wx.Button(self, -1, "Abort")
\r
901 self.sizer.Add(self.statusText, (0,0), span=(1,5))
\r
902 self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)
\r
903 self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)
\r
905 self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)
\r
906 self.sizer.AddGrowableCol(0)
\r
907 self.sizer.AddGrowableRow(0)
\r
909 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
\r
910 self.SetSizer(self.sizer)
\r
914 threading.Thread(target=self.OnRun).start()
\r
916 def OnAbort(self, e):
\r
921 self.abortButton.SetLabel('Close')
\r
923 def SetProgress(self, stepName, layer, maxLayer):
\r
924 if self.prevStep != stepName:
\r
925 self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
\r
926 newTime = time.time()
\r
927 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
\r
928 self.startTime = newTime
\r
929 self.prevStep = stepName
\r
931 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
\r
932 self.progressGauge.SetValue(int(progresValue))
\r
933 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
\r
936 resultFile = open(self.resultFilename, "w")
\r
937 put = profile.setTempOverride
\r
938 self.progressLog = []
\r
939 for action in self.actionList:
\r
940 wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))
\r
941 if not action.usePreviousSlice:
\r
942 p = sliceRun.startSliceCommandProcess(action.sliceCmd)
\r
943 line = p.stdout.readline()
\r
946 while(len(line) > 0):
\r
947 line = line.rstrip()
\r
948 if line[0:9] == "Progress[" and line[-1:] == "]":
\r
949 progress = line[9:-1].split(":")
\r
950 if len(progress) > 2:
\r
951 maxValue = int(progress[2])
\r
952 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
\r
955 self.progressLog.append(line)
\r
956 wx.CallAfter(self.statusText.SetLabel, line)
\r
959 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
\r
962 line = p.stdout.readline()
\r
963 self.returnCode = p.wait()
\r
965 put('machine_center_x', action.centerX - self.extruderOffset[action.extruder][0])
\r
966 put('machine_center_y', action.centerY - self.extruderOffset[action.extruder][1])
\r
967 put('clear_z', action.clearZ)
\r
968 put('extruder', action.extruder)
\r
969 put('print_temperature', action.temperature)
\r
971 if action == self.actionList[0]:
\r
972 resultFile.write(';TYPE:CUSTOM\n')
\r
973 resultFile.write('T%d\n' % (action.extruder))
\r
974 currentExtruder = action.extruder
\r
975 prevTemp = action.temperature
\r
976 resultFile.write(profile.getAlterationFileContents('start.gcode'))
\r
978 #reset the extrusion length, and move to the next object center.
\r
979 resultFile.write(';TYPE:CUSTOM\n')
\r
980 if prevTemp != action.temperature and action.temperature > 0:
\r
981 resultFile.write('M104 S%d\n' % (int(action.temperature)))
\r
982 prevTemp = action.temperature
\r
983 resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
\r
984 resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
\r
985 profile.resetTempOverride()
\r
987 if not action.usePreviousSlice:
\r
988 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
989 data = f.read(4096)
\r
991 resultFile.write(data)
\r
992 data = f.read(4096)
\r
994 savedCenterX = action.centerX
\r
995 savedCenterY = action.centerY
\r
997 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
1001 line = self._adjustNumberInLine(line, 'X', action.centerX - savedCenterX)
\r
1003 line = self._adjustNumberInLine(line, 'Y', action.centerY - savedCenterY)
\r
1004 resultFile.write(line)
\r
1007 if not action.leaveResultForNextSlice:
\r
1008 os.remove(sliceRun.getExportFilename(action.filename, "project_tmp"))
\r
1010 wx.CallAfter(self.progressGauge.SetValue, 10000)
\r
1011 self.totalDoneFactor = 0.0
\r
1012 wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
\r
1014 resultFile.write(';TYPE:CUSTOM\n')
\r
1015 if len(self.actionList) > 1 and self.actionList[-1].clearZ > 1:
\r
1016 #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
1017 resultFile.write('G1 Z%f F%f\n' % (self.actionList[-1].clearZ, profile.getProfileSettingFloat('max_z_speed') * 60))
\r
1018 resultFile.write(profile.getAlterationFileContents('end.gcode'))
\r
1019 resultFile.close()
\r
1021 gcode = gcodeInterpreter.gcode()
\r
1022 gcode.load(self.resultFilename)
\r
1025 sliceTime = time.time() - self.sliceStartTime
\r
1026 status = "Build: %s" % (self.resultFilename)
\r
1027 status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
\r
1028 status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
\r
1029 status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
\r
1030 cost = gcode.calculateCost()
\r
1032 status += "\nCost: %s" % (cost)
\r
1033 profile.replaceGCodeTags(self.resultFilename, gcode)
\r
1034 wx.CallAfter(self.statusText.SetLabel, status)
\r
1035 wx.CallAfter(self.OnSliceDone)
\r
1037 def _adjustNumberInLine(self, line, tag, f):
\r
1038 m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)
\r
1039 return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'
\r
1041 def OnSliceDone(self):
\r
1042 self.abortButton.Destroy()
\r
1043 self.closeButton = wx.Button(self, -1, "Close")
\r
1044 self.printButton = wx.Button(self, -1, "Print")
\r
1045 self.logButton = wx.Button(self, -1, "Show log")
\r
1046 self.sizer.Add(self.closeButton, (3,0), span=(1,1))
\r
1047 self.sizer.Add(self.printButton, (3,1), span=(1,1))
\r
1048 self.sizer.Add(self.logButton, (3,2), span=(1,1))
\r
1049 if exporer.hasExporer():
\r
1050 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
\r
1051 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
\r
1052 self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))
\r
1053 if profile.getPreference('sdpath') != '':
\r
1054 self.copyToSDButton = wx.Button(self, -1, "To SDCard")
\r
1055 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
\r
1056 self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))
\r
1057 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
\r
1058 self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
\r
1059 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
\r
1063 def OnCopyToSD(self, e):
\r
1064 filename = os.path.basename(self.resultFilename)
\r
1065 if profile.getPreference('sdshortnames') == 'True':
\r
1066 filename = sliceRun.getShortFilename(filename)
\r
1067 shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))
\r
1069 def OnOpenFileLocation(self, e):
\r
1070 exporer.openExporer(self.resultFilename)
\r
1072 def OnPrint(self, e):
\r
1073 printWindow.printFile(self.resultFilename)
\r
1075 def OnShowLog(self, e):
\r
1076 LogWindow('\n'.join(self.progressLog))
\r
1078 class preferencesDialog(configBase.configWindowBase):
\r
1079 def __init__(self, parent):
\r
1080 super(preferencesDialog, self).__init__(title="Project Planner Preferences")
\r
1082 self.parent = parent
\r
1083 wx.EVT_CLOSE(self, self.OnClose)
\r
1085 extruderAmount = int(profile.getPreference('extruder_amount'))
\r
1087 left, right, main = self.CreateConfigPanel(self)
\r
1088 configBase.TitleRow(left, 'Machine head size')
\r
1089 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
1090 validators.validFloat(c, 0.1)
\r
1091 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
1092 validators.validFloat(c, 0.1)
\r
1093 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
1094 validators.validFloat(c, 0.1)
\r
1095 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
1096 validators.validFloat(c, 0.1)
\r
1097 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
1098 validators.validFloat(c)
\r
1100 self.okButton = wx.Button(left, -1, 'Ok')
\r
1101 left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
\r
1102 self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
\r
1104 self.MakeModal(True)
\r
1108 def OnClose(self, e):
\r
1109 self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
1110 self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
1111 self.parent.Refresh()
\r
1113 self.MakeModal(False)
\r
1116 class LogWindow(wx.Frame):
\r
1117 def __init__(self, logText):
\r
1118 super(LogWindow, self).__init__(None, title="Slice log")
\r
1119 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
1120 self.SetSize((400,300))
\r
1125 app = wx.App(False)
\r
1126 projectPlanner().Show(True)
\r
1129 if __name__ == '__main__':
\r