1 from __future__ import absolute_import
\r
4 import wx, os, platform, types, webbrowser, math, subprocess, threading, time, re
\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 validators
\r
24 from gui import printWindow
\r
25 from gui import dropTarget
\r
26 from util import profile
\r
27 from util import util3d
\r
28 from util import stl
\r
29 from util import mesh
\r
30 from util import sliceRun
\r
31 from util import gcodeInterpreter
\r
32 from util import exporer
\r
34 class Action(object):
\r
37 class ProjectObject(stl.stlModel):
\r
38 def __init__(self, parent, filename):
\r
39 super(ProjectObject, self).__init__()
\r
43 self.parent = parent
\r
44 self.filename = filename
\r
55 self.modelDisplayList = None
\r
56 self.modelDirty = False
\r
60 self.centerX = -self.getMinimum()[0] + 5
\r
61 self.centerY = -self.getMinimum()[1] + 5
\r
63 self.updateModelTransform()
\r
65 self.centerX = -self.getMinimum()[0] + 5
\r
66 self.centerY = -self.getMinimum()[1] + 5
\r
68 def isSameExceptForPosition(self, other):
\r
69 if self.filename != other.filename:
\r
71 if self.scale != other.scale:
\r
73 if self.rotate != other.rotate:
\r
75 if self.flipX != other.flipX:
\r
77 if self.flipY != other.flipY:
\r
79 if self.flipZ != other.flipZ:
\r
81 if self.swapXZ != other.swapXZ:
\r
83 if self.swapYZ != other.swapYZ:
\r
85 if self.extruder != other.extruder:
\r
87 if self.profile != other.profile:
\r
91 def updateModelTransform(self):
\r
92 self.setRotateMirror(self.rotate, self.flipX, self.flipY, self.flipZ, self.swapXZ, self.swapYZ)
\r
93 minZ = self.getMinimumZ()
\r
94 minV = self.getMinimum()
\r
95 maxV = self.getMaximum()
\r
96 self.vertexes -= numpy.array([minV[0] + (maxV[0] - minV[0]) / 2, minV[1] + (maxV[1] - minV[1]) / 2, minZ])
\r
97 minZ = self.getMinimumZ()
\r
98 self.modelDirty = True
\r
101 p = ProjectObject(self.parent, self.filename)
\r
103 p.centerX = self.centerX + 5
\r
104 p.centerY = self.centerY + 5
\r
106 p.filename = self.filename
\r
107 p.scale = self.scale
\r
108 p.rotate = self.rotate
\r
109 p.flipX = self.flipX
\r
110 p.flipY = self.flipY
\r
111 p.flipZ = self.flipZ
\r
112 p.swapXZ = self.swapXZ
\r
113 p.swapYZ = self.swapYZ
\r
114 p.extruder = self.extruder
\r
115 p.profile = self.profile
\r
117 p.updateModelTransform()
\r
122 if self.centerX < -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]:
\r
123 self.centerX = -self.getMinimum()[0] * self.scale + self.parent.extruderOffset[self.extruder][0]
\r
124 if self.centerY < -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]:
\r
125 self.centerY = -self.getMinimum()[1] * self.scale + self.parent.extruderOffset[self.extruder][1]
\r
126 if self.centerX > self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale:
\r
127 self.centerX = self.parent.machineSize[0] + self.parent.extruderOffset[self.extruder][0] - self.getMaximum()[0] * self.scale
\r
128 if self.centerY > self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale:
\r
129 self.centerY = self.parent.machineSize[1] + self.parent.extruderOffset[self.extruder][1] - self.getMaximum()[1] * self.scale
\r
131 class projectPlanner(wx.Frame):
\r
132 "Main user interface window"
\r
133 def __init__(self):
\r
134 super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
\r
136 wx.EVT_CLOSE(self, self.OnClose)
\r
137 self.panel = wx.Panel(self, -1)
\r
138 self.SetSizer(wx.BoxSizer(wx.VERTICAL))
\r
139 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
140 #self.SetIcon(icon.getMainIcon())
\r
142 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, '.stl'))
\r
145 self.selection = None
\r
148 self.machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
\r
149 self.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
150 self.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
152 self.extruderOffset = [
\r
153 numpy.array([0,0,0]),
\r
154 numpy.array([profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0]),
\r
155 numpy.array([profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0]),
\r
156 numpy.array([profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0])]
\r
158 self.toolbar = toolbarUtil.Toolbar(self.panel)
\r
160 toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
\r
161 toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
\r
162 self.toolbar.AddSeparator()
\r
164 toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
\r
165 toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
\r
166 self.toolbar.AddSeparator()
\r
167 toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
\r
168 self.toolbar.AddSeparator()
\r
169 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
170 toolbarUtil.NormalButton(self.toolbar, self.OnSaveCombinedSTL, 'save-combination.png', 'Save all the combined STL files into a single STL file as a plate.')
\r
171 self.toolbar.AddSeparator()
\r
173 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
174 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
175 self.toolbar.AddSeparator()
\r
176 toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
\r
178 self.toolbar.Realize()
\r
180 self.toolbar2 = toolbarUtil.Toolbar(self.panel)
\r
182 toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
\r
183 toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
\r
184 self.toolbar2.AddSeparator()
\r
185 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
\r
186 toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
\r
187 toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
\r
188 toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to slice a specific object.')
\r
189 self.toolbar2.AddSeparator()
\r
190 toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
\r
191 toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Slice to project into a gcode file.')
\r
192 self.toolbar2.Realize()
\r
194 self.toolbar3 = toolbarUtil.Toolbar(self.panel)
\r
195 self.mirrorX = toolbarUtil.ToggleButton(self.toolbar3, 'flip_x', 'object-mirror-x-on.png', 'object-mirror-x-off.png', 'Mirror X', callback=self.OnMirrorChange)
\r
196 self.mirrorY = toolbarUtil.ToggleButton(self.toolbar3, 'flip_y', 'object-mirror-y-on.png', 'object-mirror-y-off.png', 'Mirror Y', callback=self.OnMirrorChange)
\r
197 self.mirrorZ = toolbarUtil.ToggleButton(self.toolbar3, 'flip_z', 'object-mirror-z-on.png', 'object-mirror-z-off.png', 'Mirror Z', callback=self.OnMirrorChange)
\r
198 self.toolbar3.AddSeparator()
\r
201 self.swapXZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_xz', 'object-swap-xz-on.png', 'object-swap-xz-off.png', 'Swap XZ', callback=self.OnMirrorChange)
\r
202 self.swapYZ = toolbarUtil.ToggleButton(self.toolbar3, 'swap_yz', 'object-swap-yz-on.png', 'object-swap-yz-off.png', 'Swap YZ', callback=self.OnMirrorChange)
\r
203 self.toolbar3.Realize()
\r
205 sizer = wx.GridBagSizer(2,2)
\r
206 self.panel.SetSizer(sizer)
\r
207 self.preview = PreviewGLCanvas(self.panel, self)
\r
208 self.listbox = wx.ListBox(self.panel, -1, choices=[])
\r
209 self.addButton = wx.Button(self.panel, -1, "Add")
\r
210 self.remButton = wx.Button(self.panel, -1, "Remove")
\r
211 self.sliceButton = wx.Button(self.panel, -1, "Slice")
\r
212 self.autoPlaceButton = wx.Button(self.panel, -1, "Auto Place")
\r
214 sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
215 sizer.Add(self.toolbar2, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
216 sizer.Add(self.preview, (1,0), span=(5,1), flag=wx.EXPAND)
\r
217 sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
\r
218 sizer.Add(self.toolbar3, (2,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
\r
219 sizer.Add(self.addButton, (3,1), span=(1,1))
\r
220 sizer.Add(self.remButton, (3,2), span=(1,1))
\r
221 sizer.Add(self.sliceButton, (4,1), span=(1,1))
\r
222 sizer.Add(self.autoPlaceButton, (4,2), span=(1,1))
\r
223 sizer.AddGrowableCol(0)
\r
224 sizer.AddGrowableRow(1)
\r
226 self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
\r
227 self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
\r
228 self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
\r
229 self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
\r
230 self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
\r
232 panel = wx.Panel(self.panel, -1)
\r
233 sizer.Add(panel, (5,1), span=(1,2))
\r
235 sizer = wx.GridBagSizer(2,2)
\r
236 panel.SetSizer(sizer)
\r
238 self.scaleCtrl = wx.TextCtrl(panel, -1, '')
\r
239 self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
\r
240 self.rotateCtrl.SetRange(0, 360)
\r
242 sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
243 sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
244 sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
245 sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
247 if int(profile.getPreference('extruder_amount')) > 1:
\r
248 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
249 sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
\r
250 sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
\r
251 self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
\r
253 self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
\r
254 self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
\r
256 self.SetSize((800,600))
\r
258 def OnClose(self, e):
\r
261 def OnQuit(self, e):
\r
264 def OnPreferences(self, e):
\r
265 prefDialog = preferencesDialog(self)
\r
266 prefDialog.Centre()
\r
267 prefDialog.Show(True)
\r
269 def OnCutMesh(self, e):
\r
270 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
271 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
272 if dlg.ShowModal() == wx.ID_OK:
\r
273 filename = dlg.GetPath()
\r
274 model = stl.stlModel().load(filename)
\r
275 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
276 parts = model.splitToParts(pd.Update)
\r
278 partFilename = filename[:filename.rfind('.')] + "_part%d.stl" % (parts.index(part))
\r
279 stl.saveAsSTL(part, partFilename)
\r
280 item = ProjectObject(self, partFilename)
\r
281 self.list.append(item)
\r
282 self.selection = item
\r
283 self._updateListbox()
\r
284 self.OnListSelect(None)
\r
286 self.preview.Refresh()
\r
289 def OnDropFiles(self, filenames):
\r
290 for filename in filenames:
\r
291 item = ProjectObject(self, filename)
\r
292 profile.putPreference('lastFile', item.filename)
\r
293 self.list.append(item)
\r
294 self.selection = item
\r
295 self._updateListbox()
\r
296 self.OnListSelect(None)
\r
297 self.preview.Refresh()
\r
299 def OnPrintTypeChange(self):
\r
301 if self.printAllAtOnce.GetValue():
\r
303 self.preview.Refresh()
\r
305 def OnSaveCombinedSTL(self, e):
\r
306 dlg=wx.FileDialog(self, "Save as STL", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
307 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
308 if dlg.ShowModal() == wx.ID_OK:
\r
309 self._saveCombinedSTL(dlg.GetPath())
\r
312 def _saveCombinedSTL(self, filename):
\r
314 for item in self.list:
\r
315 totalCount += item.vertexCount
\r
316 output = mesh.mesh()
\r
317 output._prepareVertexCount(totalCount)
\r
318 for item in self.list:
\r
319 offset = numpy.array([item.centerX, item.centerY, 0])
\r
320 for v in item.vertexes:
\r
321 v0 = v * item.scale + offset
\r
322 output.addVertex(v0[0], v0[1], v0[2])
\r
323 stl.saveAsSTL(output, filename)
\r
325 def OnSaveProject(self, e):
\r
326 dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
327 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
328 if dlg.ShowModal() == wx.ID_OK:
\r
329 cp = ConfigParser.ConfigParser()
\r
331 for item in self.list:
\r
332 section = 'model_%d' % (i)
\r
333 cp.add_section(section)
\r
334 cp.set(section, 'filename', item.filename.encode("utf-8"))
\r
335 cp.set(section, 'centerX', str(item.centerX))
\r
336 cp.set(section, 'centerY', str(item.centerY))
\r
337 cp.set(section, 'scale', str(item.scale))
\r
338 cp.set(section, 'rotate', str(item.rotate))
\r
339 cp.set(section, 'flipX', str(item.flipX))
\r
340 cp.set(section, 'flipY', str(item.flipY))
\r
341 cp.set(section, 'flipZ', str(item.flipZ))
\r
342 cp.set(section, 'swapXZ', str(item.swapXZ))
\r
343 cp.set(section, 'swapYZ', str(item.swapYZ))
\r
344 cp.set(section, 'extruder', str(item.extruder+1))
\r
345 if item.profile != None:
\r
346 cp.set(section, 'profile', item.profile)
\r
348 cp.write(open(dlg.GetPath(), "w"))
\r
351 def OnLoadProject(self, e):
\r
352 dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
353 dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
\r
354 if dlg.ShowModal() == wx.ID_OK:
\r
355 cp = ConfigParser.ConfigParser()
\r
356 cp.read(dlg.GetPath())
\r
359 while cp.has_section('model_%d' % (i)):
\r
360 section = 'model_%d' % (i)
\r
362 item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
\r
363 item.centerX = float(cp.get(section, 'centerX'))
\r
364 item.centerY = float(cp.get(section, 'centerY'))
\r
365 item.scale = float(cp.get(section, 'scale'))
\r
366 item.rotate = float(cp.get(section, 'rotate'))
\r
367 item.flipX = cp.get(section, 'flipX') == 'True'
\r
368 item.flipY = cp.get(section, 'flipY') == 'True'
\r
369 item.flipZ = cp.get(section, 'flipZ') == 'True'
\r
370 item.swapXZ = cp.get(section, 'swapXZ') == 'True'
\r
371 item.swapYZ = cp.get(section, 'swapYZ') == 'True'
\r
372 if cp.has_option(section, 'extruder'):
\r
373 item.extuder = int(cp.get(section, 'extruder')) - 1
\r
374 if cp.has_option(section, 'profile'):
\r
375 item.profile = cp.get(section, 'profile')
\r
376 item.updateModelTransform()
\r
379 self.list.append(item)
\r
381 self.selected = self.list[0]
\r
382 self._updateListbox()
\r
383 self.OnListSelect(None)
\r
384 self.preview.Refresh()
\r
388 def On3DClick(self):
\r
389 self.preview.yaw = 30
\r
390 self.preview.pitch = 60
\r
391 self.preview.zoom = 300
\r
392 self.preview.view3D = True
\r
393 self.preview.Refresh()
\r
395 def OnTopClick(self):
\r
396 self.preview.view3D = False
\r
397 self.preview.zoom = self.machineSize[0] / 2 + 10
\r
398 self.preview.offsetX = 0
\r
399 self.preview.offsetY = 0
\r
400 self.preview.Refresh()
\r
402 def OnListSelect(self, e):
\r
403 if self.listbox.GetSelection() == -1:
\r
405 self.selection = self.list[self.listbox.GetSelection()]
\r
406 self.scaleCtrl.SetValue(str(self.selection.scale))
\r
407 self.rotateCtrl.SetValue(int(self.selection.rotate))
\r
408 if int(profile.getPreference('extruder_amount')) > 1:
\r
410 self.extruderCtrl.SetValue(str(self.selection.extruder+1))
\r
412 self.mirrorX.SetValue(self.selection.flipX)
\r
413 self.mirrorY.SetValue(self.selection.flipY)
\r
414 self.mirrorZ.SetValue(self.selection.flipZ)
\r
415 self.swapXZ.SetValue(self.selection.swapXZ)
\r
416 self.swapYZ.SetValue(self.selection.swapYZ)
\r
418 self.preview.Refresh()
\r
420 def OnAddModel(self, e):
\r
421 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
422 dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
\r
423 if dlg.ShowModal() == wx.ID_OK:
\r
424 for filename in dlg.GetPaths():
\r
425 item = ProjectObject(self, filename)
\r
426 profile.putPreference('lastFile', item.filename)
\r
427 self.list.append(item)
\r
428 self.selection = item
\r
429 self._updateListbox()
\r
430 self.OnListSelect(None)
\r
431 self.preview.Refresh()
\r
434 def OnRemModel(self, e):
\r
435 if self.selection == None:
\r
437 self.list.remove(self.selection)
\r
438 self._updateListbox()
\r
439 self.preview.Refresh()
\r
441 def OnMoveUp(self, e):
\r
442 if self.selection == None:
\r
444 i = self.listbox.GetSelection()
\r
447 self.list.remove(self.selection)
\r
448 self.list.insert(i-1, self.selection)
\r
449 self._updateListbox()
\r
450 self.preview.Refresh()
\r
452 def OnMoveDown(self, e):
\r
453 if self.selection == None:
\r
455 i = self.listbox.GetSelection()
\r
456 if i == len(self.list) - 1:
\r
458 self.list.remove(self.selection)
\r
459 self.list.insert(i+1, self.selection)
\r
460 self._updateListbox()
\r
461 self.preview.Refresh()
\r
463 def OnCopy(self, e):
\r
464 if self.selection == None:
\r
467 item = self.selection.clone()
\r
468 self.list.insert(self.list.index(self.selection), item)
\r
469 self.selection = item
\r
471 self._updateListbox()
\r
472 self.preview.Refresh()
\r
474 def OnSetCustomProfile(self, e):
\r
475 if self.selection == None:
\r
478 dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
\r
479 dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
\r
480 if dlg.ShowModal() == wx.ID_OK:
\r
481 self.selection.profile = dlg.GetPath()
\r
483 self.selection.profile = None
\r
484 self._updateListbox()
\r
487 def _updateListbox(self):
\r
488 self.listbox.Clear()
\r
489 for item in self.list:
\r
490 if item.profile != None:
\r
491 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
\r
493 self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
\r
494 if self.selection in self.list:
\r
495 self.listbox.SetSelection(self.list.index(self.selection))
\r
496 elif len(self.list) > 0:
\r
497 self.selection = self.list[0]
\r
498 self.listbox.SetSelection(0)
\r
500 self.selection = None
\r
501 self.listbox.SetSelection(-1)
\r
503 def OnAutoPlace(self, e):
\r
504 bestAllowedSize = int(self.machineSize[1])
\r
505 bestArea = self._doAutoPlace(bestAllowedSize)
\r
506 for i in xrange(10, int(self.machineSize[1]), 10):
\r
507 area = self._doAutoPlace(i)
\r
508 if area < bestArea:
\r
509 bestAllowedSize = i
\r
511 self._doAutoPlace(bestAllowedSize)
\r
512 for item in self.list:
\r
514 self.preview.Refresh()
\r
516 def _doAutoPlace(self, allowedSizeY):
\r
517 extraSizeMin, extraSizeMax = self.getExtraHeadSize()
\r
519 if extraSizeMin[0] > extraSizeMax[0]:
\r
520 posX = self.machineSize[0]
\r
528 minX = self.machineSize[0]
\r
529 minY = self.machineSize[1]
\r
532 for item in self.list:
\r
533 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
534 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
535 if item.centerY + item.getSize()[1] >= allowedSizeY:
\r
537 posX = minX - extraSizeMax[0] - 1
\r
539 posX = maxX + extraSizeMin[0] + 1
\r
541 item.centerX = posX + item.getMaximum()[0] * item.scale * dirX
\r
542 item.centerY = posY + item.getMaximum()[1] * item.scale * dirY
\r
543 posY += item.getSize()[1] * item.scale * dirY + extraSizeMin[1] + 1
\r
544 minX = min(minX, item.centerX - item.getSize()[0] * item.scale / 2)
\r
545 minY = min(minY, item.centerY - item.getSize()[1] * item.scale / 2)
\r
546 maxX = max(maxX, item.centerX + item.getSize()[0] * item.scale / 2)
\r
547 maxY = max(maxY, item.centerY + item.getSize()[1] * item.scale / 2)
\r
549 for item in self.list:
\r
551 item.centerX -= minX / 2
\r
553 item.centerX += (self.machineSize[0] - maxX) / 2
\r
554 item.centerY += (self.machineSize[1] - maxY) / 2
\r
556 if minX < 0 or maxX > self.machineSize[0]:
\r
557 return ((maxX - minX) + (maxY - minY)) * 100
\r
559 return (maxX - minX) + (maxY - minY)
\r
561 def OnSlice(self, e):
\r
562 dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
\r
563 dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
\r
564 if dlg.ShowModal() != wx.ID_OK:
\r
567 resultFilename = dlg.GetPath()
\r
570 put = profile.setTempOverride
\r
571 oldProfile = profile.getGlobalProfileString()
\r
573 put('add_start_end_gcode', 'False')
\r
574 put('gcode_extension', 'project_tmp')
\r
575 if self.printMode == 0:
\r
578 for item in self.list:
\r
579 if item.profile != None and os.path.isfile(item.profile):
\r
580 profile.loadGlobalProfile(item.profile)
\r
581 put('machine_center_x', item.centerX - self.extruderOffset[item.extruder][0])
\r
582 put('machine_center_y', item.centerY - self.extruderOffset[item.extruder][1])
\r
583 put('model_scale', item.scale)
\r
584 put('flip_x', item.flipX)
\r
585 put('flip_y', item.flipY)
\r
586 put('flip_z', item.flipZ)
\r
587 put('model_rotate_base', item.rotate)
\r
588 put('swap_xz', item.swapXZ)
\r
589 put('swap_yz', item.swapYZ)
\r
592 action.sliceCmd = sliceRun.getSliceCommand(item.filename)
\r
593 action.centerX = item.centerX
\r
594 action.centerY = item.centerY
\r
595 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
596 action.extruder = item.extruder
\r
597 action.filename = item.filename
\r
598 clearZ = max(clearZ, item.getMaximum()[2] * item.scale + 5.0)
\r
599 action.clearZ = clearZ
\r
600 action.leaveResultForNextSlice = False
\r
601 action.usePreviousSlice = False
\r
602 actionList.append(action)
\r
604 if self.list.index(item) > 0 and item.isSameExceptForPosition(self.list[self.list.index(item)-1]):
\r
605 actionList[-2].leaveResultForNextSlice = True
\r
606 actionList[-1].usePreviousSlice = True
\r
608 if item.profile != None:
\r
609 profile.loadGlobalProfileFromString(oldProfile)
\r
612 self._saveCombinedSTL(resultFilename + "_temp_.stl")
\r
613 put('model_scale', 1.0)
\r
614 put('flip_x', False)
\r
615 put('flip_y', False)
\r
616 put('flip_z', False)
\r
617 put('model_rotate_base', 0)
\r
618 put('swap_xz', False)
\r
619 put('swap_yz', False)
\r
623 action.sliceCmd = sliceRun.getSliceCommand(resultFilename + "_temp_.stl")
\r
624 action.centerX = profile.getProfileSettingFloat('machine_center_x')
\r
625 action.centerY = profile.getProfileSettingFloat('machine_center_y')
\r
626 action.temperature = profile.getProfileSettingFloat('print_temperature')
\r
627 action.extruder = 0
\r
628 action.filename = resultFilename + "_temp_.stl"
\r
630 action.leaveResultForNextSlice = False
\r
631 action.usePreviousSlice = False
\r
632 actionList.append(action)
\r
634 #Restore the old profile.
\r
635 profile.resetTempOverride()
\r
637 pspw = ProjectSliceProgressWindow(actionList, resultFilename)
\r
638 pspw.extruderOffset = self.extruderOffset
\r
642 def OnScaleChange(self, e):
\r
643 if self.selection == None:
\r
646 self.selection.scale = float(self.scaleCtrl.GetValue())
\r
648 self.selection.scale = 1.0
\r
649 self.preview.Refresh()
\r
651 def OnRotateChange(self, e):
\r
652 if self.selection == None:
\r
654 self.selection.rotate = float(self.rotateCtrl.GetValue())
\r
655 self.selection.updateModelTransform()
\r
656 self.preview.Refresh()
\r
658 def OnExtruderChange(self, e):
\r
659 if self.selection == None:
\r
661 self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
\r
662 self.preview.Refresh()
\r
664 def OnMirrorChange(self):
\r
665 if self.selection == None:
\r
667 self.selection.flipX = self.mirrorX.GetValue()
\r
668 self.selection.flipY = self.mirrorY.GetValue()
\r
669 self.selection.flipZ = self.mirrorZ.GetValue()
\r
670 self.selection.swapXZ = self.swapXZ.GetValue()
\r
671 self.selection.swapYZ = self.swapYZ.GetValue()
\r
672 self.selection.updateModelTransform()
\r
673 self.preview.Refresh()
\r
675 def getExtraHeadSize(self):
\r
676 extraSizeMin = self.headSizeMin
\r
677 extraSizeMax = self.headSizeMax
\r
678 if profile.getProfileSettingFloat('skirt_line_count') > 0:
\r
679 skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
\r
680 extraSizeMin = extraSizeMin + numpy.array([skirtSize, skirtSize, 0])
\r
681 extraSizeMax = extraSizeMax + numpy.array([skirtSize, skirtSize, 0])
\r
682 if profile.getProfileSetting('enable_raft') != 'False':
\r
683 raftSize = profile.getProfileSettingFloat('raft_margin') * 2
\r
684 extraSizeMin = extraSizeMin + numpy.array([raftSize, raftSize, 0])
\r
685 extraSizeMax = extraSizeMax + numpy.array([raftSize, raftSize, 0])
\r
686 if profile.getProfileSetting('support') != 'None':
\r
687 extraSizeMin = extraSizeMin + numpy.array([3.0, 0, 0])
\r
688 extraSizeMax = extraSizeMax + numpy.array([3.0, 0, 0])
\r
690 if self.printMode == 1:
\r
691 extraSizeMin = numpy.array([6.0, 6.0, 0])
\r
692 extraSizeMax = numpy.array([6.0, 6.0, 0])
\r
694 return extraSizeMin, extraSizeMax
\r
696 class PreviewGLCanvas(glcanvas.GLCanvas):
\r
697 def __init__(self, parent, projectPlannerWindow):
\r
698 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
\r
699 glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
\r
700 self.parent = projectPlannerWindow
\r
701 self.context = glcanvas.GLContext(self)
\r
702 wx.EVT_PAINT(self, self.OnPaint)
\r
703 wx.EVT_SIZE(self, self.OnSize)
\r
704 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
\r
705 wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
\r
706 wx.EVT_MOTION(self, self.OnMouseMotion)
\r
707 wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
\r
710 self.zoom = self.parent.machineSize[0] / 2 + 10
\r
713 self.view3D = False
\r
714 self.allowDrag = False
\r
716 def OnMouseLeftDown(self,e):
\r
717 self.allowDrag = True
\r
719 def OnMouseMotion(self,e):
\r
720 if self.allowDrag and e.Dragging() and e.LeftIsDown():
\r
722 self.yaw += e.GetX() - self.oldX
\r
723 self.pitch -= e.GetY() - self.oldY
\r
724 if self.pitch > 170:
\r
726 if self.pitch < 10:
\r
729 item = self.parent.selection
\r
731 item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
\r
732 item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
\r
736 self.allowDrag = False
\r
737 if e.Dragging() and e.RightIsDown():
\r
739 self.zoom += e.GetY() - self.oldY
\r
743 self.oldX = e.GetX()
\r
744 self.oldY = e.GetY()
\r
746 def OnMouseWheel(self,e):
\r
748 self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
\r
749 if self.zoom < 1.0:
\r
753 def OnEraseBackground(self,event):
\r
754 #Workaround for windows background redraw flicker.
\r
757 def OnSize(self,event):
\r
760 def OnPaint(self,event):
\r
761 dc = wx.PaintDC(self)
\r
762 if not hasOpenGLlibs:
\r
764 dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
\r
766 self.SetCurrent(self.context)
\r
767 opengl.InitGL(self, self.view3D, self.zoom)
\r
769 glTranslate(0,0,-self.zoom)
\r
770 glRotate(-self.pitch, 1,0,0)
\r
771 glRotate(self.yaw, 0,0,1)
\r
773 glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
\r
774 glTranslate(self.offsetX, self.offsetY, 0.0)
\r
775 glTranslate(-self.parent.machineSize[0]/2, -self.parent.machineSize[1]/2, 0)
\r
781 machineSize = self.parent.machineSize
\r
782 extraSizeMin, extraSizeMax = self.parent.getExtraHeadSize()
\r
784 for item in self.parent.list:
\r
785 item.validPlacement = True
\r
786 item.gotHit = False
\r
788 for idx1 in xrange(0, len(self.parent.list)):
\r
789 item = self.parent.list[idx1]
\r
790 iMin1 = (item.getMinimum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
791 iMax1 = (item.getMaximum() * item.scale) + numpy.array([item.centerX, item.centerY, 0]) + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
792 for idx2 in xrange(0, idx1):
\r
793 item2 = self.parent.list[idx2]
\r
794 iMin2 = (item2.getMinimum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
795 iMax2 = (item2.getMaximum() * item2.scale) + numpy.array([item2.centerX, item2.centerY, 0])
\r
796 if item != item2 and iMax1[0] >= iMin2[0] and iMin1[0] <= iMax2[0] and iMax1[1] >= iMin2[1] and iMin1[1] <= iMax2[1]:
\r
797 item.validPlacement = False
\r
798 item2.gotHit = True
\r
800 seenSelected = False
\r
801 for item in self.parent.list:
\r
802 if item == self.parent.selection:
\r
803 seenSelected = True
\r
804 if item.modelDisplayList == None:
\r
805 item.modelDisplayList = glGenLists(1);
\r
806 if item.modelDirty:
\r
807 item.modelDirty = False
\r
808 modelSize = item.getMaximum() - item.getMinimum()
\r
809 glNewList(item.modelDisplayList, GL_COMPILE)
\r
810 opengl.DrawMesh(item)
\r
813 if item.validPlacement:
\r
814 if self.parent.selection == item:
\r
815 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.9, 0.7, 1.0])
\r
816 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.3, 0.2, 0.0])
\r
818 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.8, 0.6, 1.0])
\r
819 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 0.0])
\r
821 if self.parent.selection == item:
\r
822 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
823 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
825 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
\r
826 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
\r
829 glEnable(GL_LIGHTING)
\r
830 glTranslate(item.centerX, item.centerY, 0)
\r
832 glEnable(GL_NORMALIZE)
\r
833 glScalef(item.scale, item.scale, item.scale)
\r
834 glCallList(item.modelDisplayList)
\r
837 vMin = item.getMinimum() * item.scale
\r
838 vMax = item.getMaximum() * item.scale
\r
839 vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
\r
840 vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
\r
842 glDisable(GL_LIGHTING)
\r
844 if self.parent.selection == item:
\r
846 glColor3f(1.0,0.0,0.3)
\r
848 glColor3f(1.0,0.0,1.0)
\r
849 opengl.DrawBox(vMin, vMax)
\r
851 glColor3f(1.0,0.3,0.0)
\r
853 glColor3f(1.0,1.0,0.0)
\r
854 opengl.DrawBox(vMinHead, vMaxHead)
\r
857 glColor3f(0.5,0.0,0.1)
\r
859 glColor3f(0.5,0.0,0.5)
\r
860 opengl.DrawBox(vMinHead, vMaxHead)
\r
863 glColor3f(0.7,0.1,0.0)
\r
865 glColor3f(0.7,0.7,0.0)
\r
866 opengl.DrawBox(vMin, vMax)
\r
870 opengl.DrawMachine(util3d.Vector3(machineSize[0], machineSize[1], machineSize[2]))
\r
873 class ProjectSliceProgressWindow(wx.Frame):
\r
874 def __init__(self, actionList, resultFilename):
\r
875 super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
\r
876 self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
\r
878 self.actionList = actionList
\r
879 self.resultFilename = resultFilename
\r
881 self.prevStep = 'start'
\r
882 self.totalDoneFactor = 0.0
\r
883 self.startTime = time.time()
\r
884 self.sliceStartTime = time.time()
\r
886 self.sizer = wx.GridBagSizer(2, 2)
\r
887 self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
\r
888 self.progressGauge = wx.Gauge(self, -1)
\r
889 self.progressGauge.SetRange(10000)
\r
890 self.progressGauge2 = wx.Gauge(self, -1)
\r
891 self.progressGauge2.SetRange(len(self.actionList))
\r
892 self.abortButton = wx.Button(self, -1, "Abort")
\r
893 self.sizer.Add(self.statusText, (0,0), span=(1,5))
\r
894 self.sizer.Add(self.progressGauge, (1, 0), span=(1,5), flag=wx.EXPAND)
\r
895 self.sizer.Add(self.progressGauge2, (2, 0), span=(1,5), flag=wx.EXPAND)
\r
897 self.sizer.Add(self.abortButton, (3,0), span=(1,5), flag=wx.ALIGN_CENTER)
\r
898 self.sizer.AddGrowableCol(0)
\r
899 self.sizer.AddGrowableRow(0)
\r
901 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
\r
902 self.SetSizer(self.sizer)
\r
906 threading.Thread(target=self.OnRun).start()
\r
908 def OnAbort(self, e):
\r
913 self.abortButton.SetLabel('Close')
\r
915 def SetProgress(self, stepName, layer, maxLayer):
\r
916 if self.prevStep != stepName:
\r
917 self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
\r
918 newTime = time.time()
\r
919 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
\r
920 self.startTime = newTime
\r
921 self.prevStep = stepName
\r
923 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
\r
924 self.progressGauge.SetValue(int(progresValue))
\r
925 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
\r
928 resultFile = open(self.resultFilename, "w")
\r
929 put = profile.setTempOverride
\r
930 self.progressLog = []
\r
931 for action in self.actionList:
\r
932 wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))
\r
933 if not action.usePreviousSlice:
\r
934 p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
\r
935 line = p.stdout.readline()
\r
938 while(len(line) > 0):
\r
939 line = line.rstrip()
\r
940 if line[0:9] == "Progress[" and line[-1:] == "]":
\r
941 progress = line[9:-1].split(":")
\r
942 if len(progress) > 2:
\r
943 maxValue = int(progress[2])
\r
944 wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
\r
947 self.progressLog.append(line)
\r
948 wx.CallAfter(self.statusText.SetLabel, line)
\r
951 wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
\r
954 line = p.stdout.readline()
\r
955 self.returnCode = p.wait()
\r
957 put('machine_center_x', action.centerX - self.extruderOffset[action.extruder][0])
\r
958 put('machine_center_y', action.centerY - self.extruderOffset[action.extruder][1])
\r
959 put('clear_z', action.clearZ)
\r
960 put('extruder', action.extruder)
\r
961 put('print_temperature', action.temperature)
\r
963 if action == self.actionList[0]:
\r
964 resultFile.write(';TYPE:CUSTOM\n')
\r
965 resultFile.write('T%d\n' % (action.extruder))
\r
966 currentExtruder = action.extruder
\r
967 prevTemp = action.temperature
\r
968 resultFile.write(profile.getAlterationFileContents('start.gcode'))
\r
970 #reset the extrusion length, and move to the next object center.
\r
971 resultFile.write(';TYPE:CUSTOM\n')
\r
972 if prevTemp != action.temperature and action.temperature > 0:
\r
973 resultFile.write('M104 S%d\n' % (int(action.temperature)))
\r
974 prevTemp = action.temperature
\r
975 resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
\r
976 resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
\r
977 profile.resetTempOverride()
\r
979 if not action.usePreviousSlice:
\r
980 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
981 data = f.read(4096)
\r
983 resultFile.write(data)
\r
984 data = f.read(4096)
\r
986 savedCenterX = action.centerX
\r
987 savedCenterY = action.centerY
\r
989 f = open(sliceRun.getExportFilename(action.filename, "project_tmp"), "r")
\r
993 line = self._adjustNumberInLine(line, 'X', action.centerX - savedCenterX)
\r
995 line = self._adjustNumberInLine(line, 'Y', action.centerY - savedCenterY)
\r
996 resultFile.write(line)
\r
999 if not action.leaveResultForNextSlice:
\r
1000 os.remove(sliceRun.getExportFilename(action.filename, "project_tmp"))
\r
1002 wx.CallAfter(self.progressGauge.SetValue, 10000)
\r
1003 self.totalDoneFactor = 0.0
\r
1004 wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
\r
1006 resultFile.write(';TYPE:CUSTOM\n')
\r
1007 resultFile.write('G1 Z%f F%f\n' % (self.actionList[-1].clearZ, profile.getProfileSettingFloat('max_z_speed') * 60))
\r
1008 resultFile.write(profile.getAlterationFileContents('end.gcode'))
\r
1009 resultFile.close()
\r
1011 gcode = gcodeInterpreter.gcode()
\r
1012 gcode.load(self.resultFilename)
\r
1015 sliceTime = time.time() - self.sliceStartTime
\r
1016 status = "Build: %s" % (self.resultFilename)
\r
1017 status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
\r
1018 status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
\r
1019 status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
\r
1020 cost = gcode.calculateCost()
\r
1022 status += "\nCost: %s" % (cost)
\r
1023 profile.replaceGCodeTags(self.resultFilename, gcode)
\r
1024 wx.CallAfter(self.statusText.SetLabel, status)
\r
1025 wx.CallAfter(self.OnSliceDone)
\r
1027 def _adjustNumberInLine(self, line, tag, f):
\r
1028 m = re.search('^(.*'+tag+')([0-9\.]*)(.*)$', line)
\r
1029 return m.group(1) + str(float(m.group(2)) + f) + m.group(3) + '\n'
\r
1031 def OnSliceDone(self):
\r
1032 self.abortButton.Destroy()
\r
1033 self.closeButton = wx.Button(self, -1, "Close")
\r
1034 self.printButton = wx.Button(self, -1, "Print")
\r
1035 self.logButton = wx.Button(self, -1, "Show log")
\r
1036 self.sizer.Add(self.closeButton, (3,0), span=(1,1))
\r
1037 self.sizer.Add(self.printButton, (3,1), span=(1,1))
\r
1038 self.sizer.Add(self.logButton, (3,2), span=(1,1))
\r
1039 if exporer.hasExporer():
\r
1040 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
\r
1041 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
\r
1042 self.sizer.Add(self.openFileLocationButton, (3,3), span=(1,1))
\r
1043 if profile.getPreference('sdpath') != '':
\r
1044 self.copyToSDButton = wx.Button(self, -1, "To SDCard")
\r
1045 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
\r
1046 self.sizer.Add(self.copyToSDButton, (3,4), span=(1,1))
\r
1047 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
\r
1048 self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
\r
1049 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
\r
1053 def OnCopyToSD(self, e):
\r
1054 filename = os.path.basename(self.resultFilename)
\r
1055 if profile.getPreference('sdshortnames') == 'True':
\r
1056 filename = sliceRun.getShortFilename(filename)
\r
1057 shutil.copy(self.resultFilename, os.path.join(profile.getPreference('sdpath'), filename))
\r
1059 def OnOpenFileLocation(self, e):
\r
1060 exporer.openExporer(self.resultFilename)
\r
1062 def OnPrint(self, e):
\r
1063 printWindow.printFile(self.resultFilename)
\r
1065 def OnShowLog(self, e):
\r
1066 LogWindow('\n'.join(self.progressLog))
\r
1068 class preferencesDialog(configBase.configWindowBase):
\r
1069 def __init__(self, parent):
\r
1070 super(preferencesDialog, self).__init__(title="Project Planner Preferences")
\r
1072 self.parent = parent
\r
1073 wx.EVT_CLOSE(self, self.OnClose)
\r
1075 extruderAmount = int(profile.getPreference('extruder_amount'))
\r
1077 left, right, main = self.CreateConfigPanel(self)
\r
1078 configBase.TitleRow(left, 'Machine head size')
\r
1079 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
1080 validators.validFloat(c, 0.1)
\r
1081 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
1082 validators.validFloat(c, 0.1)
\r
1083 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
1084 validators.validFloat(c, 0.1)
\r
1085 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
1086 validators.validFloat(c, 0.1)
\r
1087 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
1088 validators.validFloat(c)
\r
1090 self.okButton = wx.Button(left, -1, 'Ok')
\r
1091 left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
\r
1092 self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
\r
1094 self.MakeModal(True)
\r
1098 def OnClose(self, e):
\r
1099 self.parent.headSizeMin = numpy.array([profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0])
\r
1100 self.parent.headSizeMax = numpy.array([profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0])
\r
1101 self.parent.Refresh()
\r
1103 self.MakeModal(False)
\r
1106 class LogWindow(wx.Frame):
\r
1107 def __init__(self, logText):
\r
1108 super(LogWindow, self).__init__(None, title="Slice log")
\r
1109 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
1110 self.SetSize((400,300))
\r
1115 app = wx.App(False)
\r
1116 projectPlanner().Show(True)
\r
1119 if __name__ == '__main__':
\r