OctoPrint/Cura/gui/projectPlanner.py

954 lines
34 KiB
Python
Raw Normal View History

from __future__ import absolute_import
import __init__
import wx, os, platform, types, webbrowser, math, subprocess, threading, time
import ConfigParser
from wx import glcanvas
import wx
try:
import OpenGL
OpenGL.ERROR_CHECKING = False
from OpenGL.GLU import *
from OpenGL.GL import *
hasOpenGLlibs = True
except:
print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/"
hasOpenGLlibs = False
from gui import opengl
from gui import toolbarUtil
from gui import icon
from gui import configBase
from gui import validators
from gui import printWindow
from util import profile
from util import util3d
from util import stl
from util import sliceRun
from util import gcodeInterpreter
from util import exporer
class Action(object):
pass
class ProjectObject(stl.stlModel):
def __init__(self, parent, filename):
super(ProjectObject, self).__init__()
self.load(filename)
self.parent = parent
self.filename = filename
self.scale = 1.0
self.rotate = 0.0
self.flipX = False
self.flipY = False
self.flipZ = False
self.swapXZ = False
self.swapYZ = False
self.extruder = 0
self.profile = None
self.modelDisplayList = None
self.modelDirty = False
self.origonalVertexes = list(self.vertexes)
for i in xrange(0, len(self.origonalVertexes)):
self.origonalVertexes[i] = self.origonalVertexes[i].copy()
self.getMinimumZ()
self.centerX = -self.getMinimum().x + 5
self.centerY = -self.getMinimum().y + 5
self.updateModelTransform()
self.centerX = -self.getMinimum().x + 5
self.centerY = -self.getMinimum().y + 5
def updateModelTransform(self):
rotate = self.rotate / 180.0 * math.pi
scaleX = 1.0
scaleY = 1.0
scaleZ = 1.0
if self.flipX:
scaleX = -scaleX
if self.flipY:
scaleY = -scaleY
if self.flipZ:
scaleZ = -scaleZ
swapXZ = self.swapXZ
swapYZ = self.swapYZ
mat00 = math.cos(rotate) * scaleX
mat01 =-math.sin(rotate) * scaleY
mat10 = math.sin(rotate) * scaleX
mat11 = math.cos(rotate) * scaleY
for i in xrange(0, len(self.origonalVertexes)):
x = self.origonalVertexes[i].x
y = self.origonalVertexes[i].y
z = self.origonalVertexes[i].z
if swapXZ:
x, z = z, x
if swapYZ:
y, z = z, y
self.vertexes[i].x = x * mat00 + y * mat01
self.vertexes[i].y = x * mat10 + y * mat11
self.vertexes[i].z = z * scaleZ
for face in self.faces:
v1 = face.v[0]
v2 = face.v[1]
v3 = face.v[2]
face.normal = (v2 - v1).cross(v3 - v1)
face.normal.normalize()
minZ = self.getMinimumZ()
minV = self.getMinimum()
maxV = self.getMaximum()
for v in self.vertexes:
v.z -= minZ
v.x -= minV.x + (maxV.x - minV.x) / 2
v.y -= minV.y + (maxV.y - minV.y) / 2
self.getMinimumZ()
self.modelDirty = True
def clone(self):
p = ProjectObject(self.parent, self.filename)
p.centerX = self.centerX + 5
p.centerY = self.centerY + 5
p.filename = self.filename
p.scale = self.scale
p.rotate = self.rotate
p.flipX = self.flipX
p.flipY = self.flipY
p.flipZ = self.flipZ
p.swapXZ = self.swapXZ
p.swapYZ = self.swapYZ
p.extruder = self.extruder
p.profile = self.profile
p.updateModelTransform()
return p
def clampXY(self):
if self.centerX < -self.getMinimum().x * self.scale + self.parent.extruderOffset[self.extruder].x:
self.centerX = -self.getMinimum().x * self.scale + self.parent.extruderOffset[self.extruder].x
if self.centerY < -self.getMinimum().y * self.scale + self.parent.extruderOffset[self.extruder].y:
self.centerY = -self.getMinimum().y * self.scale + self.parent.extruderOffset[self.extruder].y
if self.centerX > self.parent.machineSize.x + self.parent.extruderOffset[self.extruder].x - self.getMaximum().x * self.scale:
self.centerX = self.parent.machineSize.x + self.parent.extruderOffset[self.extruder].x - self.getMaximum().x * self.scale
if self.centerY > self.parent.machineSize.y + self.parent.extruderOffset[self.extruder].y - self.getMaximum().y * self.scale:
self.centerY = self.parent.machineSize.y + self.parent.extruderOffset[self.extruder].y - self.getMaximum().y * self.scale
class projectPlanner(wx.Frame):
"Main user interface window"
def __init__(self):
2012-04-20 15:15:10 +00:00
super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
wx.EVT_CLOSE(self, self.OnClose)
#self.SetIcon(icon.getMainIcon())
self.list = []
self.selection = None
self.machineSize = util3d.Vector3(profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height'))
self.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)
self.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)
self.extruderOffset = [
util3d.Vector3(0,0,0),
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x1'), profile.getPreferenceFloat('extruder_offset_y1'), 0),
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x2'), profile.getPreferenceFloat('extruder_offset_y2'), 0),
util3d.Vector3(profile.getPreferenceFloat('extruder_offset_x3'), profile.getPreferenceFloat('extruder_offset_y3'), 0)]
self.toolbarPanel = wx.Panel(self, -1)
self.toolbarPanel.SetSizer(wx.BoxSizer(wx.VERTICAL))
self.toolbar = toolbarUtil.Toolbar(self.toolbarPanel)
self.toolbarPanel.GetSizer().Add(self.toolbar, 0, flag=wx.EXPAND)
toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project')
toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project')
self.toolbar.AddSeparator()
group = []
toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick)
toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True)
self.toolbar.AddSeparator()
toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner')
self.toolbar.AddSeparator()
toolbarUtil.NormalButton(self.toolbar, self.OnPreferences, 'preferences.png', 'Project planner preferences')
self.toolbar.Realize()
self.toolbar2Panel = wx.Panel(self, -1)
self.toolbar2Panel.SetSizer(wx.BoxSizer(wx.VERTICAL))
self.toolbar2 = toolbarUtil.Toolbar(self.toolbar2Panel)
self.toolbar2Panel.GetSizer().Add(self.toolbar2, 0, flag=wx.EXPAND)
toolbarUtil.NormalButton(self.toolbar2, self.OnAddModel, 'object-add.png', 'Add model')
toolbarUtil.NormalButton(self.toolbar2, self.OnRemModel, 'object-remove.png', 'Remove model')
self.toolbar2.AddSeparator()
toolbarUtil.NormalButton(self.toolbar2, self.OnMoveUp, 'move-up.png', 'Move model up in print list')
toolbarUtil.NormalButton(self.toolbar2, self.OnMoveDown, 'move-down.png', 'Move model down in print list')
toolbarUtil.NormalButton(self.toolbar2, self.OnCopy, 'copy.png', 'Make a copy of the current selected object')
toolbarUtil.NormalButton(self.toolbar2, self.OnSetCustomProfile, 'set-profile.png', 'Set a custom profile to be used to slice a specific object.')
self.toolbar2.AddSeparator()
toolbarUtil.NormalButton(self.toolbar2, self.OnAutoPlace, 'autoplace.png', 'Automaticly organize the objects on the platform.')
toolbarUtil.NormalButton(self.toolbar2, self.OnSlice, 'slice.png', 'Slice to project into a gcode file.')
self.toolbar2.Realize()
sizer = wx.GridBagSizer(2,2)
self.SetSizer(sizer)
self.preview = PreviewGLCanvas(self)
self.listbox = wx.ListBox(self, -1, choices=[])
self.addButton = wx.Button(self, -1, "Add")
self.remButton = wx.Button(self, -1, "Remove")
self.sliceButton = wx.Button(self, -1, "Slice")
self.autoPlaceButton = wx.Button(self, -1, "Auto Place")
sizer.Add(self.toolbarPanel, (0,0), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
sizer.Add(self.toolbar2Panel, (0,1), span=(1,2), flag=wx.EXPAND|wx.LEFT|wx.RIGHT)
sizer.Add(self.preview, (1,0), span=(4,1), flag=wx.EXPAND)
sizer.Add(self.listbox, (1,1), span=(1,2), flag=wx.EXPAND)
sizer.Add(self.addButton, (2,1), span=(1,1))
sizer.Add(self.remButton, (2,2), span=(1,1))
sizer.Add(self.sliceButton, (3,1), span=(1,1))
sizer.Add(self.autoPlaceButton, (3,2), span=(1,1))
sizer.AddGrowableCol(0)
sizer.AddGrowableRow(1)
self.addButton.Bind(wx.EVT_BUTTON, self.OnAddModel)
self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
self.autoPlaceButton.Bind(wx.EVT_BUTTON, self.OnAutoPlace)
self.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
panel = wx.Panel(self, -1)
sizer.Add(panel, (4,1), span=(1,2))
sizer = wx.GridBagSizer(2,2)
panel.SetSizer(sizer)
self.scaleCtrl = wx.TextCtrl(panel, -1, '')
self.rotateCtrl = wx.SpinCtrl(panel, -1, '', size=(21*4,21), style=wx.SP_ARROW_KEYS)
self.rotateCtrl.SetRange(0, 360)
sizer.Add(wx.StaticText(panel, -1, 'Scale'), (0,0), flag=wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.scaleCtrl, (0,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
sizer.Add(wx.StaticText(panel, -1, 'Rotate'), (1,0), flag=wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.rotateCtrl, (1,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
if int(profile.getPreference('extruder_amount')) > 1:
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)
sizer.Add(wx.StaticText(panel, -1, 'Extruder'), (2,0), flag=wx.ALIGN_CENTER_VERTICAL)
sizer.Add(self.extruderCtrl, (2,1), flag=wx.ALIGN_BOTTOM|wx.EXPAND)
self.extruderCtrl.Bind(wx.EVT_COMBOBOX, self.OnExtruderChange)
self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
self.SetSize((800,600))
def OnClose(self, e):
self.Destroy()
def OnQuit(self, e):
self.Close()
def OnPreferences(self, e):
prefDialog = preferencesDialog(self)
prefDialog.Centre()
prefDialog.Show(True)
def OnSaveProject(self, e):
dlg=wx.FileDialog(self, "Save project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
if dlg.ShowModal() == wx.ID_OK:
cp = ConfigParser.ConfigParser()
i = 0
for item in self.list:
section = 'model_%d' % (i)
cp.add_section(section)
cp.set(section, 'filename', item.filename.encode("utf-8"))
cp.set(section, 'centerX', str(item.centerX))
cp.set(section, 'centerY', str(item.centerY))
cp.set(section, 'scale', str(item.scale))
cp.set(section, 'rotate', str(item.rotate))
cp.set(section, 'flipX', str(item.flipX))
cp.set(section, 'flipY', str(item.flipY))
cp.set(section, 'flipZ', str(item.flipZ))
cp.set(section, 'swapXZ', str(item.swapXZ))
cp.set(section, 'swapYZ', str(item.swapYZ))
cp.set(section, 'extruder', str(item.extruder+1))
if item.profile != None:
cp.set(section, 'profile', item.profile)
i += 1
cp.write(open(dlg.GetPath(), "w"))
dlg.Destroy()
def OnLoadProject(self, e):
dlg=wx.FileDialog(self, "Open project file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard("Project files (*.curaproject)|*.curaproject")
if dlg.ShowModal() == wx.ID_OK:
cp = ConfigParser.ConfigParser()
cp.read(dlg.GetPath())
self.list = []
i = 0
while cp.has_section('model_%d' % (i)):
section = 'model_%d' % (i)
item = ProjectObject(self, unicode(cp.get(section, 'filename'), "utf-8"))
item.centerX = float(cp.get(section, 'centerX'))
item.centerY = float(cp.get(section, 'centerY'))
item.scale = float(cp.get(section, 'scale'))
item.rotate = float(cp.get(section, 'rotate'))
item.flipX = cp.get(section, 'flipX') == 'True'
item.flipY = cp.get(section, 'flipY') == 'True'
item.flipZ = cp.get(section, 'flipZ') == 'True'
item.swapXZ = cp.get(section, 'swapXZ') == 'True'
item.swapYZ = cp.get(section, 'swapYZ') == 'True'
if cp.has_option(section, 'extruder'):
item.extuder = int(cp.get(section, 'extruder')) - 1
if cp.has_option(section, 'profile'):
item.profile = cp.get(section, 'profile')
item.updateModelTransform()
i += 1
self.list.append(item)
self.selected = self.list[0]
self._updateListbox()
self.OnListSelect(None)
self.preview.Refresh()
dlg.Destroy()
def On3DClick(self):
self.preview.yaw = 30
self.preview.pitch = 60
self.preview.zoom = 300
self.preview.view3D = True
self.preview.Refresh()
def OnTopClick(self):
self.preview.view3D = False
self.preview.zoom = self.machineSize.x / 2 + 10
self.preview.offsetX = 0
self.preview.offsetY = 0
self.preview.Refresh()
def OnListSelect(self, e):
if self.listbox.GetSelection() == -1:
return
self.selection = self.list[self.listbox.GetSelection()]
self.scaleCtrl.SetValue(str(self.selection.scale))
self.rotateCtrl.SetValue(int(self.selection.rotate))
if int(profile.getPreference('extruder_amount')) > 1:
self.extruderCtrl.SetValue(str(self.selection.extruder+1))
self.preview.Refresh()
def OnAddModel(self, e):
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)
dlg.SetWildcard("STL files (*.stl)|*.stl;*.STL")
if dlg.ShowModal() == wx.ID_OK:
for filename in dlg.GetPaths():
item = ProjectObject(self, filename)
profile.putPreference('lastFile', item.filename)
self.list.append(item)
self.selection = item
self._updateListbox()
self.OnListSelect(None)
self.preview.Refresh()
dlg.Destroy()
def OnRemModel(self, e):
if self.selection == None:
return
self.list.remove(self.selection)
self._updateListbox()
self.preview.Refresh()
def OnMoveUp(self, e):
if self.selection == None:
return
i = self.listbox.GetSelection()
if i == 0:
return
self.list.remove(self.selection)
self.list.insert(i-1, self.selection)
self._updateListbox()
self.preview.Refresh()
def OnMoveDown(self, e):
if self.selection == None:
return
i = self.listbox.GetSelection()
if i == len(self.list) - 1:
return
self.list.remove(self.selection)
self.list.insert(i+1, self.selection)
self._updateListbox()
self.preview.Refresh()
def OnCopy(self, e):
if self.selection == None:
return
item = self.selection.clone()
self.list.append(item)
self.selection = item
self._updateListbox()
self.preview.Refresh()
def OnSetCustomProfile(self, e):
if self.selection == None:
return
dlg=wx.FileDialog(self, "Select profile", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard("Profile files (*.ini)|*.ini;*.INI")
if dlg.ShowModal() == wx.ID_OK:
self.selection.profile = dlg.GetPath()
else:
self.selection.profile = None
self._updateListbox()
dlg.Destroy()
def _updateListbox(self):
self.listbox.Clear()
for item in self.list:
if item.profile != None:
self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1] + " *")
else:
self.listbox.AppendAndEnsureVisible(os.path.split(item.filename)[1])
if self.selection in self.list:
self.listbox.SetSelection(self.list.index(self.selection))
elif len(self.list) > 0:
self.selection = self.list[0]
self.listbox.SetSelection(0)
else:
self.selection = None
self.listbox.SetSelection(-1)
def OnAutoPlace(self, e):
bestAllowedSize = int(self.machineSize.y)
bestArea = self._doAutoPlace(bestAllowedSize)
for i in xrange(10, int(self.machineSize.y), 10):
area = self._doAutoPlace(i)
if area < bestArea:
bestAllowedSize = i
bestArea = area
self._doAutoPlace(bestAllowedSize)
for item in self.list:
item.clampXY()
self.preview.Refresh()
def _doAutoPlace(self, allowedSizeY):
extraSizeMin = self.headSizeMin
extraSizeMax = self.headSizeMax
if profile.getProfileSettingFloat('skirt_line_count') > 0:
skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
if extraSizeMin.x > extraSizeMax.x:
posX = self.machineSize.x
dirX = -1
else:
posX = 0
dirX = 1
posY = 0
dirY = 1
minX = self.machineSize.x
minY = self.machineSize.y
maxX = 0
maxY = 0
for item in self.list:
item.centerX = posX + item.getMaximum().x * item.scale * dirX
item.centerY = posY + item.getMaximum().y * item.scale * dirY
if item.centerY + item.getSize().y >= allowedSizeY:
if dirX < 0:
posX = minX - extraSizeMax.x - 1
else:
posX = maxX + extraSizeMin.x + 1
posY = 0
item.centerX = posX + item.getMaximum().x * item.scale * dirX
item.centerY = posY + item.getMaximum().y * item.scale * dirY
posY += item.getSize().y * item.scale * dirY + extraSizeMin.y + 1
minX = min(minX, item.centerX - item.getSize().x * item.scale / 2)
minY = min(minY, item.centerY - item.getSize().y * item.scale / 2)
maxX = max(maxX, item.centerX + item.getSize().x * item.scale / 2)
maxY = max(maxY, item.centerY + item.getSize().y * item.scale / 2)
for item in self.list:
if dirX < 0:
item.centerX -= minX / 2
else:
item.centerX += (self.machineSize.x - maxX) / 2
item.centerY += (self.machineSize.y - maxY) / 2
if minX < 0 or maxX > self.machineSize.x:
return ((maxX - minX) + (maxY - minY)) * 100
return (maxX - minX) + (maxY - minY)
def OnSlice(self, e):
put = profile.setTempOverride
oldProfile = profile.getGlobalProfileString()
put('model_multiply_x', '1')
put('model_multiply_y', '1')
put('enable_raft', 'False')
put('add_start_end_gcode', 'False')
put('gcode_extension', 'project_tmp')
clearZ = 0
actionList = []
for item in self.list:
if item.profile != None and os.path.isfile(item.profile):
profile.loadGlobalProfile(item.profile)
put('machine_center_x', item.centerX - self.extruderOffset[item.extruder].x)
put('machine_center_y', item.centerY - self.extruderOffset[item.extruder].y)
put('model_scale', item.scale)
put('flip_x', item.flipX)
put('flip_y', item.flipY)
put('flip_z', item.flipZ)
put('model_rotate_base', item.rotate)
put('swap_xz', item.swapXZ)
put('swap_yz', item.swapYZ)
action = Action()
action.sliceCmd = sliceRun.getSliceCommand(item.filename)
action.centerX = item.centerX
action.centerY = item.centerY
action.extruder = item.extruder
action.filename = item.filename
clearZ = max(clearZ, item.getMaximum().z * item.scale)
action.clearZ = clearZ
actionList.append(action)
if item.profile != None:
profile.loadGlobalProfileFromString(oldProfile)
#Restore the old profile.
profile.resetTempOverride()
dlg=wx.FileDialog(self, "Save project gcode file", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
dlg.SetWildcard("GCode file (*.gcode)|*.gcode")
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
resultFilename = dlg.GetPath()
dlg.Destroy()
pspw = ProjectSliceProgressWindow(actionList, resultFilename)
pspw.extruderOffset = self.extruderOffset
pspw.Centre()
pspw.Show(True)
def OnScaleChange(self, e):
if self.selection == None:
return
try:
self.selection.scale = float(self.scaleCtrl.GetValue())
except ValueError:
self.selection.scale = 1.0
self.preview.Refresh()
def OnRotateChange(self, e):
if self.selection == None:
return
self.selection.rotate = float(self.rotateCtrl.GetValue())
self.selection.updateModelTransform()
self.preview.Refresh()
def OnExtruderChange(self, e):
if self.selection == None:
return
self.selection.extruder = int(self.extruderCtrl.GetValue()) - 1
self.preview.Refresh()
class PreviewGLCanvas(glcanvas.GLCanvas):
def __init__(self, parent):
attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
glcanvas.GLCanvas.__init__(self, parent, attribList = attribList)
self.parent = parent
self.context = glcanvas.GLContext(self)
wx.EVT_PAINT(self, self.OnPaint)
wx.EVT_SIZE(self, self.OnSize)
wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
wx.EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
wx.EVT_MOTION(self, self.OnMouseMotion)
wx.EVT_MOUSEWHEEL(self, self.OnMouseWheel)
self.yaw = 30
self.pitch = 60
self.zoom = self.parent.machineSize.x / 2 + 10
self.offsetX = 0
self.offsetY = 0
self.view3D = False
self.allowDrag = False
def OnMouseLeftDown(self,e):
self.allowDrag = True
def OnMouseMotion(self,e):
if self.allowDrag and e.Dragging() and e.LeftIsDown():
if self.view3D:
self.yaw += e.GetX() - self.oldX
self.pitch -= e.GetY() - self.oldY
if self.pitch > 170:
self.pitch = 170
if self.pitch < 10:
self.pitch = 10
else:
item = self.parent.selection
if item != None:
item.centerX += float(e.GetX() - self.oldX) * self.zoom / self.GetSize().GetHeight() * 2
item.centerY -= float(e.GetY() - self.oldY) * self.zoom / self.GetSize().GetHeight() * 2
item.clampXY()
self.Refresh()
else:
self.allowDrag = False
if e.Dragging() and e.RightIsDown():
if self.view3D:
self.zoom += e.GetY() - self.oldY
if self.zoom < 1:
self.zoom = 1
self.Refresh()
self.oldX = e.GetX()
self.oldY = e.GetY()
def OnMouseWheel(self,e):
if self.view3D:
self.zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
if self.zoom < 1.0:
self.zoom = 1.0
self.Refresh()
def OnEraseBackground(self,event):
#Workaround for windows background redraw flicker.
pass
def OnSize(self,event):
self.Refresh()
def OnPaint(self,event):
dc = wx.PaintDC(self)
if not hasOpenGLlibs:
dc.Clear()
dc.DrawText("No PyOpenGL installation found.\nNo preview window available.", 10, 10)
return
self.SetCurrent(self.context)
opengl.InitGL(self, self.view3D, self.zoom)
if self.view3D:
glTranslate(0,0,-self.zoom)
glRotate(-self.pitch, 1,0,0)
glRotate(self.yaw, 0,0,1)
if False: #self.parent.triangleMesh != None:
glTranslate(0,0,-self.parent.triangleMesh.getMaximum().z / 2)
else:
glScale(1.0/self.zoom, 1.0/self.zoom, 1.0)
glTranslate(self.offsetX, self.offsetY, 0.0)
glTranslate(-self.parent.machineSize.x/2, -self.parent.machineSize.y/2, 0)
self.OnDraw()
self.SwapBuffers()
def OnDraw(self):
machineSize = self.parent.machineSize
opengl.DrawMachine(machineSize)
extraSizeMin = self.parent.headSizeMin
extraSizeMax = self.parent.headSizeMax
if profile.getProfileSettingFloat('skirt_line_count') > 0:
skirtSize = profile.getProfileSettingFloat('skirt_line_count') * profile.calculateEdgeWidth() + profile.getProfileSettingFloat('skirt_gap')
extraSizeMin = extraSizeMin + util3d.Vector3(skirtSize, skirtSize, 0)
extraSizeMax = extraSizeMax + util3d.Vector3(skirtSize, skirtSize, 0)
for item in self.parent.list:
item.validPlacement = True
item.gotHit = False
for idx1 in xrange(0, len(self.parent.list)):
item = self.parent.list[idx1]
iMin1 = item.getMinimum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) - extraSizeMin - self.parent.extruderOffset[item.extruder]
iMax1 = item.getMaximum() * item.scale + util3d.Vector3(item.centerX, item.centerY, 0) + extraSizeMax - self.parent.extruderOffset[item.extruder]
for idx2 in xrange(0, idx1):
item2 = self.parent.list[idx2]
iMin2 = item2.getMinimum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
iMax2 = item2.getMaximum() * item2.scale + util3d.Vector3(item2.centerX, item2.centerY, 0)
if item != item2 and iMax1.x >= iMin2.x and iMin1.x <= iMax2.x and iMax1.y >= iMin2.y and iMin1.y <= iMax2.y:
item.validPlacement = False
item2.gotHit = True
seenSelected = False
for item in self.parent.list:
if item == self.parent.selection:
seenSelected = True
if item.modelDisplayList == None:
item.modelDisplayList = glGenLists(1);
if item.modelDirty:
item.modelDirty = False
modelSize = item.getMaximum() - item.getMinimum()
glNewList(item.modelDisplayList, GL_COMPILE)
opengl.DrawSTL(item)
glEndList()
if item.validPlacement:
if self.parent.selection == item:
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.9, 0.7, 1.0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.3, 0.2, 0.0])
else:
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.8, 0.6, 1.0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 0.0])
else:
if self.parent.selection == item:
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
else:
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 0.0, 0.0, 0.0])
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.0, 0.0, 0.0])
glPushMatrix()
glEnable(GL_LIGHTING)
glTranslate(item.centerX, item.centerY, 0)
glPushMatrix()
glEnable(GL_NORMALIZE)
glScalef(item.scale, item.scale, item.scale)
glCallList(item.modelDisplayList)
glPopMatrix()
vMin = item.getMinimum() * item.scale
vMax = item.getMaximum() * item.scale
vMinHead = vMin - extraSizeMin - self.parent.extruderOffset[item.extruder]
vMaxHead = vMax + extraSizeMax - self.parent.extruderOffset[item.extruder]
glDisable(GL_LIGHTING)
if self.parent.selection == item:
if item.gotHit:
glColor3f(1.0,0.0,0.3)
else:
glColor3f(1.0,0.0,1.0)
opengl.DrawBox(vMin, vMax)
if item.gotHit:
glColor3f(1.0,0.3,0.0)
else:
glColor3f(1.0,1.0,0.0)
opengl.DrawBox(vMinHead, vMaxHead)
elif seenSelected:
if item.gotHit:
glColor3f(0.5,0.0,0.1)
else:
glColor3f(0.5,0.0,0.5)
opengl.DrawBox(vMinHead, vMaxHead)
else:
if item.gotHit:
glColor3f(0.7,0.1,0.0)
else:
glColor3f(0.7,0.7,0.0)
opengl.DrawBox(vMin, vMax)
glPopMatrix()
glFlush()
class ProjectSliceProgressWindow(wx.Frame):
def __init__(self, actionList, resultFilename):
super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
self.actionList = actionList
self.resultFilename = resultFilename
self.abort = False
self.prevStep = 'start'
self.totalDoneFactor = 0.0
self.startTime = time.time()
self.sliceStartTime = time.time()
self.sizer = wx.GridBagSizer(2, 2)
self.statusText = wx.StaticText(self, -1, "Building: %s" % (resultFilename))
self.progressGauge = wx.Gauge(self, -1)
self.progressGauge.SetRange(10000)
self.progressGauge2 = wx.Gauge(self, -1)
self.progressGauge2.SetRange(len(self.actionList))
self.abortButton = wx.Button(self, -1, "Abort")
self.sizer.Add(self.statusText, (0,0), span=(1,3))
self.sizer.Add(self.progressGauge, (1, 0), span=(1,3), flag=wx.EXPAND)
self.sizer.Add(self.progressGauge2, (2, 0), span=(1,3), flag=wx.EXPAND)
self.sizer.Add(self.abortButton, (3,0), span=(1,3), flag=wx.ALIGN_CENTER)
self.sizer.AddGrowableCol(0)
self.sizer.AddGrowableRow(0)
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
self.SetSizer(self.sizer)
self.Layout()
self.Fit()
threading.Thread(target=self.OnRun).start()
def OnAbort(self, e):
if self.abort:
self.Close()
else:
self.abort = True
self.abortButton.SetLabel('Close')
def SetProgress(self, stepName, layer, maxLayer):
if self.prevStep != stepName:
2012-05-04 10:03:08 +00:00
self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
newTime = time.time()
#print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
self.startTime = newTime
self.prevStep = stepName
2012-05-04 10:03:08 +00:00
progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
self.progressGauge.SetValue(int(progresValue))
self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
def OnRun(self):
resultFile = open(self.resultFilename, "w")
put = profile.setTempOverride
for action in self.actionList:
wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.actionList.index(action) + 1, len(self.actionList)))
p = subprocess.Popen(action.sliceCmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
line = p.stdout.readline()
maxValue = 1
self.progressLog = []
while(len(line) > 0):
line = line.rstrip()
if line[0:9] == "Progress[" and line[-1:] == "]":
progress = line[9:-1].split(":")
if len(progress) > 2:
maxValue = int(progress[2])
wx.CallAfter(self.SetProgress, progress[0], int(progress[1]), maxValue)
else:
print line
self.progressLog.append(line)
wx.CallAfter(self.statusText.SetLabel, line)
if self.abort:
p.terminate()
wx.CallAfter(self.statusText.SetLabel, "Aborted by user.")
resultFile.close()
return
line = p.stdout.readline()
self.returnCode = p.wait()
put('machine_center_x', action.centerX - self.extruderOffset[action.extruder].x)
put('machine_center_y', action.centerY - self.extruderOffset[action.extruder].y)
put('clear_z', action.clearZ)
put('extruder', action.extruder)
if action == self.actionList[0]:
resultFile.write(';TYPE:CUSTOM\n')
resultFile.write('T%d\n' % (action.extruder))
currentExtruder = action.extruder
resultFile.write(profile.getAlterationFileContents('start.gcode'))
else:
#reset the extrusion length, and move to the next object center.
resultFile.write(';TYPE:CUSTOM\n')
resultFile.write(profile.getAlterationFileContents('nextobject.gcode'))
resultFile.write(';PRINTNR:%d\n' % self.actionList.index(action))
profile.resetTempOverride()
f = open(action.filename[: action.filename.rfind('.')] + "_export.project_tmp", "r")
data = f.read(4096)
while data != '':
resultFile.write(data)
data = f.read(4096)
f.close()
os.remove(action.filename[: action.filename.rfind('.')] + "_export.project_tmp")
wx.CallAfter(self.progressGauge.SetValue, 10000)
self.totalDoneFactor = 0.0
wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
resultFile.write(';TYPE:CUSTOM\n')
resultFile.write(profile.getAlterationFileContents('end.gcode'))
resultFile.close()
gcode = gcodeInterpreter.gcode()
gcode.load(self.resultFilename)
self.abort = True
sliceTime = time.time() - self.sliceStartTime
status = "Build: %s" % (self.resultFilename)
status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
status += "\nFilament: %.2fm %.2fg" % (gcode.extrusionAmount / 1000, gcode.calculateWeight() * 1000)
status += "\nPrint time: %02d:%02d" % (int(gcode.totalMoveTimeMinute / 60), int(gcode.totalMoveTimeMinute % 60))
cost = gcode.calculateCost()
if cost != False:
status += "\nCost: %s" % (cost)
wx.CallAfter(self.statusText.SetLabel, status)
wx.CallAfter(self.OnSliceDone)
def OnSliceDone(self):
self.abortButton.Destroy()
self.closeButton = wx.Button(self, -1, "Close")
self.printButton = wx.Button(self, -1, "Print")
self.sizer.Add(self.closeButton, (3,0), span=(1,1))
self.sizer.Add(self.printButton, (3,1), span=(1,1))
if exporer.hasExporer():
self.openFileLocationButton = wx.Button(self, -1, "Open file location")
self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
self.sizer.Add(self.openFileLocationButton, (3,2), span=(1,1))
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
self.Bind(wx.EVT_BUTTON, self.OnPrint, self.printButton)
self.Layout()
self.Fit()
def OnOpenFileLocation(self, e):
exporer.openExporer(self.resultFilename)
def OnPrint(self, e):
printWindow.printFile(self.resultFilename)
class preferencesDialog(configBase.configWindowBase):
def __init__(self, parent):
super(preferencesDialog, self).__init__(title="Project Planner Preferences")
self.parent = parent
wx.EVT_CLOSE(self, self.OnClose)
extruderAmount = int(profile.getPreference('extruder_amount'))
left, right, main = self.CreateConfigPanel(self)
configBase.TitleRow(left, 'Machine head size')
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')
validators.validFloat(c, 0.1)
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')
validators.validFloat(c, 0.1)
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')
validators.validFloat(c, 0.1)
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')
validators.validFloat(c, 0.1)
self.okButton = wx.Button(left, -1, 'Ok')
left.GetSizer().Add(self.okButton, (left.GetSizer().GetRows(), 1))
self.okButton.Bind(wx.EVT_BUTTON, self.OnClose)
self.MakeModal(True)
main.Fit()
self.Fit()
def OnClose(self, e):
self.parent.headSizeMin = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_min_y'),0)
self.parent.headSizeMax = util3d.Vector3(profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_max_y'),0)
self.MakeModal(False)
self.Destroy()
def main():
app = wx.App(False)
projectPlanner().Show(True)
app.MainLoop()
if __name__ == '__main__':
main()