chiark / gitweb /
52411cf4ae298b27bcb15e936a48be5f29c17436
[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, platform
5
6 from gui import taskbar
7 from gui import preferencesDialog
8 from util import profile
9 from util import sliceRun
10 from util import exporer
11 from util import gcodeInterpreter
12
13 class sliceProgessPanel(wx.Panel):
14         def __init__(self, mainWindow, parent, filelist):
15                 wx.Panel.__init__(self, parent, -1)
16                 self.mainWindow = mainWindow
17                 self.filelist = filelist
18                 self.abort = False
19                 
20                 box = wx.StaticBox(self, -1, filelist[0])
21                 self.sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
22
23                 mainSizer = wx.BoxSizer(wx.VERTICAL) 
24                 mainSizer.Add(self.sizer, 0, flag=wx.EXPAND)
25
26                 self.statusText = wx.StaticText(self, -1, "Starting...")
27                 self.progressGauge = wx.Gauge(self, -1)
28                 self.progressGauge.SetRange(10000 * len(filelist))
29                 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
30                 self.sizer.Add(self.statusText, 2, flag=wx.ALIGN_CENTER )
31                 self.sizer.Add(self.progressGauge, 2)
32                 self.sizer.Add(self.abortButton, 0)
33
34                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
35
36                 self.SetSizer(mainSizer)
37                 self.prevStep = 'start'
38                 self.totalDoneFactor = 0.0
39                 self.startTime = time.time()
40                 if profile.getPreference('save_profile') == 'True':
41                         profile.saveGlobalProfile(self.filelist[0][: self.filelist[0].rfind('.')] + "_profile.ini")
42                 cmdList = []
43                 for filename in self.filelist:
44                         idx = self.filelist.index(filename)
45                         #print filename, idx
46                         if idx > 0:
47                                 profile.setTempOverride('fan_enabled', 'False')
48                                 profile.setTempOverride('skirt_line_count', '0')
49                                 profile.setTempOverride('object_center_x', profile.getPreferenceFloat('machine_width') / 2 - profile.getPreferenceFloat('extruder_offset_x%d' % (idx)))
50                                 profile.setTempOverride('object_center_y', profile.getPreferenceFloat('machine_depth') / 2 - profile.getPreferenceFloat('extruder_offset_y%d' % (idx)))
51                                 profile.setTempOverride('alternative_center', self.filelist[0])
52                         if len(self.filelist) > 1:
53                                 profile.setTempOverride('add_start_end_gcode', 'False')
54                                 profile.setTempOverride('gcode_extension', 'multi_extrude_tmp')
55                         cmdList.append(sliceRun.getSliceCommand(filename))
56                 profile.resetTempOverride()
57                 self.thread = WorkerThread(self, filelist, cmdList)
58         
59         def OnAbort(self, e):
60                 if self.abort:
61                         self.mainWindow.removeSliceProgress(self)
62                 else:
63                         self.abort = True
64         
65         def OnShowGCode(self, e):
66                 self.mainWindow.preview3d.loadModelFiles(self.filelist)
67                 self.mainWindow.preview3d.setViewMode("GCode")
68         
69         def OnShowLog(self, e):
70                 LogWindow('\n'.join(self.progressLog))
71         
72         def OnOpenFileLocation(self, e):
73                 exporer.openExporer(sliceRun.getExportFilename(self.filelist[0]))
74         
75         def OnCopyToSD(self, e):
76                 if profile.getPreference('sdpath') == '':
77                         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)
78                         prefDialog = preferencesDialog.preferencesDialog(self.GetParent())
79                         prefDialog.Centre()
80                         prefDialog.Show(True)
81                         if profile.getPreference('sdpath') == '':
82                                 print "No path set"
83                                 return
84                 exportFilename = sliceRun.getExportFilename(self.filelist[0])
85                 filename = os.path.basename(exportFilename)
86                 if profile.getPreference('sdshortnames') == 'True':
87                         filename = sliceRun.getShortFilename(filename)
88                 try:
89                         shutil.copy(exportFilename, os.path.join(profile.getPreference('sdpath'), filename))
90                 except:
91                         self.GetParent().preview3d.ShowWarningPopup("Failed to copy file to SD card.")
92                         return
93                 self.GetParent().preview3d.ShowWarningPopup("Copy finished, safely remove SD card?", self.OnSafeRemove)
94         
95         def OnSafeRemove(self):
96                 if platform.system() == "Windows":
97                         cmd = "%s %s>NUL" % (os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), profile.getPreference('sdpath'))
98                 elif platform.system() == "Darwin":
99                         cmd = "diskutil eject '%s' > /dev/null 2>&1" % (profile.getPreference('sdpath'))
100                 else:
101                         cmd = "umount '%s' > /dev/null 2>&1" % (profile.getPreference('sdpath'))
102                 if os.system(cmd):
103                         self.GetParent().preview3d.ShowWarningPopup("Safe remove failed.")
104                 else:
105                         self.GetParent().preview3d.ShowWarningPopup("You can now eject the card.")
106
107         def OnSliceDone(self, result):
108                 self.progressGauge.Destroy()
109                 self.abortButton.Destroy()
110                 self.progressLog = result.progressLog
111                 self.logButton = wx.Button(self, -1, "Show Log")
112                 self.abortButton = wx.Button(self, -1, "X", style=wx.BU_EXACTFIT)
113                 self.Bind(wx.EVT_BUTTON, self.OnShowLog, self.logButton)
114                 self.Bind(wx.EVT_BUTTON, self.OnAbort, self.abortButton)
115                 self.sizer.Add(self.logButton, 0)
116                 if result.returnCode == 0:
117                         status = "Ready: Filament: %.2fm %.2fg" % (result.gcode.extrusionAmount / 1000, result.gcode.calculateWeight() * 1000)
118                         status += " Print time: %02d:%02d" % (int(result.gcode.totalMoveTimeMinute / 60), int(result.gcode.totalMoveTimeMinute % 60))
119                         cost = result.gcode.calculateCost()
120                         if cost != False:
121                                 status += " Cost: %s" % (cost)
122                         self.statusText.SetLabel(status)
123                         if exporer.hasExporer():
124                                 self.openFileLocationButton = wx.Button(self, -1, "Open file location")
125                                 self.Bind(wx.EVT_BUTTON, self.OnOpenFileLocation, self.openFileLocationButton)
126                                 self.sizer.Add(self.openFileLocationButton, 0)
127                         if len(profile.getSDcardDrives()) > 0:
128                                 self.copyToSDButton = wx.Button(self, -1, "Copy to SDCard")
129                                 self.Bind(wx.EVT_BUTTON, self.OnCopyToSD, self.copyToSDButton)
130                                 self.sizer.Add(self.copyToSDButton, 0)
131                         self.showButton = wx.Button(self, -1, "Show result")
132                         self.Bind(wx.EVT_BUTTON, self.OnShowGCode, self.showButton)
133                         self.sizer.Add(self.showButton, 0)
134                 else:
135                         self.statusText.SetLabel("Something went wrong during slicing!")
136                 self.sizer.Add(self.abortButton, 0)
137                 self.sizer.Layout()
138                 self.Layout()
139                 self.abort = True
140                 if self.mainWindow.preview3d.loadReModelFiles(self.filelist):
141                         self.mainWindow.preview3d.setViewMode("GCode")
142                 taskbar.setBusy(self.GetParent(), False)
143         
144         def SetProgress(self, stepName, layer, maxLayer):
145                 if self.prevStep != stepName:
146                         self.totalDoneFactor += sliceRun.sliceStepTimeFactor[self.prevStep]
147                         newTime = time.time()
148                         #print "#####" + str(newTime-self.startTime) + " " + self.prevStep + " -> " + stepName
149                         self.startTime = newTime
150                         self.prevStep = stepName
151                 
152                 progresValue = ((self.totalDoneFactor + sliceRun.sliceStepTimeFactor[stepName] * layer / maxLayer) / sliceRun.totalRunTimeFactor) * 10000
153                 self.progressGauge.SetValue(int(progresValue))
154                 taskbar.setProgress(self.GetParent(), int(progresValue), self.progressGauge.GetRange())
155                 self.statusText.SetLabel("Preparing: processing %s [%d/%d]" % (stepName, layer, maxLayer))
156
157 class WorkerThread(threading.Thread):
158         def __init__(self, notifyWindow, filelist, cmdList):
159                 threading.Thread.__init__(self)
160                 self.filelist = filelist
161                 self.notifyWindow = notifyWindow
162                 self.cmdList = cmdList
163                 self.fileIdx = 0
164                 self.start()
165
166         def run(self):
167                 p = sliceRun.startSliceCommandProcess(self.cmdList[self.fileIdx])
168                 line = p.stdout.readline()
169                 self.progressLog = []
170                 maxValue = 1
171                 while(len(line) > 0):
172                         line = line.rstrip()
173                         if line[0:9] == "Progress[" and line[-1:] == "]":
174                                 progress = line[9:-1].split(":")
175                                 if len(progress) > 2:
176                                         maxValue = int(progress[2])
177                                 wx.CallAfter(self.notifyWindow.SetProgress, progress[0], int(progress[1]), maxValue)
178                         else:
179                                 self.progressLog.append(line)
180                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, line)
181                         if self.notifyWindow.abort:
182                                 p.terminate()
183                                 wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Aborted by user.")
184                                 return
185                         line = p.stdout.readline()
186                 line = p.stderr.readline()
187                 while(len(line) > 0):
188                         line = line.rstrip()
189                         self.progressLog.append(line)
190                         line = p.stderr.readline()
191                 self.returnCode = p.wait()
192                 self.fileIdx += 1
193                 if self.fileIdx == len(self.cmdList):
194                         if len(self.filelist) > 1:
195                                 self._stitchMultiExtruder()
196                         gcodeFilename = sliceRun.getExportFilename(self.filelist[0])
197                         gcodefile = open(gcodeFilename, "a")
198                         for logLine in self.progressLog:
199                                 if logLine.startswith('Model error('):
200                                         gcodefile.write(';%s\n' % (logLine))
201                         gcodefile.close()
202                         wx.CallAfter(self.notifyWindow.statusText.SetLabel, "Running plugins")
203                         ret = profile.runPostProcessingPlugins(gcodeFilename)
204                         if ret != None:
205                                 self.progressLog.append(ret)
206                         self.gcode = gcodeInterpreter.gcode()
207                         self.gcode.load(gcodeFilename)
208                         profile.replaceGCodeTags(gcodeFilename, self.gcode)
209                         wx.CallAfter(self.notifyWindow.OnSliceDone, self)
210                 else:
211                         self.run()
212         
213         def _stitchMultiExtruder(self):
214                 files = []
215                 resultFile = open(sliceRun.getExportFilename(self.filelist[0]), "w")
216                 resultFile.write(';TYPE:CUSTOM\n')
217                 resultFile.write(profile.getAlterationFileContents('start.gcode'))
218                 for filename in self.filelist:
219                         if os.path.isfile(sliceRun.getExportFilename(filename, 'multi_extrude_tmp')):
220                                 files.append(open(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'), "r"))
221                         else:
222                                 return
223                 
224                 currentExtruder = 0
225                 resultFile.write('T%d\n' % (currentExtruder))
226                 layerNr = -1
227                 hasLine = True
228                 filesOrder = files[:]
229                 while hasLine:
230                         hasLine = False
231                         filesOrder.reverse()
232                         for f in filesOrder:
233                                 layerHasLine = False
234                                 for line in f:
235                                         hasLine = True
236                                         if line.startswith(';LAYER:'):
237                                                 break
238                                         if 'Z' in line:
239                                                 lastZ = float(re.search('Z([^\s]+)', line).group(1))
240                                         if not layerHasLine:
241                                                 nextExtruder = files.index(f)
242                                                 resultFile.write(';LAYER:%d\n' % (layerNr))
243                                                 resultFile.write(';EXTRUDER:%d\n' % (nextExtruder))
244                                                 if nextExtruder != currentExtruder:
245                                                         resultFile.write(';TYPE:CUSTOM\n')
246                                                         profile.setTempOverride('extruder', nextExtruder)
247                                                         resultFile.write(profile.getAlterationFileContents('switchExtruder.gcode') + '\n')
248                                                         profile.resetTempOverride()
249                                                         currentExtruder = nextExtruder
250                                                 layerHasLine = True
251                                         resultFile.write(line)
252                         layerNr += 1
253                 for f in files:
254                         f.close()
255                 for filename in self.filelist:
256                         os.remove(sliceRun.getExportFilename(filename, 'multi_extrude_tmp'))
257                 resultFile.write(';TYPE:CUSTOM\n')
258                 resultFile.write(profile.getAlterationFileContents('end.gcode'))
259                 resultFile.close()
260
261 class LogWindow(wx.Frame):
262         def __init__(self, logText):
263                 super(LogWindow, self).__init__(None, title="Slice log")
264                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
265                 self.SetSize((400,300))
266                 self.Centre()
267                 self.Show(True)
268