1 from __future__ import absolute_import
\r
4 import wx, threading, re, subprocess, sys, os, time, platform
\r
5 from wx.lib import buttons
\r
8 from gui import toolbarUtil
\r
9 from gui import webcam
\r
10 from gui import taskbar
\r
11 from util import machineCom
\r
12 from util import profile
\r
13 from util import gcodeInterpreter
\r
14 from util import power
\r
16 printWindowMonitorHandle = None
\r
18 def printFile(filename):
\r
19 global printWindowMonitorHandle
\r
20 if printWindowMonitorHandle == None:
\r
21 printWindowMonitorHandle = printProcessMonitor()
\r
22 printWindowMonitorHandle.loadFile(filename)
\r
24 def startPrintInterface(filename):
\r
25 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
\r
26 # It needs to run in a seperate process, as any running python code blocks the GCode sender pyton code (http://wiki.python.org/moin/GlobalInterpreterLock).
\r
28 printWindowHandle = printWindow()
\r
29 printWindowHandle.Show(True)
\r
30 printWindowHandle.Raise()
\r
31 printWindowHandle.OnConnect(None)
\r
32 t = threading.Thread(target=printWindowHandle.LoadGCodeFile,args=(filename,))
\r
37 class printProcessMonitor():
\r
41 def loadFile(self, filename):
\r
42 if self.handle == None:
43 cmdList = [sys.executable, sys.argv[0], '-r', filename]
44 if platform.system() == "Darwin":
45 if platform.machine() == 'i386':
46 cmdList.insert(0, 'arch')
47 cmdList.insert(1, '-i386')
48 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
49 self.thread = threading.Thread(target=self.Monitor)
\r
52 self.handle.stdin.write(filename + '\n')
\r
56 line = p.stdout.readline()
\r
57 while(len(line) > 0):
\r
58 #print line.rstrip()
\r
59 line = p.stdout.readline()
\r
64 class PrintCommandButton(buttons.GenBitmapButton):
\r
65 def __init__(self, parent, commandList, bitmapFilename, size=(20,20)):
\r
66 self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename)
\r
67 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
\r
69 self.commandList = commandList
\r
70 self.parent = parent
\r
72 self.SetBezelWidth(1)
\r
73 self.SetUseFocusIndicator(False)
\r
75 self.Bind(wx.EVT_BUTTON, self.OnClick)
\r
77 def OnClick(self, e):
\r
78 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
\r
80 for cmd in self.commandList:
\r
81 self.parent.machineCom.sendCommand(cmd)
\r
84 class printWindow(wx.Frame):
\r
85 "Main user interface window"
\r
87 super(printWindow, self).__init__(None, -1, title='Printing')
\r
88 self.machineCom = None
\r
90 self.gcodeList = None
\r
94 self.bufferLineCount = 4
\r
96 self.feedrateRatioOuterWall = 1.0
\r
97 self.feedrateRatioInnerWall = 1.0
\r
98 self.feedrateRatioFill = 1.0
\r
99 self.feedrateRatioSupport = 1.0
\r
101 self.termHistory = []
\r
102 self.termHistoryIdx = 0
\r
105 if webcam.hasWebcamSupport():
\r
106 self.cam = webcam.webcam()
\r
107 if not self.cam.hasCamera():
\r
110 #self.SetIcon(icon.getMainIcon())
\r
112 self.SetSizer(wx.BoxSizer())
\r
113 self.panel = wx.Panel(self)
\r
114 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
115 self.sizer = wx.GridBagSizer(2, 2)
\r
116 self.panel.SetSizer(self.sizer)
\r
118 sb = wx.StaticBox(self.panel, label="Statistics")
\r
119 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
121 self.powerWarningText = wx.StaticText(parent=self.panel,
\r
123 label="Connect your computer to AC power\nIf it shuts down during printing, the product will be lost.",
\r
124 style=wx.ALIGN_CENTER)
\r
125 self.powerWarningText.SetBackgroundColour('red')
\r
126 self.powerWarningText.SetForegroundColour('white')
\r
127 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
\r
128 self.powerManagement = power.PowerManagement()
\r
129 self.powerWarningTimer = wx.Timer(self)
\r
130 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
\r
131 self.OnPowerWarningChange(None)
\r
132 self.powerWarningTimer.Start(10000)
\r
134 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
135 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
137 self.sizer.Add(boxsizer, pos=(0,0), span=(7,1), flag=wx.EXPAND)
\r
139 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
140 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
141 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
142 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
143 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
144 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
145 self.progress = wx.Gauge(self.panel, -1)
\r
147 self.sizer.Add(self.connectButton, pos=(1,1), flag=wx.EXPAND)
\r
148 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
\r
149 self.sizer.Add(self.printButton, pos=(2,1), flag=wx.EXPAND)
\r
150 self.sizer.Add(self.pauseButton, pos=(3,1), flag=wx.EXPAND)
\r
151 self.sizer.Add(self.cancelButton, pos=(4,1), flag=wx.EXPAND)
\r
152 self.sizer.Add(self.machineLogButton, pos=(5,1), flag=wx.EXPAND)
\r
153 self.sizer.Add(self.progress, pos=(7,0), span=(1,7), flag=wx.EXPAND)
\r
155 nb = wx.Notebook(self.panel)
\r
156 self.sizer.Add(nb, pos=(0,2), span=(7,4), flag=wx.EXPAND)
\r
158 self.temperaturePanel = wx.Panel(nb)
\r
159 sizer = wx.GridBagSizer(2, 2)
\r
160 self.temperaturePanel.SetSizer(sizer)
\r
162 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
163 self.temperatureSelect.SetRange(0, 400)
\r
164 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
165 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
166 self.bedTemperatureSelect.SetRange(0, 400)
\r
167 self.bedTemperatureLabel.Show(False)
\r
168 self.bedTemperatureSelect.Show(False)
\r
170 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
172 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
173 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
174 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
175 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
176 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
177 sizer.AddGrowableRow(2)
\r
178 sizer.AddGrowableCol(1)
\r
180 nb.AddPage(self.temperaturePanel, 'Temp')
\r
182 self.directControlPanel = wx.Panel(nb)
\r
184 sizer = wx.GridBagSizer(2, 2)
\r
185 self.directControlPanel.SetSizer(sizer)
\r
186 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
187 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
191 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
192 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
194 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
195 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
196 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
198 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
200 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
201 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
205 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
206 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
208 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
210 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
211 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
214 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
215 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60,20)), pos=(2,10), span=(1,3), flag=wx.EXPAND)
\r
217 nb.AddPage(self.directControlPanel, 'Jog')
\r
219 self.speedPanel = wx.Panel(nb)
\r
220 sizer = wx.GridBagSizer(2, 2)
\r
221 self.speedPanel.SetSizer(sizer)
\r
223 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
224 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
225 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
226 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
227 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
228 self.fillSpeedSelect.SetRange(5, 1000)
\r
229 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
230 self.supportSpeedSelect.SetRange(5, 1000)
\r
232 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
233 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
234 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
235 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
236 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
237 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
238 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
239 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
240 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
241 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
242 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
243 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
245 nb.AddPage(self.speedPanel, 'Speed')
\r
247 self.termPanel = wx.Panel(nb)
\r
248 sizer = wx.GridBagSizer(2, 2)
\r
249 self.termPanel.SetSizer(sizer)
\r
251 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
252 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
253 self.termLog.SetFont(f)
\r
254 self.termLog.SetEditable(0)
\r
255 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
256 self.termInput.SetFont(f)
\r
258 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
259 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
260 sizer.AddGrowableCol(0)
\r
261 sizer.AddGrowableRow(0)
\r
263 nb.AddPage(self.termPanel, 'Term')
\r
265 if self.cam != None:
\r
266 self.camPage = wx.Panel(nb)
\r
267 sizer = wx.GridBagSizer(2, 2)
\r
268 self.camPage.SetSizer(sizer)
\r
270 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
271 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
273 pages = self.cam.propertyPages()
\r
274 self.cam.buttons = [self.timelapsEnable]
\r
276 button = wx.Button(self.camPage, -1, page)
\r
277 button.index = pages.index(page)
\r
278 sizer.Add(button, pos=(1, pages.index(page)))
\r
279 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
280 self.cam.buttons.append(button)
\r
282 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
283 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
285 self.camPreview = wx.Panel(self.camPage)
\r
286 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
288 nb.AddPage(self.camPage, 'Camera')
\r
289 self.camPreview.timer = wx.Timer(self)
\r
290 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
291 self.camPreview.timer.Start(500)
\r
292 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
294 self.sizer.AddGrowableRow(5)
\r
295 self.sizer.AddGrowableCol(3)
\r
297 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
298 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
299 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
300 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
301 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
302 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
303 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
305 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
306 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
308 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
309 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
310 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
311 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
312 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
313 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
319 self.statsText.SetMinSize(self.statsText.GetSize())
\r
321 self.UpdateButtonStates()
\r
322 #self.UpdateProgress()
\r
324 def OnCameraTimer(self, e):
\r
325 if not self.campreviewEnable.GetValue():
\r
327 if self.machineCom != None and self.machineCom.isPrinting():
\r
329 self.cam.takeNewImage()
\r
330 self.camPreview.Refresh()
\r
332 def OnCameraEraseBackground(self, e):
\r
335 dc = wx.ClientDC(self)
\r
336 rect = self.GetUpdateRegion().GetBox()
\r
337 dc.SetClippingRect(rect)
\r
338 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
339 if self.cam.getLastImage() != None:
\r
340 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
342 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
346 def OnPropertyPageButton(self, e):
\r
347 self.cam.openPropertyPage(e.GetEventObject().index)
\r
349 def UpdateButtonStates(self):
\r
350 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
351 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
352 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
353 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
354 if self.machineCom != None and self.machineCom.isPaused():
\r
355 self.pauseButton.SetLabel('Resume')
\r
357 self.pauseButton.SetLabel('Pause')
\r
358 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
359 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
360 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
361 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
362 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
\r
363 if self.cam != None:
\r
364 for button in self.cam.buttons:
\r
365 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
367 def UpdateProgress(self):
\r
369 if self.gcode == None:
\r
370 status += "Loading gcode...\n"
\r
372 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
373 cost = self.gcode.calculateCost()
\r
375 status += "Filament cost: %s\n" % (cost)
\r
376 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
377 if self.machineCom == None or not self.machineCom.isPrinting():
\r
378 self.progress.SetValue(0)
\r
379 if self.gcodeList != None:
\r
380 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
382 printTime = self.machineCom.getPrintTime() / 60
\r
383 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
384 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
385 if self.currentZ > 0:
\r
386 status += 'Height: %0.1f\n' % (self.currentZ)
\r
387 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
388 if printTimeLeft == None:
\r
389 status += 'Print time left: Unknown\n'
\r
391 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
392 self.progress.SetValue(self.machineCom.getPrintPos())
\r
393 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
394 if self.machineCom != None:
\r
395 if self.machineCom.getTemp() > 0:
\r
396 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
397 if self.machineCom.getBedTemp() > 0:
\r
398 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
399 self.bedTemperatureLabel.Show(True)
\r
400 self.bedTemperatureSelect.Show(True)
\r
401 self.temperaturePanel.Layout()
\r
402 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
404 self.statsText.SetLabel(status.strip())
\r
406 def OnConnect(self, e):
\r
407 if self.machineCom != None:
\r
408 self.machineCom.close()
\r
409 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
410 self.UpdateButtonStates()
\r
411 taskbar.setBusy(self, True)
\r
413 def OnLoad(self, e):
\r
416 def OnPrint(self, e):
\r
417 if self.machineCom == None or not self.machineCom.isOperational():
\r
419 if self.gcodeList == None:
\r
421 if self.machineCom.isPrinting():
\r
424 if self.cam != None and self.timelapsEnable.GetValue():
\r
425 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
426 self.machineCom.printGCode(self.gcodeList)
\r
427 self.UpdateButtonStates()
\r
429 def OnCancel(self, e):
\r
430 self.pauseButton.SetLabel('Pause')
\r
431 self.machineCom.cancelPrint()
\r
432 self.machineCom.sendCommand("M84")
\r
433 self.UpdateButtonStates()
\r
435 def OnPause(self, e):
\r
436 if self.machineCom.isPaused():
\r
437 self.machineCom.setPause(False)
\r
439 self.machineCom.setPause(True)
\r
441 def OnMachineLog(self, e):
\r
442 LogWindow('\n'.join(self.machineCom.getLog()))
\r
444 def OnClose(self, e):
\r
445 global printWindowHandle
\r
446 printWindowHandle = None
\r
447 if self.machineCom != None:
\r
448 self.machineCom.close()
\r
451 def OnTempChange(self, e):
\r
452 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
454 def OnBedTempChange(self, e):
\r
455 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
457 def OnSpeedChange(self, e):
\r
458 if self.machineCom == None:
\r
460 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
461 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
462 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
463 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
465 def AddTermLog(self, line):
\r
466 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
467 l = len(self.termLog.GetValue())
\r
468 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
470 def OnTermEnterLine(self, e):
\r
471 line = self.termInput.GetValue()
\r
474 self.termLog.AppendText('>%s\n' % (line))
\r
475 self.machineCom.sendCommand(line)
\r
476 self.termHistory.append(line)
\r
477 self.termHistoryIdx = len(self.termHistory)
\r
478 self.termInput.SetValue('')
\r
480 def OnTermKey(self, e):
\r
481 if len(self.termHistory) > 0:
\r
482 if e.GetKeyCode() == wx.WXK_UP:
\r
483 self.termHistoryIdx = self.termHistoryIdx - 1
\r
484 if self.termHistoryIdx < 0:
\r
485 self.termHistoryIdx = len(self.termHistory) - 1
\r
486 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
487 if e.GetKeyCode() == wx.WXK_DOWN:
\r
488 self.termHistoryIdx = self.termHistoryIdx - 1
\r
489 if self.termHistoryIdx >= len(self.termHistory):
\r
490 self.termHistoryIdx = 0
\r
491 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
494 def LoadGCodeFile(self, filename):
\r
495 if self.machineCom != None and self.machineCom.isPrinting():
\r
497 #Send an initial M110 to reset the line counter to zero.
\r
498 prevLineType = lineType = 'CUSTOM'
\r
499 gcodeList = ["M110"]
\r
500 for line in open(filename, 'r'):
\r
501 if line.startswith(';TYPE:'):
\r
502 lineType = line[6:].strip()
\r
504 line = line[0:line.find(';')]
\r
505 line = line.strip()
\r
507 if prevLineType != lineType:
\r
508 gcodeList.append((line, lineType, ))
\r
510 gcodeList.append(line)
\r
511 prevLineType = lineType
\r
512 gcode = gcodeInterpreter.gcode()
\r
513 gcode.loadList(gcodeList)
\r
514 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
515 self.filename = filename
\r
517 self.gcodeList = gcodeList
\r
519 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
520 wx.CallAfter(self.UpdateButtonStates)
\r
521 wx.CallAfter(self.UpdateProgress)
\r
522 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
524 def sendLine(self, lineNr):
\r
525 if lineNr >= len(self.gcodeList):
\r
527 line = self.gcodeList[lineNr]
\r
529 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
530 n = int(re.search('S([0-9]*)', line).group(1))
\r
531 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
532 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
533 n = int(re.search('S([0-9]*)', line).group(1))
\r
534 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
536 print "Unexpected error:", sys.exc_info()
\r
537 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
538 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
541 def mcLog(self, message):
\r
545 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
546 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
547 if self.temperatureSelect.GetValue() != targetTemp:
\r
548 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
549 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
550 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
552 def mcStateChange(self, state):
\r
553 if self.machineCom != None:
\r
554 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
555 self.cam.endTimelaps()
\r
556 if state == self.machineCom.STATE_OPERATIONAL:
\r
557 taskbar.setBusy(self, False)
\r
558 if self.machineCom.isClosedOrError():
\r
559 taskbar.setBusy(self, False)
\r
560 if self.machineCom.isPaused():
\r
561 taskbar.setPause(self, True)
\r
562 wx.CallAfter(self.UpdateButtonStates)
\r
563 wx.CallAfter(self.UpdateProgress)
\r
565 def mcMessage(self, message):
\r
566 wx.CallAfter(self.AddTermLog, message)
\r
568 def mcProgress(self, lineNr):
\r
569 wx.CallAfter(self.UpdateProgress)
\r
571 def mcZChange(self, newZ):
\r
572 self.currentZ = newZ
\r
573 if self.cam != None:
\r
574 wx.CallAfter(self.cam.takeNewImage)
\r
575 wx.CallAfter(self.camPreview.Refresh)
\r
577 class temperatureGraph(wx.Panel):
\r
578 def __init__(self, parent):
\r
579 super(temperatureGraph, self).__init__(parent)
\r
581 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
582 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
583 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
585 self.lastDraw = time.time() - 1.0
\r
587 self.backBuffer = None
\r
588 self.addPoint(0,0,0,0)
\r
589 self.SetMinSize((320,200))
\r
591 def OnEraseBackground(self, e):
\r
594 def OnSize(self, e):
\r
595 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
596 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
597 self.UpdateDrawing(True)
\r
599 def OnDraw(self, e):
\r
600 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
602 def UpdateDrawing(self, force = False):
\r
604 if not force and now - self.lastDraw < 1.0:
\r
606 self.lastDraw = now
\r
608 dc.SelectObject(self.backBuffer)
\r
610 w, h = self.GetSizeTuple()
\r
611 bgLinePen = wx.Pen('#A0A0A0')
\r
612 tempPen = wx.Pen('#FF4040')
\r
613 tempSPPen = wx.Pen('#FFA0A0')
\r
614 tempPenBG = wx.Pen('#FFD0D0')
\r
615 bedTempPen = wx.Pen('#4040FF')
\r
616 bedTempSPPen = wx.Pen('#A0A0FF')
\r
617 bedTempPenBG = wx.Pen('#D0D0FF')
\r
619 #Draw the background up to the current temperatures.
\r
625 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
626 x1 = int(w - (now - t))
\r
627 for x in xrange(x0, x1 + 1):
\r
628 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
629 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
630 dc.SetPen(tempPenBG)
\r
631 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
632 dc.SetPen(bedTempPenBG)
\r
633 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
641 for x in xrange(w, 0, -30):
\r
642 dc.SetPen(bgLinePen)
\r
643 dc.DrawLine(x, 0, x, h)
\r
644 for y in xrange(h-1, 0, -h * 50 / 300):
\r
645 dc.SetPen(bgLinePen)
\r
646 dc.DrawLine(0, y, w, y)
\r
647 dc.DrawLine(0, 0, w, 0)
\r
648 dc.DrawLine(0, 0, 0, h)
\r
650 #Draw the main lines
\r
656 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
657 x1 = int(w - (now - t))
\r
658 for x in xrange(x0, x1 + 1):
\r
659 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
660 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
661 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
662 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
663 dc.SetPen(tempSPPen)
\r
664 dc.DrawPoint(x, h - (tSP * h / 300))
\r
665 dc.SetPen(bedTempSPPen)
\r
666 dc.DrawPoint(x, h - (btSP * h / 300))
\r
668 dc.DrawPoint(x, h - (t * h / 300))
\r
669 dc.SetPen(bedTempPen)
\r
670 dc.DrawPoint(x, h - (bt * h / 300))
\r
678 self.Refresh(eraseBackground=False)
\r
681 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
684 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
685 if bedTemp == None:
\r
687 if bedTempSP == None:
\r
689 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
690 wx.CallAfter(self.UpdateDrawing)
\r
692 class LogWindow(wx.Frame):
\r
693 def __init__(self, logText):
\r
694 super(LogWindow, self).__init__(None, title="Machine log")
\r
695 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
696 self.SetSize((500,400))
\r