chiark / gitweb /
bd278ccbbdd0d59551c06353b5e23695f3048a78
[cura.git] / Cura / gui / sliceProgessPanel.py
1 from __future__ import absolute_import
2 import __init__
3
4 import wx, sys, os, shutil, math, threading, subprocess, time, re
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                 box = wx.StaticBox(self, -1, filelist[0])
19                 self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
20
21                 mainSizer = wx.BoxSizer(wx.VERTICAL) 
22                 mainSizer.Add(self.sizer, 0, flag=wx.EXPAND)
23
24                 self.statusText = wx.StaticText(self, -1, "Starting...")
25                 self.progressGauge = wx.Gauge(self, -1)
26                 self.progressGauge.SetRange(10000 * len(filelist))
27                 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
28                 self.sizer.Add(self.statusText, 2, flag=wx.ALIGN_CENTER )
29                 self.sizer.Add(self.progressGauge, 2)
30                 self.sizer.Add(self.abortButton, 0)
31
32                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
33
34                 self.SetSizer(mainSizer)
35                 self.prevStep = 'start'
36                 self.totalDoneFactor = 0.0
37                 self.startTime = time.time()
38                 if profile.getPreference('save_profile') == 'True':
39                         profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini")
40                 cmdList = []
41                 for filename in self.filelist:
42                         idx = self.filelist.index(filename)
43                         #print filename, idx
44                         if idx > 0:
45                                 profile.setTempOverride('fan_enabled', 'False')
46                                 profile.setTempOverride('skirt_line_count', '0')
47                                 profile.setTempOverride('machine_center_x', profile.getProfileSettingFloat('machine_center_x') - profile.getPreferenceFloat('extruder_offset_x%d' % (idx)))
48                                 profile.setTempOverride('machine_center_y', profile.getProfileSettingFloat('machine_center_y') - profile.getPreferenceFloat('extruder_offset_y%d' % (idx)))
49                                 profile.setTempOverride('alternative_center', self.filelist[0])
50                         if len(self.filelist) > 1:
51                                 profile.setTempOverride('add_start_end_gcode', 'False')
52                                 profile.setTempOverride('gcode_extension', 'multi_extrude_tmp')
53                         cmdList.append(sliceRun.getSliceCommand(filename))
54                 profile.resetTempOverride()
55                 self.thread = WorkerThread(self, filelist, cmdList)
56         
57         def OnAbort(self, e):
58                 if self.abort:
59                         self.mainWindow.removeSliceProgress(self)
60                 else:
61                         self.abort = True
62         
63         def OnShowGCode(self, e):
64                 self.mainWindow.preview3d.loadModelFiles(self.filelist)
65                 self.mainWindow.preview3d.setViewMode("GCode")
66         
67         def OnShowLog(self, e):
68                 LogWindow('\n'.join(self.progressLog))
69         
70         def OnOpenFileLocation(self, e):
71                 exporer.openExporer(sliceRun.getExportFilename(self.filelist[0]))
72         
73         def OnCopyToSD(self, e):
74                 exportFilename = sliceRun.getExportFilename(self.filelist[0])
75                 filename = os.path.basename(exportFilename)
76                 if profile.getPreference('sdshortnames') == 'True':
77                         filename = sliceRun.getShortFilename(filename)
78                 shutil.copy(exportFilename, os.path.join(profile.getPreference('sdpath'), filename))
79         
80         def OnSliceDone(self, result):
81                 self.progressGauge.Destroy()
82                 self.abortButton.Destroy()
83                 self.progressLog = result.progressLog
84                 self.logButton = wx.Button(self, -1, "Show Log")
85                 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
86                 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
87                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
88                 self.sizer.Add(self.logButton, 0)
89                 if result.returnCode == 0:
90                         status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
91                         status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
92                         cost = result.gcode.calculateCost()
93                         if cost != False:
94                                 status += " Cost: %s" % (cost)
95                         self.statusText.SetLabel(status)
96                         if exporer.hasExporer():
97                                 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
98                                 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
99                                 self.sizer.Add(self.openFileLocationButton, 0)
100                         if profile.getPreference('sdpath') != '':
101                                 self.copyToSDButton = wx.Button(self, -1, "To SDCard")
102                                 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
103                                 self.sizer.Add(self.copyToSDButton, 0)
104                         self.showButton = wx.Button(self, -1, "Show result")
105                         self.Bind(wx.EVT_BUTTON, self.OnShowGCode, self.showButton)
106                         self.sizer.Add(self.showButton, 0)
107                 else:
108                         self.statusText.SetLabel("Something went wrong during slicing!")
109                 self.sizer.Add(self.abortButton, 0)
110                 self.sizer.Layout()
111                 self.Layout()
112                 self.abort = True
113                 if self.mainWindow.preview3d.loadReModelFiles(self.filelist):
114                         self.mainWindow.preview3d.setViewMode("GCode")
115         
116         def SetProgress(self, stepName, layer, maxLayer):
117                 if self.prevStep != stepName:
118                         self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
119                         newTime = time.time()
120                         #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
121                         self.startTime = newTime
122                         self.prevStep = stepName
123                 
124                 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
125                 self.progressGauge.SetValue(int(progresValue))
126                 self.statusText.SetLabel(stepName + " [" + str(layer) + "/" + str(maxLayer) + "]")
127
128 class WorkerThread(threading.Thread):
129         def __init__(self, notifyWindow, filelist, cmdList):
130                 threading.Thread.__init__(self)
131                 self.filelist = filelist
132                 self.notifyWindow = notifyWindow
133                 self.cmdList = cmdList
134                 self.fileIdx = 0
135                 self.start()
136
137         def run(self):
138                 kwargs = {} 
139                 if subprocess.mswindows: 
140                         su = subprocess.STARTUPINFO() 
141                         su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
142                         su.wShowWindow = subprocess.SW_HIDE
143                         kwargs['startupinfo'] = su
144                 print self.cmdList[self.fileIdx]
145                 p = subprocess.Popen(self.cmdList[self.fileIdx], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
146                 line = p.stdout.readline()
147                 self.progressLog = []
148                 maxValue = 1
149                 while(len(line) > 0):
150                         line = line.rstrip()
151                         if line[0:9] == "Progress[" and line[-1:] == "]":
152                                 progress = line[9:-1].split(":")
153                                 if len(progress) > 2:
154                                         maxValue = int(progress[2])
155                                 wx.CallAfter(self.notifyWindow.SetProgress, progress[0], int(progress[1]), maxValue)
156                         else:
157                                 self.progressLog.append(line)
158                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, line)
159                         if self.notifyWindow.abort:
160                                 p.terminate()
161                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.")
162                                 return
163                         line = p.stdout.readline()
164                 self.returnCode = p.wait()
165                 self.fileIdx += 1
166                 if self.fileIdx == len(self.cmdList):
167                         if len(self.filelist) > 1:
168                                 self._stitchMultiExtruder()
169                         gcodeFilename = sliceRun.getExportFilename(self.filelist[0])
170                         gcodefile = open(gcodeFilename, "a")
171                         for logLine in self.progressLog:
172                                 if logLine.startswith('Model error('):
173                                         gcodefile.write(';%s\n' % (logLine))
174                         gcodefile.close()
175                         self.gcode = gcodeInterpreter.gcode()
176                         self.gcode.load(gcodeFilename)
177                         profile.replaceGCodeTags(gcodeFilename, self.gcode)
178                         wx.CallAfter(self.notifyWindow.OnSliceDone, self)
179                 else:
180                         self.run()
181         
182         def _stitchMultiExtruder(self):
183                 files = []
184                 resultFile = open(sliceRun.getExportFilename(self.filelist[0]), "w")
185                 resultFile.write(';TYPE:CUSTOM\n')
186                 resultFile.write(profile.getAlterationFileContents('start.gcode'))
187                 for filename in self.filelist:
188                         if os.path.isfile(sliceRun.getExportFilename(filename, 'multi_extrude_tmp')):
189                                 files.append(open(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'), "r"))
190                         else:
191                                 return
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 'Z' in line:
206                                                 lastZ = float(re.search('Z([^\s]+)', line).group(1))
207                                         if not layerHasLine:
208                                                 nextExtruder = files.index(f)
209                                                 resultFile.write(';LAYER:%d\n' % (layerNr))
210                                                 resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
211                                                 if nextExtruder != currentExtruder:
212                                                         resultFile.write(';TYPE:CUSTOM\n')
213                                                         profile.setTempOverride('extruder', nextExtruder)
214                                                         resultFile.write(profile.getAlterationFileContents('switchExtruder.gcode'))
215                                                         profile.resetTempOverride()
216                                                         currentExtruder = nextExtruder
217                                                 layerHasLine = True
218                                         resultFile.write(line)
219                         layerNr += 1
220                 for f in files:
221                         f.close()
222                 for filename in self.filelist:
223                         os.remove(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'))
224                 resultFile.write(';TYPE:CUSTOM\n')
225                 resultFile.write(profile.getAlterationFileContents('end.gcode'))
226                 resultFile.close()
227
228 class LogWindow(wx.Frame):
229         def __init__(self, logText):
230                 super(LogWindow, self).__init__(None, title="Slice log")
231                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
232                 self.SetSize((400,300))
233                 self.Centre()
234                 self.Show(True)
235