OctoPrint/Cura/gui/batchRun.py

237 lines
8.2 KiB
Python

from __future__ import absolute_import
import __init__
import wx, os, platform, types, webbrowser, math, subprocess, multiprocessing, threading, time, re, shutil
from util import profile
from util import sliceRun
from util import meshLoader
from gui import dropTarget
class batchRunWindow(wx.Frame):
def __init__(self, parent):
super(batchRunWindow, self).__init__(parent, title='Cura - Batch run')
self.list = []
self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions()))
wx.EVT_CLOSE(self, self.OnClose)
self.panel = wx.Panel(self, -1)
self.SetSizer(wx.BoxSizer(wx.VERTICAL))
self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
#self.SetIcon(icon.getMainIcon())
self.sizer = wx.GridBagSizer(2,2)
self.panel.SetSizer(self.sizer)
self.listbox = wx.ListBox(self.panel, -1, choices=[])
self.addButton = wx.Button(self.panel, -1, "Add")
self.remButton = wx.Button(self.panel, -1, "Remove")
self.sliceButton = wx.Button(self.panel, -1, "Prepare all")
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.listbox.Bind(wx.EVT_LISTBOX, self.OnListSelect)
self.sizer.Add(self.listbox, (0,0), span=(1,3), flag=wx.EXPAND)
self.sizer.Add(self.addButton, (1,0), span=(1,1))
self.sizer.Add(self.remButton, (1,1), span=(1,1))
self.sizer.Add(self.sliceButton, (1,2), span=(1,1))
self.sizer.AddGrowableCol(2)
self.sizer.AddGrowableRow(0)
def OnAddModel(self, e):
dlg=wx.FileDialog(self, "Open file to batch prepare", 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():
profile.putPreference('lastFile', filename)
self.list.append(filename)
self.selection = filename
self._updateListbox()
dlg.Destroy()
def OnDropFiles(self, filenames):
for filename in filenames:
profile.putPreference('lastFile', filename)
self.list.append(filename)
self.selection = filename
self._updateListbox()
def OnRemModel(self, e):
if self.selection == None:
return
self.list.remove(self.selection)
self._updateListbox()
def OnListSelect(self, e):
if self.listbox.GetSelection() == -1:
return
self.selection = self.list[self.listbox.GetSelection()]
def _updateListbox(self):
self.listbox.Clear()
for item in self.list:
self.listbox.AppendAndEnsureVisible(os.path.split(item)[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 OnClose(self, e):
self.Destroy()
def OnSlice(self, e):
sliceCmdList = []
for filename in self.list:
sliceCmdList.append(sliceRun.getSliceCommand(filename))
bspw = BatchSliceProgressWindow(self.list[:], sliceCmdList)
bspw.Centre()
bspw.Show(True)
class BatchSliceProgressWindow(wx.Frame):
def __init__(self, filenameList, sliceCmdList):
super(BatchSliceProgressWindow, self).__init__(None, title='Cura')
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
self.filenameList = filenameList
self.sliceCmdList = sliceCmdList
self.abort = False
self.sliceStartTime = time.time()
try:
self.threadCount = multiprocessing.cpu_count() - 1
except:
self.threadCount = 1
if self.threadCount < 1:
self.threadCount = 1
if self.threadCount > len(self.sliceCmdList):
self.threadCount = len(self.sliceCmdList)
self.cmdIndex = 0
self.prevStep = []
self.totalDoneFactor = []
for i in xrange(0, self.threadCount):
self.prevStep.append('start')
self.totalDoneFactor.append(0.0)
self.sizer = wx.GridBagSizer(2, 2)
self.progressGauge = []
self.statusText = []
for i in xrange(0, self.threadCount):
self.statusText.append(wx.StaticText(self, -1, "Building: %d " % (len(self.sliceCmdList))))
self.progressGauge.append(wx.Gauge(self, -1))
self.progressGauge[i].SetRange(10000)
self.progressTextTotal = wx.StaticText(self, -1, "Done: 0/%d " % (len(self.sliceCmdList)))
self.progressGaugeTotal = wx.Gauge(self, -1)
self.progressGaugeTotal.SetRange(len(self.sliceCmdList))
self.abortButton = wx.Button(self, -1, "Abort")
for i in xrange(0, self.threadCount):
self.sizer.Add(self.statusText[i], (i*2,0), span=(1,4))
self.sizer.Add(self.progressGauge[i], (1+i*2, 0), span=(1,4), flag=wx.EXPAND)
self.sizer.Add(self.progressTextTotal, (self.threadCount*2,0), span=(1,4))
self.sizer.Add(self.progressGaugeTotal, (1+self.threadCount*2, 0), span=(1,4), flag=wx.EXPAND)
self.sizer.Add(self.abortButton, (2+self.threadCount*2,0), span=(1,4), 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.OnRunManager).start()
def OnAbort(self, e):
if self.abort:
self.Close()
else:
self.abort = True
self.abortButton.SetLabel('Close')
def SetProgress(self, index, stepName, layer, maxLayer):
if self.prevStep[index] != stepName:
self.totalDoneFactor[index] += sliceRun.sliceStepTimeFactor[self.prevStep[index]]
newTime = time.time()
self.prevStep[index] = stepName
progresValue = ((self.totalDoneFactor[index] + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
self.progressGauge[index].SetValue(int(progresValue))
self.statusText[index].SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
def OnRunManager(self):
threads = []
for i in xrange(0, self.threadCount):
threads.append(threading.Thread(target=self.OnRun, args=(i,)))
for t in threads:
t.start()
for t in threads:
t.join()
self.abort = True
sliceTime = time.time() - self.sliceStartTime
status = "Build: %d models" % (len(self.sliceCmdList))
status += "\nSlicing took: %02d:%02d" % (sliceTime / 60, sliceTime % 60)
wx.CallAfter(self.statusText[0].SetLabel, status)
wx.CallAfter(self.OnSliceDone)
def OnRun(self, index):
while self.cmdIndex < len(self.sliceCmdList):
action = self.sliceCmdList[self.cmdIndex]
self.cmdIndex += 1
wx.CallAfter(self.SetTitle, "Building: [%d/%d]" % (self.sliceCmdList.index(action) + 1, len(self.sliceCmdList)))
p = sliceRun.startSliceCommandProcess(action)
line = p.stdout.readline()
maxValue = 1
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, index, progress[0], int(progress[1]), maxValue)
else:
wx.CallAfter(self.statusText[index].SetLabel, line)
if self.abort:
p.terminate()
wx.CallAfter(self.statusText[index].SetLabel, "Aborted by user.")
return
line = p.stdout.readline()
self.returnCode = p.wait()
wx.CallAfter(self.progressGauge[index].SetValue, 10000)
self.totalDoneFactor[index] = 0.0
wx.CallAfter(self.progressTextTotal.SetLabel, "Done %d/%d" % (self.cmdIndex, len(self.sliceCmdList)))
wx.CallAfter(self.progressGaugeTotal.SetValue, self.cmdIndex)
def OnSliceDone(self):
self.abortButton.Destroy()
self.closeButton = wx.Button(self, -1, "Close")
self.sizer.Add(self.closeButton, (2+self.threadCount*2,0), span=(1,1))
if profile.getPreference('sdpath') != '':
self.copyToSDButton = wx.Button(self, -1, "To SDCard")
self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
self.sizer.Add(self.copyToSDButton, (2+self.threadCount*2,1), span=(1,1))
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.closeButton)
self.Layout()
self.Fit()
def OnCopyToSD(self, e):
for f in self.filenameList:
exportFilename = sliceRun.getExportFilename(f)
filename = os.path.basename(exportFilename)
if profile.getPreference('sdshortnames') == 'True':
filename = sliceRun.getShortFilename(filename)
shutil.copy(exportFilename, os.path.join(profile.getPreference('sdpath'), filename))