2 from __future__ import absolute_import
12 from wx.lib import buttons
15 from gui import toolbarUtil
16 from gui import webcam
17 from gui import taskbar
18 from util import machineCom
19 from util import profile
20 from util import gcodeInterpreter
21 from util import power
22 from util.resources import getPathForImage
24 printWindowMonitorHandle = None
26 def printFile(filename):
27 global printWindowMonitorHandle
28 if printWindowMonitorHandle == None:
29 printWindowMonitorHandle = printProcessMonitor()
30 printWindowMonitorHandle.loadFile(filename)
33 def startPrintInterface(filename):
34 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
35 # 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).
37 printWindowHandle = printWindow()
38 printWindowHandle.Show(True)
39 printWindowHandle.Raise()
40 printWindowHandle.OnConnect(None)
41 t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
47 class printProcessMonitor():
51 def loadFile(self, filename):
52 if self.handle == None:
53 cmdList = [sys.executable, sys.argv[0], '-r', filename]
54 if platform.system() == "Darwin":
55 if platform.machine() == 'i386':
56 cmdList.insert(0, 'arch')
57 cmdList.insert(1, '-i386')
58 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
59 stderr=subprocess.PIPE)
60 self.thread = threading.Thread(target=self.Monitor)
63 self.handle.stdin.write(filename + '\n')
67 line = p.stdout.readline()
70 line = p.stdout.readline()
76 class PrintCommandButton(buttons.GenBitmapButton):
77 def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
78 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
79 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
81 self.commandList = commandList
85 self.SetUseFocusIndicator(False)
87 self.Bind(wx.EVT_BUTTON, self.OnClick)
90 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
92 for cmd in self.commandList:
93 self.parent.machineCom.sendCommand(cmd)
97 class printWindow(wx.Frame):
98 "Main user interface window"
101 super(printWindow, self).__init__(None, -1, title='Printing')
102 self.machineCom = None
104 self.gcodeList = None
108 self.bufferLineCount = 4
110 self.feedrateRatioOuterWall = 1.0
111 self.feedrateRatioInnerWall = 1.0
112 self.feedrateRatioFill = 1.0
113 self.feedrateRatioSupport = 1.0
115 self.termHistory = []
116 self.termHistoryIdx = 0
119 if webcam.hasWebcamSupport():
120 self.cam = webcam.webcam()
121 if not self.cam.hasCamera():
124 #self.SetIcon(icon.getMainIcon())
126 self.SetSizer(wx.BoxSizer())
127 self.panel = wx.Panel(self)
128 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
129 self.sizer = wx.GridBagSizer(2, 2)
130 self.panel.SetSizer(self.sizer)
132 sb = wx.StaticBox(self.panel, label="Statistics")
133 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
135 self.powerWarningText = wx.StaticText(parent=self.panel,
137 label="Connect your computer to AC power\nIf it shuts down during printing, the product will be lost.",
138 style=wx.ALIGN_CENTER)
139 self.powerWarningText.SetBackgroundColour('red')
140 self.powerWarningText.SetForegroundColour('white')
141 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
142 self.powerManagement = power.PowerManagement()
143 self.powerWarningTimer = wx.Timer(self)
144 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
145 self.OnPowerWarningChange(None)
146 self.powerWarningTimer.Start(10000)
148 self.statsText = wx.StaticText(self.panel, -1,
149 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
150 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
152 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
154 self.connectButton = wx.Button(self.panel, -1, 'Connect')
155 #self.loadButton = wx.Button(self.panel, -1, 'Load')
156 self.printButton = wx.Button(self.panel, -1, 'Print')
157 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
158 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
159 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
160 self.progress = wx.Gauge(self.panel, -1)
162 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
163 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
164 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
165 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
166 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
167 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
168 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
170 nb = wx.Notebook(self.panel)
171 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
173 self.temperaturePanel = wx.Panel(nb)
174 sizer = wx.GridBagSizer(2, 2)
175 self.temperaturePanel.SetSizer(sizer)
177 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
178 self.temperatureSelect.SetRange(0, 400)
179 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
180 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
181 style=wx.SP_ARROW_KEYS)
182 self.bedTemperatureSelect.SetRange(0, 400)
183 self.bedTemperatureLabel.Show(False)
184 self.bedTemperatureSelect.Show(False)
186 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
188 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
189 sizer.Add(self.temperatureSelect, pos=(0, 1))
190 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
191 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
192 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
193 sizer.AddGrowableRow(2)
194 sizer.AddGrowableCol(1)
196 nb.AddPage(self.temperaturePanel, 'Temp')
198 self.directControlPanel = wx.Panel(nb)
200 sizer = wx.GridBagSizer(2, 2)
201 self.directControlPanel.SetSizer(sizer)
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
206 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
207 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
210 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
211 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
214 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
216 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
217 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
218 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
220 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
221 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
222 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
224 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
226 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
227 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
228 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
230 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
231 span=(1, 3), flag=wx.EXPAND)
232 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
233 span=(1, 3), flag=wx.EXPAND)
235 nb.AddPage(self.directControlPanel, 'Jog')
237 self.speedPanel = wx.Panel(nb)
238 sizer = wx.GridBagSizer(2, 2)
239 self.speedPanel.SetSizer(sizer)
241 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
242 self.outerWallSpeedSelect.SetRange(5, 1000)
243 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
244 self.innerWallSpeedSelect.SetRange(5, 1000)
245 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
246 self.fillSpeedSelect.SetRange(5, 1000)
247 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
248 self.supportSpeedSelect.SetRange(5, 1000)
250 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
251 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
252 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
253 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
254 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
255 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
256 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
257 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
258 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
259 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
260 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
261 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
263 nb.AddPage(self.speedPanel, 'Speed')
265 self.termPanel = wx.Panel(nb)
266 sizer = wx.GridBagSizer(2, 2)
267 self.termPanel.SetSizer(sizer)
269 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
270 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
271 self.termLog.SetFont(f)
272 self.termLog.SetEditable(0)
273 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
274 self.termInput.SetFont(f)
276 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
277 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
278 sizer.AddGrowableCol(0)
279 sizer.AddGrowableRow(0)
281 nb.AddPage(self.termPanel, 'Term')
284 self.camPage = wx.Panel(nb)
285 sizer = wx.GridBagSizer(2, 2)
286 self.camPage.SetSizer(sizer)
288 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
289 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
291 pages = self.cam.propertyPages()
292 self.cam.buttons = [self.timelapsEnable]
294 button = wx.Button(self.camPage, -1, page)
295 button.index = pages.index(page)
296 sizer.Add(button, pos=(1, pages.index(page)))
297 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
298 self.cam.buttons.append(button)
300 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
301 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
303 self.camPreview = wx.Panel(self.camPage)
304 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
306 nb.AddPage(self.camPage, 'Camera')
307 self.camPreview.timer = wx.Timer(self)
308 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
309 self.camPreview.timer.Start(500)
310 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
312 self.sizer.AddGrowableRow(5)
313 self.sizer.AddGrowableCol(3)
315 self.Bind(wx.EVT_CLOSE, self.OnClose)
316 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
317 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
318 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
319 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
320 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
321 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
323 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
324 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
326 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
327 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
328 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
329 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
330 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
331 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
337 self.statsText.SetMinSize(self.statsText.GetSize())
339 self.UpdateButtonStates()
341 #self.UpdateProgress()
343 def OnCameraTimer(self, e):
344 if not self.campreviewEnable.GetValue():
346 if self.machineCom != None and self.machineCom.isPrinting():
348 self.cam.takeNewImage()
349 self.camPreview.Refresh()
351 def OnCameraEraseBackground(self, e):
354 dc = wx.ClientDC(self)
355 rect = self.GetUpdateRegion().GetBox()
356 dc.SetClippingRect(rect)
357 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
358 if self.cam.getLastImage() != None:
359 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
361 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
365 def OnPropertyPageButton(self, e):
366 self.cam.openPropertyPage(e.GetEventObject().index)
368 def UpdateButtonStates(self):
369 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
370 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
371 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
372 self.machineCom.isPrinting() or self.machineCom.isPaused()))
373 self.pauseButton.Enable(
374 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
375 if self.machineCom != None and self.machineCom.isPaused():
376 self.pauseButton.SetLabel('Resume')
378 self.pauseButton.SetLabel('Pause')
379 self.cancelButton.Enable(
380 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
381 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
382 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
383 self.directControlPanel.Enable(
384 self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
385 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
387 for button in self.cam.buttons:
388 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
390 def UpdateProgress(self):
392 if self.gcode == None:
393 status += "Loading gcode...\n"
395 status += "Filament: %.2fm %.2fg\n" % (
396 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
397 cost = self.gcode.calculateCost()
399 status += "Filament cost: %s\n" % (cost)
400 status += "Estimated print time: %02d:%02d\n" % (
401 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
402 if self.machineCom == None or not self.machineCom.isPrinting():
403 self.progress.SetValue(0)
404 if self.gcodeList != None:
405 status += 'Line: -/%d\n' % (len(self.gcodeList))
407 printTime = self.machineCom.getPrintTime() / 60
408 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
409 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
410 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
411 if self.currentZ > 0:
412 status += 'Height: %0.1f\n' % (self.currentZ)
413 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
414 if printTimeLeft == None:
415 status += 'Print time left: Unknown\n'
417 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
418 self.progress.SetValue(self.machineCom.getPrintPos())
419 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
420 if self.machineCom != None:
421 if self.machineCom.getTemp() > 0:
422 status += 'Temp: %d\n' % (self.machineCom.getTemp())
423 if self.machineCom.getBedTemp() > 0:
424 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
425 self.bedTemperatureLabel.Show(True)
426 self.bedTemperatureSelect.Show(True)
427 self.temperaturePanel.Layout()
428 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
430 self.statsText.SetLabel(status.strip())
432 def OnConnect(self, e):
433 if self.machineCom != None:
434 self.machineCom.close()
435 self.machineCom = machineCom.MachineCom(callbackObject=self)
436 self.UpdateButtonStates()
437 taskbar.setBusy(self, True)
442 def OnPrint(self, e):
443 if self.machineCom == None or not self.machineCom.isOperational():
445 if self.gcodeList == None:
447 if self.machineCom.isPrinting():
450 if self.cam != None and self.timelapsEnable.GetValue():
451 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
452 self.machineCom.printGCode(self.gcodeList)
453 self.UpdateButtonStates()
455 def OnCancel(self, e):
456 self.pauseButton.SetLabel('Pause')
457 self.machineCom.cancelPrint()
458 self.machineCom.sendCommand("M84")
459 self.UpdateButtonStates()
461 def OnPause(self, e):
462 if self.machineCom.isPaused():
463 self.machineCom.setPause(False)
465 self.machineCom.setPause(True)
467 def OnMachineLog(self, e):
468 LogWindow('\n'.join(self.machineCom.getLog()))
470 def OnClose(self, e):
471 global printWindowHandle
472 printWindowHandle = None
473 if self.machineCom != None:
474 self.machineCom.close()
477 def OnTempChange(self, e):
478 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
480 def OnBedTempChange(self, e):
481 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
483 def OnSpeedChange(self, e):
484 if self.machineCom == None:
486 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
487 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
488 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
489 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
491 def AddTermLog(self, line):
492 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
493 l = len(self.termLog.GetValue())
494 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
496 def OnTermEnterLine(self, e):
497 line = self.termInput.GetValue()
500 self.termLog.AppendText('>%s\n' % (line))
501 self.machineCom.sendCommand(line)
502 self.termHistory.append(line)
503 self.termHistoryIdx = len(self.termHistory)
504 self.termInput.SetValue('')
506 def OnTermKey(self, e):
507 if len(self.termHistory) > 0:
508 if e.GetKeyCode() == wx.WXK_UP:
509 self.termHistoryIdx = self.termHistoryIdx - 1
510 if self.termHistoryIdx < 0:
511 self.termHistoryIdx = len(self.termHistory) - 1
512 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
513 if e.GetKeyCode() == wx.WXK_DOWN:
514 self.termHistoryIdx = self.termHistoryIdx - 1
515 if self.termHistoryIdx >= len(self.termHistory):
516 self.termHistoryIdx = 0
517 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
520 def OnPowerWarningChange(self, e):
521 type = self.powerManagement.get_providing_power_source_type()
522 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
523 self.powerWarningText.Hide()
525 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
526 self.powerWarningText.Show()
529 def LoadGCodeFile(self, filename):
530 if self.machineCom != None and self.machineCom.isPrinting():
532 #Send an initial M110 to reset the line counter to zero.
533 prevLineType = lineType = 'CUSTOM'
535 for line in open(filename, 'r'):
536 if line.startswith(';TYPE:'):
537 lineType = line[6:].strip()
539 line = line[0:line.find(';')]
542 if prevLineType != lineType:
543 gcodeList.append((line, lineType, ))
545 gcodeList.append(line)
546 prevLineType = lineType
547 gcode = gcodeInterpreter.gcode()
548 gcode.loadList(gcodeList)
549 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
550 self.filename = filename
552 self.gcodeList = gcodeList
554 wx.CallAfter(self.progress.SetRange, len(gcodeList))
555 wx.CallAfter(self.UpdateButtonStates)
556 wx.CallAfter(self.UpdateProgress)
557 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
559 def sendLine(self, lineNr):
560 if lineNr >= len(self.gcodeList):
562 line = self.gcodeList[lineNr]
564 if ('M104' in line or 'M109' in line) and 'S' in line:
565 n = int(re.search('S([0-9]*)', line).group(1))
566 wx.CallAfter(self.temperatureSelect.SetValue, n)
567 if ('M140' in line or 'M190' in line) and 'S' in line:
568 n = int(re.search('S([0-9]*)', line).group(1))
569 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
571 print "Unexpected error:", sys.exc_info()
572 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
573 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
576 def mcLog(self, message):
580 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
581 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
582 if self.temperatureSelect.GetValue() != targetTemp:
583 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
584 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
585 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
587 def mcStateChange(self, state):
588 if self.machineCom != None:
589 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
590 self.cam.endTimelaps()
591 if state == self.machineCom.STATE_OPERATIONAL:
592 taskbar.setBusy(self, False)
593 if self.machineCom.isClosedOrError():
594 taskbar.setBusy(self, False)
595 if self.machineCom.isPaused():
596 taskbar.setPause(self, True)
597 wx.CallAfter(self.UpdateButtonStates)
598 wx.CallAfter(self.UpdateProgress)
600 def mcMessage(self, message):
601 wx.CallAfter(self.AddTermLog, message)
603 def mcProgress(self, lineNr):
604 wx.CallAfter(self.UpdateProgress)
606 def mcZChange(self, newZ):
609 wx.CallAfter(self.cam.takeNewImage)
610 wx.CallAfter(self.camPreview.Refresh)
613 class temperatureGraph(wx.Panel):
614 def __init__(self, parent):
615 super(temperatureGraph, self).__init__(parent)
617 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
618 self.Bind(wx.EVT_SIZE, self.OnSize)
619 self.Bind(wx.EVT_PAINT, self.OnDraw)
621 self.lastDraw = time.time() - 1.0
623 self.backBuffer = None
624 self.addPoint(0, 0, 0, 0)
625 self.SetMinSize((320, 200))
627 def OnEraseBackground(self, e):
631 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
632 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
633 self.UpdateDrawing(True)
636 dc = wx.BufferedPaintDC(self, self.backBuffer)
638 def UpdateDrawing(self, force=False):
640 if not force and now - self.lastDraw < 1.0:
644 dc.SelectObject(self.backBuffer)
646 w, h = self.GetSizeTuple()
647 bgLinePen = wx.Pen('#A0A0A0')
648 tempPen = wx.Pen('#FF4040')
649 tempSPPen = wx.Pen('#FFA0A0')
650 tempPenBG = wx.Pen('#FFD0D0')
651 bedTempPen = wx.Pen('#4040FF')
652 bedTempSPPen = wx.Pen('#A0A0FF')
653 bedTempPenBG = wx.Pen('#D0D0FF')
655 #Draw the background up to the current temperatures.
661 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
662 x1 = int(w - (now - t))
663 for x in xrange(x0, x1 + 1):
664 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
665 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
667 dc.DrawLine(x, h, x, h - (t * h / 300))
668 dc.SetPen(bedTempPenBG)
669 dc.DrawLine(x, h, x, h - (bt * h / 300))
677 for x in xrange(w, 0, -30):
679 dc.DrawLine(x, 0, x, h)
680 for y in xrange(h - 1, 0, -h * 50 / 300):
682 dc.DrawLine(0, y, w, y)
683 dc.DrawLine(0, 0, w, 0)
684 dc.DrawLine(0, 0, 0, h)
692 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
693 x1 = int(w - (now - t))
694 for x in xrange(x0, x1 + 1):
695 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
696 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
697 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
698 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
700 dc.DrawPoint(x, h - (tSP * h / 300))
701 dc.SetPen(bedTempSPPen)
702 dc.DrawPoint(x, h - (btSP * h / 300))
704 dc.DrawPoint(x, h - (t * h / 300))
705 dc.SetPen(bedTempPen)
706 dc.DrawPoint(x, h - (bt * h / 300))
714 self.Refresh(eraseBackground=False)
717 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
720 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
723 if bedTempSP == None:
725 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
726 wx.CallAfter(self.UpdateDrawing)
729 class LogWindow(wx.Frame):
730 def __init__(self, logText):
731 super(LogWindow, self).__init__(None, title="Machine log")
732 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
733 self.SetSize((500, 400))