chiark / gitweb /
e3e96d258aee70cce37f0ecdc32025156e974d5d
[cura.git] / Cura / gui / sliceProgessPanel.py
1 from __future__ import absolute_import
2 import __init__
3
4 import wx, sys, os, math, threading, subprocess, time
5
6 from util import profile
7 from util import sliceRun
8 from util import exporer
9 from util import gcodeInterpreter
10
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
16                 self.abort = False
17                 
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,
29                         'skin': 1.0,
30                         'joris': 1.0,
31                         'comb': 23.7805759907,
32                         'cool': 27.148763895,
33                         'dimension': 90.4914340973
34                 }
35                 self.totalRunTimeFactor = 0
36                 for v in self.sliceStepTimeFactor.itervalues():
37                         self.totalRunTimeFactor += v
38
39                 box = wx.StaticBox(self, -1, filelist[0])
40                 self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
41
42                 mainSizer = wx.BoxSizer(wx.VERTICAL) 
43                 mainSizer.Add(self.sizer, 0, flag=wx.EXPAND)
44
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)
52
53                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
54
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")
61                 cmdList = []
62                 oldProfile = profile.getGlobalProfileString()
63                 for filename in self.filelist:
64                         idx = self.filelist.index(filename)
65                         print filename, idx
66                         if idx > 0:
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                                 profile.putProfileSetting('alternative_center', self.filelist[0])
72                         if len(self.filelist) > 1:
73                                 profile.putProfileSetting('add_start_end_gcode', 'False')
74                                 profile.putProfileSetting('gcode_extension', 'multi_extrude_tmp')
75                         cmdList.append(sliceRun.getSliceCommand(filename))
76                 profile.loadGlobalProfileFromString(oldProfile)
77                 self.thread = WorkerThread(self, filelist, cmdList)
78         
79         def OnAbort(self, e):
80                 if self.abort:
81                         self.mainWindow.removeSliceProgress(self)
82                 else:
83                         self.abort = True
84         
85         def OnShowGCode(self, e):
86                 self.mainWindow.preview3d.loadModelFiles(self.filelist)
87                 self.mainWindow.preview3d.setViewMode("GCode")
88         
89         def OnShowLog(self, e):
90                 LogWindow('\n'.join(self.progressLog))
91         
92         def OnOpenFileLocation(self, e):
93                 exporer.openExporer(self.filelist[0][: self.filelist[0].rfind('.')] + "_export.gcode")
94         
95         def OnSliceDone(self, result):
96                 self.progressGauge.Destroy()
97                 self.abortButton.Destroy()
98                 self.progressLog = result.progressLog
99                 self.logButton = wx.Button(self, -1, "Show Log")
100                 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
101                 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
102                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
103                 self.sizer.Add(self.logButton, 0)
104                 if result.returnCode == 0:
105                         status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
106                         status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
107                         cost = result.gcode.calculateCost()
108                         if cost != False:
109                                 status += " Cost: %s" % (cost)
110                         self.statusText.SetLabel(status)
111                         if exporer.hasExporer():
112                                 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
113                                 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
114                                 self.sizer.Add(self.openFileLocationButton, 0)
115                         self.showButton = wx.Button(self, -1, "Show result")
116                         self.Bind(wx.EVT_BUTTON, self.OnShowGCode, self.showButton)
117                         self.sizer.Add(self.showButton, 0)
118                 else:
119                         self.statusText.SetLabel("Something went wrong during slicing!")
120                 self.sizer.Add(self.abortButton, 0)
121                 self.sizer.Layout()
122                 self.Layout()
123                 self.abort = True
124                 if self.mainWindow.preview3d.loadReModelFiles(self.filelist):
125                         self.mainWindow.preview3d.setViewMode("GCode")
126         
127         def SetProgress(self, stepName, layer, maxLayer):
128                 if self.prevStep != stepName:
129                         self.totalDoneFactor += self.sliceStepTimeFactor[self.prevStep]
130                         newTime = time.time()
131                         #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
132                         self.startTime = newTime
133                         self.prevStep = stepName
134                 
135                 progresValue = ((self.totalDoneFactor + self.sliceStepTimeFactor[stepName] * layer / maxLayer) / self.totalRunTimeFactor) * 10000
136                 self.progressGauge.SetValue(int(progresValue))
137                 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
138
139 class WorkerThread(threading.Thread):
140         def __init__(self, notifyWindow, filelist, cmdList):
141                 threading.Thread.__init__(self)
142                 self.filelist = filelist
143                 self.notifyWindow = notifyWindow
144                 self.cmdList = cmdList
145                 self.fileIdx = 0
146                 self.start()
147
148         def run(self):
149                 p = subprocess.Popen(self.cmdList[self.fileIdx], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
150                 line = p.stdout.readline()
151                 self.progressLog = []
152                 maxValue = 1
153                 while(len(line) > 0):
154                         line = line.rstrip()
155                         if line[0:9] == "Progress[" and line[-1:] == "]":
156                                 progress = line[9:-1].split(":")
157                                 if len(progress) > 2:
158                                         maxValue = int(progress[2])
159                                 wx.CallAfter(self.notifyWindow.SetProgress, progress[0], int(progress[1]), maxValue)
160                         else:
161                                 #print line
162                                 self.progressLog.append(line)
163                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, line)
164                         if self.notifyWindow.abort:
165                                 p.terminate()
166                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.")
167                                 return
168                         line = p.stdout.readline()
169                 self.returnCode = p.wait()
170                 logfile = open(self.filelist[self.fileIdx][: self.filelist[self.fileIdx].rfind('.')] + "_export.log", "w")
171                 for logLine in self.progressLog:
172                         logfile.write(logLine)
173                         logfile.write('\n')
174                 logfile.close()
175                 self.fileIdx += 1
176                 if self.fileIdx == len(self.cmdList):
177                         if len(self.filelist) > 1:
178                                 self._stitchMultiExtruder()
179                         self.gcode = gcodeInterpreter.gcode()
180                         self.gcode.load(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode')
181                         wx.CallAfter(self.notifyWindow.OnSliceDone, self)
182                 else:
183                         self.run()
184         
185         def _stitchMultiExtruder(self):
186                 files = []
187                 resultFile = open(self.filelist[0][:self.filelist[0].rfind('.')]+'_export.gcode', "w")
188                 resultFile.write(';TYPE:CUSTOM\n')
189                 resultFile.write(profile.getAlterationFileContents('start.gcode'))
190                 for filename in self.filelist:
191                         files.append(open(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp', "r"))
192                 
193                 currentExtruder = 0
194                 resultFile.write('T%d\n' % (currentExtruder))
195                 layerNr = -1
196                 hasLine = True
197                 while hasLine:
198                         hasLine = False
199                         for f in files:
200                                 layerHasLine = False
201                                 for line in f:
202                                         hasLine = True
203                                         if line.startswith(';LAYER:'):
204                                                 break
205                                         if not layerHasLine:
206                                                 nextExtruder = files.index(f)
207                                                 resultFile.write(';LAYER:%d\n' % (layerNr))
208                                                 resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
209                                                 if nextExtruder != currentExtruder:
210                                                         resultFile.write("G1 E-5 F5000\n")
211                                                         resultFile.write("G92 E0\n")
212                                                         resultFile.write("T%d\n" % (nextExtruder))
213                                                         resultFile.write("G1 E5 F5000\n")
214                                                         resultFile.write("G92 E0\n")
215                                                         currentExtruder = nextExtruder
216                                                 layerHasLine = True
217                                         resultFile.write(line)
218                         layerNr += 1
219                 for f in files:
220                         f.close()
221                 for filename in self.filelist:
222                         os.remove(filename[:filename.rfind('.')]+'_export.multi_extrude_tmp')
223                 resultFile.write(';TYPE:CUSTOM\n')
224                 resultFile.write(profile.getAlterationFileContents('end.gcode'))
225                 resultFile.close()
226
227 class LogWindow(wx.Frame):
228         def __init__(self, logText):
229                 super(LogWindow, self).__init__(None, title="Slice log")
230                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
231                 self.SetSize((400,300))
232                 self.Centre()
233                 self.Show(True)
234