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))