2 from __future__ import absolute_import
15 from wx.lib import buttons
17 from Cura.gui.util import webcam
18 from Cura.gui.util import taskbar
19 from Cura.util import machineCom
20 from Cura.util import gcodeInterpreter
21 from Cura.util.resources import getPathForImage
23 printWindowMonitorHandle = None
25 def printFile(filename):
26 global printWindowMonitorHandle
27 if printWindowMonitorHandle is None:
28 printWindowMonitorHandle = printProcessMonitor()
29 printWindowMonitorHandle.loadFile(filename)
32 def startPrintInterface(filename, queue):
33 #startPrintInterface is called from the main script when we want the printer interface to run in a separate process.
34 # It needs to run in a separate process, as any running python code blocks the GCode sender python code (http://wiki.python.org/moin/GlobalInterpreterLock).
36 printWindowHandle = printWindow(queue)
37 printWindowHandle.Show(True)
38 printWindowHandle.Raise()
39 printWindowHandle.OnConnect(None)
40 t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
45 class printProcessMonitor():
48 self.queue = multiprocessing.Queue()
50 def loadFile(self, filename):
51 if self.handle is None or not self.handle.is_alive():
52 if self.handle is not None:
54 self.handle = multiprocessing.Process(target=startPrintInterface, args=(filename, self.queue))
57 self.queue.put(filename)
59 class PrintCommandButton(buttons.GenBitmapButton):
60 def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
61 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
62 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
64 self.commandList = commandList
68 self.SetUseFocusIndicator(False)
70 self.Bind(wx.EVT_BUTTON, self.OnClick)
73 if self.parent.machineCom is None or self.parent.machineCom.isPrinting():
75 for cmd in self.commandList:
76 self.parent.machineCom.sendCommand(cmd)
80 class printWindow(wx.Frame):
81 "Main user interface window"
83 def __init__(self, queue):
84 super(printWindow, self).__init__(None, -1, title='Printing')
85 self.machineCom = None
91 self.bufferLineCount = 4
93 self.feedrateRatioOuterWall = 1.0
94 self.feedrateRatioInnerWall = 1.0
95 self.feedrateRatioFill = 1.0
96 self.feedrateRatioSupport = 1.0
99 self.termHistoryIdx = 0
100 self.filenameQueue = queue
103 if webcam.hasWebcamSupport():
104 self.cam = webcam.webcam()
105 if not self.cam.hasCamera():
108 self.SetSizer(wx.BoxSizer())
109 self.panel = wx.Panel(self)
110 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
111 self.sizer = wx.GridBagSizer(2, 2)
112 self.panel.SetSizer(self.sizer)
114 sb = wx.StaticBox(self.panel, label="Statistics")
115 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
117 self.powerWarningText = wx.StaticText(parent=self.panel,
119 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
120 style=wx.ALIGN_CENTER)
121 self.powerWarningText.SetBackgroundColour('red')
122 self.powerWarningText.SetForegroundColour('white')
123 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
124 self.powerManagement = power.PowerManagement()
125 self.powerWarningTimer = wx.Timer(self)
126 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
127 self.OnPowerWarningChange(None)
128 self.powerWarningTimer.Start(10000)
130 self.statsText = wx.StaticText(self.panel, -1,
131 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
132 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
134 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
136 self.connectButton = wx.Button(self.panel, -1, 'Connect')
137 #self.loadButton = wx.Button(self.panel, -1, 'Load')
138 self.printButton = wx.Button(self.panel, -1, 'Print')
139 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
140 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
141 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
142 self.progress = wx.Gauge(self.panel, -1)
144 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
145 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
146 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
147 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
148 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
149 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
150 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
152 nb = wx.Notebook(self.panel)
153 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
155 self.temperaturePanel = wx.Panel(nb)
156 sizer = wx.GridBagSizer(2, 2)
157 self.temperaturePanel.SetSizer(sizer)
159 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
160 self.temperatureSelect.SetRange(0, 400)
161 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
162 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
163 style=wx.SP_ARROW_KEYS)
164 self.bedTemperatureSelect.SetRange(0, 400)
165 self.bedTemperatureLabel.Show(False)
166 self.bedTemperatureSelect.Show(False)
168 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
170 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
171 sizer.Add(self.temperatureSelect, pos=(0, 1))
172 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
173 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
174 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
175 sizer.AddGrowableRow(2)
176 sizer.AddGrowableCol(1)
178 nb.AddPage(self.temperaturePanel, 'Temp')
180 self.directControlPanel = wx.Panel(nb)
182 sizer = wx.GridBagSizer(2, 2)
183 self.directControlPanel.SetSizer(sizer)
184 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
185 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
186 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
192 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
193 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
194 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
196 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
198 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
199 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
200 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
206 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
209 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
210 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
212 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
213 span=(1, 3), flag=wx.EXPAND)
214 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
215 span=(1, 3), flag=wx.EXPAND)
217 nb.AddPage(self.directControlPanel, 'Jog')
219 self.speedPanel = wx.Panel(nb)
220 sizer = wx.GridBagSizer(2, 2)
221 self.speedPanel.SetSizer(sizer)
223 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
224 self.outerWallSpeedSelect.SetRange(5, 1000)
225 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
226 self.innerWallSpeedSelect.SetRange(5, 1000)
227 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
228 self.fillSpeedSelect.SetRange(5, 1000)
229 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
230 self.supportSpeedSelect.SetRange(5, 1000)
232 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
233 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
234 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
235 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
236 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
237 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
238 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
239 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
240 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
241 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
242 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
243 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
245 nb.AddPage(self.speedPanel, 'Speed')
247 self.termPanel = wx.Panel(nb)
248 sizer = wx.GridBagSizer(2, 2)
249 self.termPanel.SetSizer(sizer)
251 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
252 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
253 self.termLog.SetFont(f)
254 self.termLog.SetEditable(0)
255 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
256 self.termInput.SetFont(f)
258 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
259 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
260 sizer.AddGrowableCol(0)
261 sizer.AddGrowableRow(0)
263 nb.AddPage(self.termPanel, 'Term')
265 if self.cam is not None:
266 self.camPage = wx.Panel(nb)
267 sizer = wx.GridBagSizer(2, 2)
268 self.camPage.SetSizer(sizer)
270 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelapse movie recording')
271 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
273 pages = self.cam.propertyPages()
274 self.cam.buttons = [self.timelapsEnable]
276 button = wx.Button(self.camPage, -1, page)
277 button.index = pages.index(page)
278 sizer.Add(button, pos=(1, pages.index(page)))
279 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
280 self.cam.buttons.append(button)
282 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
283 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
285 self.camPreview = wx.Panel(self.camPage)
286 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
288 nb.AddPage(self.camPage, 'Camera')
289 self.camPreview.timer = wx.Timer(self)
290 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
291 self.camPreview.timer.Start(500)
292 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
294 self.sizer.AddGrowableRow(6)
295 self.sizer.AddGrowableCol(3)
297 self.Bind(wx.EVT_CLOSE, self.OnClose)
298 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
299 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
300 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
301 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
302 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
303 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
305 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
306 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
308 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
309 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
310 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
311 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
312 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
313 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
319 self.statsText.SetMinSize(self.statsText.GetSize())
321 self.UpdateButtonStates()
323 t = threading.Thread(target=self._readQueue)
327 def _readQueue(self):
329 filename = self.filenameQueue.get()
330 while self.machineCom is not None and self.machineCom.isPrinting():
332 self.LoadGCodeFile(filename)
334 def OnCameraTimer(self, e):
335 if not self.campreviewEnable.GetValue():
337 if self.machineCom is not None and self.machineCom.isPrinting():
339 self.cam.takeNewImage()
340 self.camPreview.Refresh()
342 def OnCameraEraseBackground(self, e):
345 dc = wx.ClientDC(self)
346 rect = self.GetUpdateRegion().GetBox()
347 dc.SetClippingRect(rect)
348 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
349 if self.cam.getLastImage() is not None:
350 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
352 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
356 def OnPropertyPageButton(self, e):
357 self.cam.openPropertyPage(e.GetEventObject().index)
359 def UpdateButtonStates(self):
360 self.connectButton.Enable(self.machineCom is None or self.machineCom.isClosedOrError())
361 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
362 self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
363 self.machineCom.isPrinting() or self.machineCom.isPaused()))
364 self.pauseButton.Enable(
365 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
366 if self.machineCom != None and self.machineCom.isPaused():
367 self.pauseButton.SetLabel('Resume')
369 self.pauseButton.SetLabel('Pause')
370 self.cancelButton.Enable(
371 self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
372 self.temperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
373 self.bedTemperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
374 self.directControlPanel.Enable(
375 self.machineCom is not None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
376 self.machineLogButton.Show(self.machineCom is not None and self.machineCom.isClosedOrError())
378 for button in self.cam.buttons:
379 button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
381 def UpdateProgress(self):
383 if self.gcode == None:
384 status += "Loading gcode...\n"
386 status += "Filament: %.2fm %.2fg\n" % (
387 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
388 cost = self.gcode.calculateCost()
390 status += "Filament cost: %s\n" % (cost)
391 status += "Estimated print time: %02d:%02d\n" % (
392 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
393 if self.machineCom is None or not self.machineCom.isPrinting():
394 self.progress.SetValue(0)
395 if self.gcodeList is not None:
396 status += 'Line: -/%d\n' % (len(self.gcodeList))
398 printTime = self.machineCom.getPrintTime() / 60
399 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
400 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
401 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
402 if self.currentZ > 0:
403 status += 'Height: %0.1f\n' % (self.currentZ)
404 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
405 if printTimeLeft == None:
406 status += 'Print time left: Unknown\n'
408 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
409 self.progress.SetValue(self.machineCom.getPrintPos())
410 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
411 if self.machineCom != None:
412 if self.machineCom.getTemp() > 0:
413 status += 'Temp: %d\n' % (self.machineCom.getTemp())
414 if self.machineCom.getBedTemp() > 0:
415 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
416 self.bedTemperatureLabel.Show(True)
417 self.bedTemperatureSelect.Show(True)
418 self.temperaturePanel.Layout()
419 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
421 self.statsText.SetLabel(status.strip())
423 def OnConnect(self, e):
424 if self.machineCom is not None:
425 self.machineCom.close()
426 self.machineCom = machineCom.MachineCom(callbackObject=self)
427 self.UpdateButtonStates()
428 taskbar.setBusy(self, True)
433 def OnPrint(self, e):
434 if self.machineCom is None or not self.machineCom.isOperational():
436 if self.gcodeList is None:
438 if self.machineCom.isPrinting():
441 if self.cam is not None and self.timelapsEnable.GetValue():
442 self.cam.startTimelapse(self.filename[: self.filename.rfind('.')] + ".mpg")
443 self.machineCom.printGCode(self.gcodeList)
444 self.UpdateButtonStates()
446 def OnCancel(self, e):
447 self.pauseButton.SetLabel('Pause')
448 self.machineCom.cancelPrint()
449 self.machineCom.sendCommand("M84")
450 self.UpdateButtonStates()
452 def OnPause(self, e):
453 if self.machineCom.isPaused():
454 self.machineCom.setPause(False)
456 self.machineCom.setPause(True)
458 def OnMachineLog(self, e):
459 LogWindow('\n'.join(self.machineCom.getLog()))
461 def OnClose(self, e):
462 global printWindowHandle
463 printWindowHandle = None
464 if self.machineCom != None:
465 self.machineCom.close()
468 def OnTempChange(self, e):
469 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
471 def OnBedTempChange(self, e):
472 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
474 def OnSpeedChange(self, e):
475 if self.machineCom is None:
477 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
478 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
479 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
480 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
482 def AddTermLog(self, line):
483 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
484 l = len(self.termLog.GetValue())
485 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
487 def OnTermEnterLine(self, e):
488 line = self.termInput.GetValue()
491 self.termLog.AppendText('>%s\n' % (line))
492 self.machineCom.sendCommand(line)
493 self.termHistory.append(line)
494 self.termHistoryIdx = len(self.termHistory)
495 self.termInput.SetValue('')
497 def OnTermKey(self, e):
498 if len(self.termHistory) > 0:
499 if e.GetKeyCode() == wx.WXK_UP:
500 self.termHistoryIdx = self.termHistoryIdx - 1
501 if self.termHistoryIdx < 0:
502 self.termHistoryIdx = len(self.termHistory) - 1
503 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
504 if e.GetKeyCode() == wx.WXK_DOWN:
505 self.termHistoryIdx = self.termHistoryIdx - 1
506 if self.termHistoryIdx >= len(self.termHistory):
507 self.termHistoryIdx = 0
508 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
511 def OnPowerWarningChange(self, e):
512 type = self.powerManagement.get_providing_power_source_type()
513 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
514 self.powerWarningText.Hide()
517 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
518 self.powerWarningText.Show()
522 def LoadGCodeFile(self, filename):
523 if self.machineCom is not None and self.machineCom.isPrinting():
525 #Send an initial M110 to reset the line counter to zero.
526 prevLineType = lineType = 'CUSTOM'
528 for line in open(filename, 'r'):
529 if line.startswith(';TYPE:'):
530 lineType = line[6:].strip()
532 line = line[0:line.find(';')]
535 if prevLineType != lineType:
536 gcodeList.append((line, lineType, ))
538 gcodeList.append(line)
539 prevLineType = lineType
540 gcode = gcodeInterpreter.gcode()
541 gcode.loadList(gcodeList)
542 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
543 self.filename = filename
545 self.gcodeList = gcodeList
547 wx.CallAfter(self.progress.SetRange, len(gcodeList))
548 wx.CallAfter(self.UpdateButtonStates)
549 wx.CallAfter(self.UpdateProgress)
550 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
552 def sendLine(self, lineNr):
553 if lineNr >= len(self.gcodeList):
555 line = self.gcodeList[lineNr]
557 if ('M104' in line or 'M109' in line) and 'S' in line:
558 n = int(re.search('S([0-9]*)', line).group(1))
559 wx.CallAfter(self.temperatureSelect.SetValue, n)
560 if ('M140' in line or 'M190' in line) and 'S' in line:
561 n = int(re.search('S([0-9]*)', line).group(1))
562 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
564 print "Unexpected error:", sys.exc_info()
565 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
566 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
569 def mcLog(self, message):
573 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
574 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
575 # ToFix, this causes problems with setting the temperature with the keyboard
576 # if self.temperatureSelect.GetValue() != targetTemp:
577 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
578 # if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
579 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
581 def mcStateChange(self, state):
582 if self.machineCom is not None:
583 if state == self.machineCom.STATE_OPERATIONAL and self.cam is not None:
584 self.cam.endTimelapse()
585 if state == self.machineCom.STATE_OPERATIONAL:
586 taskbar.setBusy(self, False)
587 if self.machineCom.isClosedOrError():
588 taskbar.setBusy(self, False)
589 if self.machineCom.isPaused():
590 taskbar.setPause(self, True)
591 wx.CallAfter(self.UpdateButtonStates)
592 wx.CallAfter(self.UpdateProgress)
594 def mcMessage(self, message):
595 wx.CallAfter(self.AddTermLog, message)
597 def mcProgress(self, lineNr):
598 wx.CallAfter(self.UpdateProgress)
600 def mcZChange(self, newZ):
602 if self.cam is not None:
603 wx.CallAfter(self.cam.takeNewImage)
604 wx.CallAfter(self.camPreview.Refresh)
607 class temperatureGraph(wx.Panel):
608 def __init__(self, parent):
609 super(temperatureGraph, self).__init__(parent)
611 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
612 self.Bind(wx.EVT_SIZE, self.OnSize)
613 self.Bind(wx.EVT_PAINT, self.OnDraw)
615 self.lastDraw = time.time() - 1.0
617 self.backBuffer = None
618 self.addPoint(0, 0, 0, 0)
619 self.SetMinSize((320, 200))
621 def OnEraseBackground(self, e):
625 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
626 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
627 self.UpdateDrawing(True)
630 dc = wx.BufferedPaintDC(self, self.backBuffer)
632 def UpdateDrawing(self, force=False):
634 if not force and now - self.lastDraw < 1.0:
638 dc.SelectObject(self.backBuffer)
640 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
641 w, h = self.GetSizeTuple()
642 bgLinePen = wx.Pen('#A0A0A0')
643 tempPen = wx.Pen('#FF4040')
644 tempSPPen = wx.Pen('#FFA0A0')
645 tempPenBG = wx.Pen('#FFD0D0')
646 bedTempPen = wx.Pen('#4040FF')
647 bedTempSPPen = wx.Pen('#A0A0FF')
648 bedTempPenBG = wx.Pen('#D0D0FF')
650 #Draw the background up to the current temperatures.
656 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
657 x1 = int(w - (now - t))
658 for x in xrange(x0, x1 + 1):
659 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
660 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
662 dc.DrawLine(x, h, x, h - (t * h / 300))
663 dc.SetPen(bedTempPenBG)
664 dc.DrawLine(x, h, x, h - (bt * h / 300))
672 for x in xrange(w, 0, -30):
674 dc.DrawLine(x, 0, x, h)
676 for y in xrange(h - 1, 0, -h * 50 / 300):
678 dc.DrawLine(0, y, w, y)
679 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
681 dc.DrawLine(0, 0, w, 0)
682 dc.DrawLine(0, 0, 0, h)
690 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
691 x1 = int(w - (now - t))
692 for x in xrange(x0, x1 + 1):
693 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
694 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
695 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
696 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
698 dc.DrawPoint(x, h - (tSP * h / 300))
699 dc.SetPen(bedTempSPPen)
700 dc.DrawPoint(x, h - (btSP * h / 300))
702 dc.DrawPoint(x, h - (t * h / 300))
703 dc.SetPen(bedTempPen)
704 dc.DrawPoint(x, h - (bt * h / 300))
712 self.Refresh(eraseBackground=False)
715 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
718 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
721 if bedTempSP == None:
723 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
724 wx.CallAfter(self.UpdateDrawing)
727 class LogWindow(wx.Frame):
728 def __init__(self, logText):
729 super(LogWindow, self).__init__(None, title="Machine log")
730 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
731 self.SetSize((500, 400))