1 from __future__ import absolute_import
4 import wx, sys, os, math, threading, subprocess, time
6 from util import profile
7 from util import sliceRun
8 from util import exporer
9 from util import gcodeInterpreter
11 class sliceProgessPanel(wx.Panel):
12 def __init__(self, mainWindow, parent, filelist):
13 wx.Panel.__init__(self, parent, -1)
14 self.mainWindow = mainWindow
15 self.filelist = filelist
18 #How long does each step take compared to the others. This is used to make a better scaled progress bar, and guess time left.
19 self.sliceStepTimeFactor = {
20 'start': 3.3713991642,
21 'slice': 15.4984838963,
22 'preface': 5.17178297043,
23 'inset': 116.362634182,
24 'fill': 215.702672005,
25 'multiply': 21.9536788464,
26 'speed': 12.759510994,
27 'raft': 31.4580039978,
28 'skirt': 19.3436040878,
31 'comb': 23.7805759907,
33 'dimension': 90.4914340973
35 self.totalRunTimeFactor = 0
36 for v in self.sliceStepTimeFactor.itervalues():
37 self.totalRunTimeFactor += v
39 box = wx.StaticBox(self, -1, filelist[0])
40 self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
42 mainSizer = wx.BoxSizer(wx.VERTICAL)
43 mainSizer.Add(self.sizer, 0, flag=wx.EXPAND)
45 self.statusText = wx.StaticText(self, -1, "Starting...")
46 self.progressGauge = wx.Gauge(self, -1)
47 self.progressGauge.SetRange(10000 * len(filelist))
48 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
49 self.sizer.Add(self.statusText, 2, flag=wx.ALIGN_CENTER )
50 self.sizer.Add(self.progressGauge, 2)
51 self.sizer.Add(self.abortButton, 0)
53 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
55 self.SetSizer(mainSizer)
56 self.prevStep = 'start'
57 self.totalDoneFactor = 0.0
58 self.startTime = time.time()
59 if profile.getPreference('save_profile') == 'True':
60 profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini")
62 oldProfile = profile.getGlobalProfileString()
63 for filename in self.filelist:
64 idx = self.filelist.index(filename)
67 profile.putProfileSetting('fan_enabled', 'False')
68 profile.putProfileSetting('skirt_line_count', '0')
69 profile.putProfileSetting('machine_center_x', profile.getProfileSettingFloat('machine_center_x') + float(profile.getPreference('extruder_offset_x%d' % (idx))))
70 profile.putProfileSetting('machine_center_y', profile.getProfileSettingFloat('machine_center_y') + float(profile.getPreference('extruder_offset_y%d' % (idx))))
71 if len(self.filelist) > 1:
72 profile.putProfileSetting('add_start_end_gcode', 'False')
73 profile.putProfileSetting('gcode_extension', 'multi_extrude_tmp')
74 cmdList.append(sliceRun.getSliceCommand(filename))
75 profile.loadGlobalProfileFromString(oldProfile)
76 self.thread = WorkerThread(self, filelist, cmdList)
80 self.mainWindow.removeSliceProgress(self)
84 def OnShowGCode(self, e):
85 self.mainWindow.preview3d.loadModelFiles(self.filelist)
86 self.mainWindow.preview3d.setViewMode("GCode")
88 def OnShowLog(self, e):
89 LogWindow('\n'.join(self.progressLog))
91 def OnOpenFileLocation(self, e):
92 exporer.openExporer(self.filelist[0][: self.filelist[0].rfind('.')] + "_export.gcode")
94 def OnSliceDone(self, result):
95 self.progressGauge.Destroy()
96 self.abortButton.Destroy()
97 self.progressLog = result.progressLog
98 self.logButton = wx.Button(self, -1, "Show Log")
99 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
100 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
101 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
102 self.sizer.Add(self.logButton, 0)
103 if result.returnCode == 0:
104 status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
105 status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
106 cost = result.gcode.calculateCost()
108 status += "Cost: %s" % (cost)
109 self.statusText.SetLabel(status)
110 if exporer.hasExporer():
111 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
112 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
113 self.sizer.Add(self.openFileLocationButton, 0)
114 self.showButton = wx.Button(self, -1, "Show result")
115 self.Bind(wx.EVT_BUTTON, self.OnShowGCode, self.showButton)
116 self.sizer.Add(self.showButton, 0)
118 self.statusText.SetLabel("Something went wrong during slicing!")
119 self.sizer.Add(self.abortButton, 0)
123 if self.mainWindow.preview3d.loadReModelFiles(self.filelist):
124 self.mainWindow.preview3d.setViewMode("GCode")
126 def SetProgress(self, stepName, layer, maxLayer):
127 if self.prevStep != stepName:
128 self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
129 newTime = time.time()
130 #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
131 self.startTime = newTime
132 self.prevStep = stepName
134 progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
135 self.progressGauge.SetValue(int(progresValue))
136 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
138 class WorkerThread(threading.Thread):
139 def __init__(self, notifyWindow, filelist, cmdList):
140 threading.Thread.__init__(self)
141 self.filelist = filelist
142 self.notifyWindow = notifyWindow
143 self.cmdList = cmdList
148 p = subprocess.Popen(self.cmdList[self.fileIdx], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
149 line = p.stdout.readline()
150 self.progressLog = []
152 while(len(line) > 0):
154 if line[0:9] == "Progress[" and line[-1:] == "]":
155 progress = line[9:-1].split(":")
156 if len(progress) > 2:
157 maxValue = int(progress[2])
158 wx.CallAfter(self.notifyWindow.SetProgress, progress[0], int(progress[1]), maxValue)
161 self.progressLog.append(line)
162 wx.CallAfter(self.notifyWindow.statusText.SetLabel, line)
163 if self.notifyWindow.abort:
165 wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.")
167 line = p.stdout.readline()
168 self.returnCode = p.wait()
169 logfile = open(self.filelist[self.fileIdx][: self.filelist[self.fileIdx].rfind('.')] + "_export.log", "w")
170 for logLine in self.progressLog:
171 logfile.write(logLine)
175 if self.fileIdx == len(self.cmdList):
176 if len(self.filelist) > 1:
177 self._stitchMultiExtruder()
178 self.gcode = gcodeInterpreter.gcode()
179 self.gcode.load(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode')
180 wx.CallAfter(self.notifyWindow.OnSliceDone, self)
184 def _stitchMultiExtruder(self):
186 resultFile = open(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode', "w")
187 resultFile.write(';TYPE:CUSTOM\n')
188 resultFile.write(profile.getAlterationFileContents('start.gcode'))
189 for filename in self.filelist:
190 files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r"))
193 resultFile.write('T%d\n' % (currentExtruder))
202 if line.startswith(';LAYER:'):
205 nextExtruder = files.index(f)
206 resultFile.write(';LAYER:%d\n' % (layerNr))
207 resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
208 if nextExtruder != currentExtruder:
209 resultFile.write("G1 E-5 F5000\n")
210 resultFile.write("G92 E0\n")
211 resultFile.write("T%d\n" % (nextExtruder))
212 resultFile.write("G1 E5 F5000\n")
213 resultFile.write("G92 E0\n")
214 currentExtruder = nextExtruder
216 resultFile.write(line)
220 for filename in self.filelist:
221 os.remove(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp')
222 resultFile.write(';TYPE:CUSTOM\n')
223 resultFile.write(profile.getAlterationFileContents('end.gcode'))
226 class LogWindow(wx.Frame):
227 def __init__(self, logText):
228 super(LogWindow, self).__init__(None, title="Slice log")
229 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
230 self.SetSize((400,300))