Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
84d2b079b2
2 changed files with 206 additions and 66 deletions
|
@ -1,7 +1,7 @@
|
|||
from __future__ import absolute_import
|
||||
import __init__
|
||||
|
||||
import wx, os, platform, types, webbrowser, math, subprocess
|
||||
import wx, os, platform, types, webbrowser, math, subprocess, threading, time
|
||||
import ConfigParser
|
||||
|
||||
from wx import glcanvas
|
||||
|
@ -24,10 +24,13 @@ from util import util3d
|
|||
from util import stl
|
||||
from util import sliceRun
|
||||
|
||||
class Action():
|
||||
pass
|
||||
|
||||
class projectPlanner(wx.Frame):
|
||||
"Main user interface window"
|
||||
def __init__(self):
|
||||
super(projectPlanner, self).__init__(None, title='Cura')
|
||||
super(projectPlanner, self).__init__(None, title='Cura - Project Planner')
|
||||
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
wx.EVT_CLOSE(self, self.OnClose)
|
||||
|
@ -68,9 +71,9 @@ class projectPlanner(wx.Frame):
|
|||
self.remButton = wx.Button(self, -1, "Remove")
|
||||
self.sliceButton = wx.Button(self, -1, "Slice")
|
||||
|
||||
sizer.Add(self.toolbar, (0,0), span=(1,1), flag=wx.EXPAND)
|
||||
sizer.Add(self.preview, (1,0), span=(3,1), flag=wx.EXPAND)
|
||||
sizer.Add(self.listbox, (0,1), span=(2,2), flag=wx.EXPAND)
|
||||
sizer.Add(self.toolbar, (0,0), span=(1,3), flag=wx.EXPAND)
|
||||
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))
|
||||
|
@ -81,7 +84,25 @@ class projectPlanner(wx.Frame):
|
|||
self.remButton.Bind(wx.EVT_BUTTON, self.OnRemModel)
|
||||
self.sliceButton.Bind(wx.EVT_BUTTON, self.OnSlice)
|
||||
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)
|
||||
|
||||
self.scaleCtrl.Bind(wx.EVT_TEXT, self.OnScaleChange)
|
||||
self.rotateCtrl.Bind(wx.EVT_SPINCTRL, self.OnRotateChange)
|
||||
|
||||
self.SetSize((800,600))
|
||||
|
||||
def OnClose(self, e):
|
||||
|
@ -162,7 +183,11 @@ class projectPlanner(wx.Frame):
|
|||
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))
|
||||
self.preview.Refresh()
|
||||
|
||||
def OnAddModel(self, e):
|
||||
|
@ -201,6 +226,8 @@ class projectPlanner(wx.Frame):
|
|||
put('add_start_end_gcode', 'False')
|
||||
put('gcode_extension', 'project_tmp')
|
||||
|
||||
clearZ = 0
|
||||
actionList = []
|
||||
for item in self.list:
|
||||
put('machine_center_x', item.centerX)
|
||||
put('machine_center_y', item.centerY)
|
||||
|
@ -212,7 +239,14 @@ class projectPlanner(wx.Frame):
|
|||
put('swap_xz', item.swapXZ)
|
||||
put('swap_yz', item.swapYZ)
|
||||
|
||||
item.sliceCmd = sliceRun.getSliceCommand(item.filename)
|
||||
action = Action()
|
||||
action.sliceCmd = sliceRun.getSliceCommand(item.filename)
|
||||
action.centerX = item.centerX
|
||||
action.centerY = item.centerY
|
||||
action.filename = item.filename
|
||||
clearZ = max(clearZ, item.getMaximum().z * item.scale)
|
||||
action.clearZ = clearZ
|
||||
actionList.append(action)
|
||||
|
||||
#Restore the old profile.
|
||||
profile.loadGlobalProfileFromString(oldProfile)
|
||||
|
@ -222,44 +256,12 @@ class projectPlanner(wx.Frame):
|
|||
if dlg.ShowModal() != wx.ID_OK:
|
||||
dlg.Destroy()
|
||||
return
|
||||
resultFile = open(dlg.GetPath(), "w")
|
||||
resultFilename = dlg.GetPath()
|
||||
dlg.Destroy()
|
||||
|
||||
i = 1
|
||||
maxZ = 0
|
||||
prevItem = None
|
||||
for item in self.list:
|
||||
subprocess.call(item.sliceCmd)
|
||||
|
||||
maxZ = max(maxZ, item.getMaximum().z * item.scale)
|
||||
put('machine_center_x', item.centerX)
|
||||
put('machine_center_y', item.centerY)
|
||||
put('clear_z', maxZ)
|
||||
|
||||
if prevItem == None:
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
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' % (i))
|
||||
profile.loadGlobalProfileFromString(oldProfile)
|
||||
|
||||
f = open(item.filename[: item.filename.rfind('.')] + "_export.project_tmp", "r")
|
||||
data = f.read(4096)
|
||||
while data != '':
|
||||
resultFile.write(data)
|
||||
data = f.read(4096)
|
||||
f.close()
|
||||
os.remove(item.filename[: item.filename.rfind('.')] + "_export.project_tmp")
|
||||
i += 1
|
||||
|
||||
prevItem = item
|
||||
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write(profile.getAlterationFileContents('end.gcode'))
|
||||
resultFile.close()
|
||||
pspw = ProjectSliceProgressWindow(actionList, resultFilename)
|
||||
pspw.Centre()
|
||||
pspw.Show(True)
|
||||
|
||||
def loadModelFile(self, item):
|
||||
item.load(item.filename)
|
||||
|
@ -283,12 +285,29 @@ class projectPlanner(wx.Frame):
|
|||
|
||||
self.updateModelTransform(item)
|
||||
|
||||
item.centerX = -item.getMinimum().x + 5
|
||||
item.centerY = -item.getMinimum().y + 5
|
||||
|
||||
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.updateModelTransform(self.selection)
|
||||
|
||||
def updateModelTransform(self, item):
|
||||
scale = item.scale
|
||||
rotate = item.rotate
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
scaleZ = scale
|
||||
rotate = item.rotate / 180.0 * math.pi
|
||||
scaleX = 1.0
|
||||
scaleY = 1.0
|
||||
scaleZ = 1.0
|
||||
if item.flipX:
|
||||
scaleX = -scaleX
|
||||
if item.flipY:
|
||||
|
@ -495,15 +514,15 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
|||
glDisable(GL_LIGHTING)
|
||||
|
||||
if self.parent.selection == item:
|
||||
if item.gotHit:
|
||||
glColor3f(1.0,0.3,0.0)
|
||||
else:
|
||||
glColor3f(1.0,1.0,0.0)
|
||||
opengl.DrawBox(vMin, vMax)
|
||||
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:
|
||||
|
@ -522,6 +541,129 @@ class PreviewGLCanvas(glcanvas.GLCanvas):
|
|||
|
||||
glFlush()
|
||||
|
||||
class ProjectSliceProgressWindow(wx.Frame):
|
||||
def __init__(self, actionList, resultFilename):
|
||||
super(ProjectSliceProgressWindow, self).__init__(None, title='Cura')
|
||||
self.actionList = actionList
|
||||
self.resultFilename = resultFilename
|
||||
self.abort = False
|
||||
self.prevStep = 'start'
|
||||
self.totalDoneFactor = 0.0
|
||||
self.startTime = time.time()
|
||||
|
||||
#How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
|
||||
# TODO: Duplicate with sliceProgressPanel, move to sliceRun.
|
||||
self.sliceStepTimeFactor = {
|
||||
'start': 3.3713991642,
|
||||
'slice': 15.4984838963,
|
||||
'preface': 5.17178297043,
|
||||
'inset': 116.362634182,
|
||||
'fill': 215.702672005,
|
||||
'multiply': 21.9536788464,
|
||||
'speed': 12.759510994,
|
||||
'raft': 31.4580039978,
|
||||
'skirt': 19.3436040878,
|
||||
'skin': 1.0,
|
||||
'joris': 1.0,
|
||||
'comb': 23.7805759907,
|
||||
'cool': 27.148763895,
|
||||
'dimension': 90.4914340973
|
||||
}
|
||||
self.totalRunTimeFactor = 0
|
||||
for v in self.sliceStepTimeFactor.itervalues():
|
||||
self.totalRunTimeFactor += v
|
||||
|
||||
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), flag=wx.ALIGN_CENTER)
|
||||
self.sizer.Add(self.progressGauge, (1, 0), flag=wx.EXPAND)
|
||||
self.sizer.Add(self.progressGauge2, (2, 0), flag=wx.EXPAND)
|
||||
self.sizer.Add(self.abortButton, (3,0), flag=wx.ALIGN_CENTER)
|
||||
|
||||
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):
|
||||
self.abort = True
|
||||
|
||||
def SetProgress(self, stepName, layer, maxLayer):
|
||||
if self.prevStep != stepName:
|
||||
self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
|
||||
newTime = time.time()
|
||||
#print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
|
||||
self.startTime = newTime
|
||||
self.prevStep = stepName
|
||||
|
||||
progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.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.putProfileSetting
|
||||
for action in 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.")
|
||||
return
|
||||
line = p.stdout.readline()
|
||||
self.returnCode = p.wait()
|
||||
|
||||
oldProfile = profile.getGlobalProfileString()
|
||||
put('machine_center_x', action.centerX)
|
||||
put('machine_center_y', action.centerY)
|
||||
put('clear_z', action.clearZ)
|
||||
|
||||
if action == self.actionList[0]:
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
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.loadGlobalProfileFromString(oldProfile)
|
||||
|
||||
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)
|
||||
wx.CallAfter(self.progressGauge2.SetValue, self.actionList.index(action) + 1)
|
||||
|
||||
resultFile.write(';TYPE:CUSTOM\n')
|
||||
resultFile.write(profile.getAlterationFileContents('end.gcode'))
|
||||
resultFile.close()
|
||||
|
||||
def main():
|
||||
app = wx.App(False)
|
||||
projectPlanner().Show(True)
|
||||
|
|
|
@ -115,17 +115,16 @@ def getGlobalProfileString():
|
|||
return '#'.join(ret)
|
||||
|
||||
def getProfileSetting(name):
|
||||
if name in profileDefaultSettings:
|
||||
default = profileDefaultSettings[name]
|
||||
else:
|
||||
print "Missing default setting for: '" + name + "'"
|
||||
profileDefaultSettings[name] = ''
|
||||
default = ''
|
||||
|
||||
#Check if we have a configuration file loaded, else load the default.
|
||||
if not globals().has_key('globalProfileParser'):
|
||||
loadGlobalProfile(getDefaultProfilePath())
|
||||
if not globalProfileParser.has_option('profile', name):
|
||||
if name in profileDefaultSettings:
|
||||
default = profileDefaultSettings[name]
|
||||
else:
|
||||
print "Missing default setting for: '" + name + "'"
|
||||
profileDefaultSettings[name] = ''
|
||||
default = ''
|
||||
if not globalProfileParser.has_section('profile'):
|
||||
globalProfileParser.add_section('profile')
|
||||
globalProfileParser.set('profile', name, str(default))
|
||||
|
@ -154,18 +153,17 @@ def getPreferencePath():
|
|||
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../preferences.ini"))
|
||||
|
||||
def getPreference(name):
|
||||
if name in preferencesDefaultSettings:
|
||||
default = preferencesDefaultSettings[name]
|
||||
else:
|
||||
print "Missing default setting for: '" + name + "'"
|
||||
preferencesDefaultSettings[name] = ''
|
||||
default = ''
|
||||
|
||||
global globalPreferenceParser
|
||||
if globalPreferenceParser == None:
|
||||
globalPreferenceParser = ConfigParser.ConfigParser()
|
||||
globalPreferenceParser.read(getPreferencePath())
|
||||
if not globalPreferenceParser.has_option('preference', name):
|
||||
if name in preferencesDefaultSettings:
|
||||
default = preferencesDefaultSettings[name]
|
||||
else:
|
||||
print "Missing default setting for: '" + name + "'"
|
||||
preferencesDefaultSettings[name] = ''
|
||||
default = ''
|
||||
if not globalPreferenceParser.has_section('preference'):
|
||||
globalPreferenceParser.add_section('preference')
|
||||
globalPreferenceParser.set('preference', name, str(default))
|
||||
|
|
Loading…
Reference in a new issue