OctoPrint/Cura/gui/sliceProgessPanel.py

267 lines
10 KiB
Python

from __future__ import absolute_import
import __init__
import wx, sys, os, shutil, math, threading, subprocess, time, re, platform
from gui import taskbar
from gui import preferencesDialog
from util import profile
from util import sliceRun
from util import exporer
from util import gcodeInterpreter
class sliceProgessPanel(wx.Panel):
def __init__(self, mainWindow, parent, filelist):
wx.Panel.__init__(self, parent, -1)
self.mainWindow = mainWindow
self.filelist = filelist
self.abort = False
box = wx.StaticBox(self, -1, filelist[0])
self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
mainSizer = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(self.sizer, 0, flag=wx.EXPAND)
self.statusText = wx.StaticText(self, -1, "Starting...")
self.progressGauge = wx.Gauge(self, -1)
self.progressGauge.SetRange(10000 * len(filelist))
self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
self.sizer.Add(self.statusText, 2, flag=wx.ALIGN_CENTER )
self.sizer.Add(self.progressGauge, 2)
self.sizer.Add(self.abortButton, 0)
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
self.SetSizer(mainSizer)
self.prevStep = 'start'
self.totalDoneFactor = 0.0
self.startTime = time.time()
if profile.getPreference('save_profile') == 'True':
profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini")
cmdList = []
for filename in self.filelist:
idx = self.filelist.index(filename)
#print filename, idx
if idx > 0:
profile.setTempOverride('fan_enabled', 'False')
profile.setTempOverride('skirt_line_count', '0')
profile.setTempOverride('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx)))
profile.setTempOverride('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx)))
profile.setTempOverride('alternative_center', self.filelist[0])
if len(self.filelist) > 1:
profile.setTempOverride('add_start_end_gcode', 'False')
profile.setTempOverride('gcode_extension', 'multi_extrude_tmp')
cmdList.append(sliceRun.getSliceCommand(filename))
profile.resetTempOverride()
self.thread = WorkerThread(self, filelist, cmdList)
def OnAbort(self, e):
if self.abort:
self.mainWindow.removeSliceProgress(self)
else:
self.abort = True
def OnShowGCode(self, e):
self.mainWindow.preview3d.loadModelFiles(self.filelist)
self.mainWindow.preview3d.setViewMode("GCode")
def OnShowLog(self, e):
LogWindow('\n'.join(self.progressLog))
def OnOpenFileLocation(self, e):
exporer.openExporer(sliceRun.getExportFilename(self.filelist[0]))
def OnCopyToSD(self, e):
if profile.getPreference('sdpath') == '':
wx.MessageBox("You need to configure your SD card drive first before you can copy files to it.\nOpening the preferences now.", 'No SD card drive.', wx.OK | wx.ICON_INFORMATION)
prefDialog = preferencesDialog.preferencesDialog(self.GetParent())
prefDialog.Centre()
prefDialog.Show(True)
if profile.getPreference('sdpath') == '':
print "No path set"
return
exportFilename = sliceRun.getExportFilename(self.filelist[0])
filename = os.path.basename(exportFilename)
if profile.getPreference('sdshortnames') == 'True':
filename = sliceRun.getShortFilename(filename)
try:
shutil.copy(exportFilename, os.path.join(profile.getPreference('sdpath'), filename))
except:
self.GetParent().preview3d.ShowWarningPopup("Failed to copy file to SD card.")
return
self.GetParent().preview3d.ShowWarningPopup("Copy finished, safely remove SD card?", self.OnSafeRemove)
def OnSafeRemove(self):
if platform.system() == "Windows":
cmd = "%s %s>NUL" % (os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), profile.getPreference('sdpath'))
elif platform.system() == "Darwin":
cmd = "diskutil eject '%s' > /dev/null 2>&1" % (profile.getPreference('sdpath'))
else:
cmd = "umount '%s' > /dev/null 2>&1" % (profile.getPreference('sdpath'))
if os.system(cmd):
self.GetParent().preview3d.ShowWarningPopup("Safe remove failed.")
else:
self.GetParent().preview3d.ShowWarningPopup("You can now eject the card.")
def OnSliceDone(self, result):
self.progressGauge.Destroy()
self.abortButton.Destroy()
self.progressLog = result.progressLog
self.logButton = wx.Button(self, -1, "Show Log")
self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
self.sizer.Add(self.logButton, 0)
if result.returnCode == 0:
status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
cost = result.gcode.calculateCost()
if cost != False:
status += " Cost: %s" % (cost)
self.statusText.SetLabel(status)
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, 0)
if len(profile.getSDcardDrives()) > 0:
self.copyToSDButton = wx.Button(self, -1, "Copy to SDCard")
self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
self.sizer.Add(self.copyToSDButton, 0)
self.showButton = wx.Button(self, -1, "Show result")
self.Bind(wx.EVT_BUTTON, self.OnShowGCode, self.showButton)
self.sizer.Add(self.showButton, 0)
else:
self.statusText.SetLabel("Something went wrong during slicing!")
self.sizer.Add(self.abortButton, 0)
self.sizer.Layout()
self.Layout()
self.abort = True
if self.mainWindow.preview3d.loadReModelFiles(self.filelist):
self.mainWindow.preview3d.setViewMode("GCode")
taskbar.setBusy(self.GetParent(), False)
def SetProgress(self, stepName, layer, maxLayer):
if self.prevStep != stepName:
self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
newTime = time.time()
#print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
self.startTime = newTime
self.prevStep = stepName
progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
self.progressGauge.SetValue(int(progresValue))
taskbar.setProgress(self.GetParent(), int(progresValue), self.progressGauge.GetRange())
self.statusText.SetLabel("Preparing: processing %s [%d/%d]" % (stepName, layer, maxLayer))
class WorkerThread(threading.Thread):
def __init__(self, notifyWindow, filelist, cmdList):
threading.Thread.__init__(self)
self.filelist = filelist
self.notifyWindow = notifyWindow
self.cmdList = cmdList
self.fileIdx = 0
self.start()
def run(self):
p = sliceRun.startSliceCommandProcess(self.cmdList[self.fileIdx])
line = p.stdout.readline()
self.progressLog = []
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.notifyWindow.SetProgress, progress[0], int(progress[1]), maxValue)
else:
self.progressLog.append(line)
wx.CallAfter(self.notifyWindow.statusText.SetLabel, line)
if self.notifyWindow.abort:
p.terminate()
wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.")
return
line = p.stdout.readline()
line = p.stderr.readline()
while(len(line) > 0):
line = line.rstrip()
self.progressLog.append(line)
line = p.stderr.readline()
self.returnCode = p.wait()
self.fileIdx += 1
if self.fileIdx == len(self.cmdList):
if len(self.filelist) > 1:
self._stitchMultiExtruder()
gcodeFilename = sliceRun.getExportFilename(self.filelist[0])
gcodefile = open(gcodeFilename, "a")
for logLine in self.progressLog:
if logLine.startswith('Model error('):
gcodefile.write(';%s\n' % (logLine))
gcodefile.close()
wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Running plugins")
ret = profile.runPostProcessingPlugins(gcodeFilename)
if ret != None:
self.progressLog.append(ret)
self.gcode = gcodeInterpreter.gcode()
self.gcode.load(gcodeFilename)
profile.replaceGCodeTags(gcodeFilename, self.gcode)
wx.CallAfter(self.notifyWindow.OnSliceDone, self)
else:
self.run()
def _stitchMultiExtruder(self):
files = []
resultFile = open(sliceRun.getExportFilename(self.filelist[0]), "w")
resultFile.write(';TYPE:CUSTOM\n')
resultFile.write(profile.getAlterationFileContents('start.gcode'))
for filename in self.filelist:
if os.path.isfile(sliceRun.getExportFilename(filename, 'multi_extrude_tmp')):
files.append(open(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'), "r"))
else:
return
currentExtruder = 0
resultFile.write('T%d\n' % (currentExtruder))
layerNr = -1
hasLine = True
while hasLine:
hasLine = False
for f in files:
layerHasLine = False
for line in f:
hasLine = True
if line.startswith(';LAYER:'):
break
if 'Z' in line:
lastZ = float(re.search('Z([^\s]+)', line).group(1))
if not layerHasLine:
nextExtruder = files.index(f)
resultFile.write(';LAYER:%d\n' % (layerNr))
resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
if nextExtruder != currentExtruder:
resultFile.write(';TYPE:CUSTOM\n')
profile.setTempOverride('extruder', nextExtruder)
resultFile.write(profile.getAlterationFileContents('switchExtruder.gcode'))
profile.resetTempOverride()
currentExtruder = nextExtruder
layerHasLine = True
resultFile.write(line)
layerNr += 1
for f in files:
f.close()
for filename in self.filelist:
os.remove(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'))
resultFile.write(';TYPE:CUSTOM\n')
resultFile.write(profile.getAlterationFileContents('end.gcode'))
resultFile.close()
class LogWindow(wx.Frame):
def __init__(self, logText):
super(LogWindow, self).__init__(None, title="Slice log")
self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
self.SetSize((400,300))
self.Centre()
self.Show(True)