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):
330 filename = self.filenameQueue.get()
332 while self.machineCom is not None and self.machineCom.isPrinting():
334 self.LoadGCodeFile(filename)
336 def OnCameraTimer(self, e):
337 if not self.campreviewEnable.GetValue():
339 if self.machineCom is not None and self.machineCom.isPrinting():
341 self.cam.takeNewImage()
342 self.camPreview.Refresh()
344 def OnCameraEraseBackground(self, e):
347 dc = wx.ClientDC(self)
348 rect = self.GetUpdateRegion().GetBox()
349 dc.SetClippingRect(rect)
350 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
351 if self.cam.getLastImage() is not None:
352 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
354 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
358 def OnPropertyPageButton(self, e):
359 self.cam.openPropertyPage(e.GetEventObject().index)
361 def UpdateButtonStates(self):
362 self.connectButton.Enable(self.machineCom is None or self.machineCom.isClosedOrError())
363 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
364 self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
365 self.machineCom.isPrinting() or self.machineCom.isPaused()))
366 self.pauseButton.Enable(
367 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
368 if self.machineCom != None and self.machineCom.isPaused():
369 self.pauseButton.SetLabel('Resume')
371 self.pauseButton.SetLabel('Pause')
372 self.cancelButton.Enable(
373 self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
374 self.temperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
375 self.bedTemperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
376 self.directControlPanel.Enable(
377 self.machineCom is not None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
378 self.machineLogButton.Show(self.machineCom is not None and self.machineCom.isClosedOrError())
380 for button in self.cam.buttons:
381 button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
383 def UpdateProgress(self):
385 if self.gcode == None:
386 status += "Loading gcode...\n"
388 status += "Filament: %.2fm %.2fg\n" % (
389 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
390 cost = self.gcode.calculateCost()
392 status += "Filament cost: %s\n" % (cost)
393 status += "Estimated print time: %02d:%02d\n" % (
394 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
395 if self.machineCom is None or not self.machineCom.isPrinting():
396 self.progress.SetValue(0)
397 if self.gcodeList is not None:
398 status += 'Line: -/%d\n' % (len(self.gcodeList))
400 printTime = self.machineCom.getPrintTime() / 60
401 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
402 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
403 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
404 if self.currentZ > 0:
405 status += 'Height: %0.1f\n' % (self.currentZ)
406 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
407 if printTimeLeft == None:
408 status += 'Print time left: Unknown\n'
410 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
411 self.progress.SetValue(self.machineCom.getPrintPos())
412 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
413 if self.machineCom != None:
414 if self.machineCom.getTemp() > 0:
415 status += 'Temp: %d\n' % (self.machineCom.getTemp())
416 if self.machineCom.getBedTemp() > 0:
417 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
418 self.bedTemperatureLabel.Show(True)
419 self.bedTemperatureSelect.Show(True)
420 self.temperaturePanel.Layout()
421 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
423 self.statsText.SetLabel(status.strip())
425 def OnConnect(self, e):
426 if self.machineCom is not None:
427 self.machineCom.close()
428 self.machineCom = machineCom.MachineCom(callbackObject=self)
429 self.UpdateButtonStates()
430 taskbar.setBusy(self, True)
435 def OnPrint(self, e):
436 if self.machineCom is None or not self.machineCom.isOperational():
438 if self.gcodeList is None:
440 if self.machineCom.isPrinting():
443 if self.cam is not None and self.timelapsEnable.GetValue():
444 self.cam.startTimelapse(self.filename[: self.filename.rfind('.')] + ".mpg")
445 self.machineCom.printGCode(self.gcodeList)
446 self.UpdateButtonStates()
448 def OnCancel(self, e):
449 self.pauseButton.SetLabel('Pause')
450 self.machineCom.cancelPrint()
451 self.machineCom.sendCommand("M84")
452 self.UpdateButtonStates()
454 def OnPause(self, e):
455 if self.machineCom.isPaused():
456 self.machineCom.setPause(False)
458 self.machineCom.setPause(True)
460 def OnMachineLog(self, e):
461 LogWindow('\n'.join(self.machineCom.getLog()))
463 def OnClose(self, e):
464 global printWindowHandle
465 printWindowHandle = None
466 if self.machineCom != None:
467 self.machineCom.close()
470 def OnTempChange(self, e):
471 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
473 def OnBedTempChange(self, e):
474 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
476 def OnSpeedChange(self, e):
477 if self.machineCom is None:
479 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
480 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
481 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
482 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
484 def AddTermLog(self, line):
485 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
486 l = len(self.termLog.GetValue())
487 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
489 def OnTermEnterLine(self, e):
490 line = self.termInput.GetValue()
493 self.termLog.AppendText('>%s\n' % (line))
494 self.machineCom.sendCommand(line)
495 self.termHistory.append(line)
496 self.termHistoryIdx = len(self.termHistory)
497 self.termInput.SetValue('')
499 def OnTermKey(self, e):
500 if len(self.termHistory) > 0:
501 if e.GetKeyCode() == wx.WXK_UP:
502 self.termHistoryIdx = self.termHistoryIdx - 1
503 if self.termHistoryIdx < 0:
504 self.termHistoryIdx = len(self.termHistory) - 1
505 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
506 if e.GetKeyCode() == wx.WXK_DOWN:
507 self.termHistoryIdx = self.termHistoryIdx - 1
508 if self.termHistoryIdx >= len(self.termHistory):
509 self.termHistoryIdx = 0
510 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
513 def OnPowerWarningChange(self, e):
514 type = self.powerManagement.get_providing_power_source_type()
515 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
516 self.powerWarningText.Hide()
519 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
520 self.powerWarningText.Show()
524 def LoadGCodeFile(self, filename):
525 if self.machineCom is not None and self.machineCom.isPrinting():
527 #Send an initial M110 to reset the line counter to zero.
528 prevLineType = lineType = 'CUSTOM'
530 for line in open(filename, 'r'):
531 if line.startswith(';TYPE:'):
532 lineType = line[6:].strip()
534 line = line[0:line.find(';')]
537 if prevLineType != lineType:
538 gcodeList.append((line, lineType, ))
540 gcodeList.append(line)
541 prevLineType = lineType
542 gcode = gcodeInterpreter.gcode()
543 gcode.loadList(gcodeList)
544 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
545 self.filename = filename
547 self.gcodeList = gcodeList
549 wx.CallAfter(self.progress.SetRange, len(gcodeList))
550 wx.CallAfter(self.UpdateButtonStates)
551 wx.CallAfter(self.UpdateProgress)
552 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
554 def sendLine(self, lineNr):
555 if lineNr >= len(self.gcodeList):
557 line = self.gcodeList[lineNr]
559 if ('M104' in line or 'M109' in line) and 'S' in line:
560 n = int(re.search('S([0-9]*)', line).group(1))
561 wx.CallAfter(self.temperatureSelect.SetValue, n)
562 if ('M140' in line or 'M190' in line) and 'S' in line:
563 n = int(re.search('S([0-9]*)', line).group(1))
564 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
566 print "Unexpected error:", sys.exc_info()
567 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
568 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
571 def mcLog(self, message):
575 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
576 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
577 # ToFix, this causes problems with setting the temperature with the keyboard
578 # if self.temperatureSelect.GetValue() != targetTemp:
579 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
580 # if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
581 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
583 def mcStateChange(self, state):
584 if self.machineCom is not None:
585 if state == self.machineCom.STATE_OPERATIONAL and self.cam is not None:
586 self.cam.endTimelapse()
587 if state == self.machineCom.STATE_OPERATIONAL:
588 taskbar.setBusy(self, False)
589 if self.machineCom.isClosedOrError():
590 taskbar.setBusy(self, False)
591 if self.machineCom.isPaused():
592 taskbar.setPause(self, True)
593 wx.CallAfter(self.UpdateButtonStates)
594 wx.CallAfter(self.UpdateProgress)
596 def mcMessage(self, message):
597 wx.CallAfter(self.AddTermLog, message)
599 def mcProgress(self, lineNr):
600 wx.CallAfter(self.UpdateProgress)
602 def mcZChange(self, newZ):
604 if self.cam is not None:
605 wx.CallAfter(self.cam.takeNewImage)
606 wx.CallAfter(self.camPreview.Refresh)
609 class temperatureGraph(wx.Panel):
610 def __init__(self, parent):
611 super(temperatureGraph, self).__init__(parent)
613 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
614 self.Bind(wx.EVT_SIZE, self.OnSize)
615 self.Bind(wx.EVT_PAINT, self.OnDraw)
617 self.lastDraw = time.time() - 1.0
619 self.backBuffer = None
620 self.addPoint(0, 0, 0, 0)
621 self.SetMinSize((320, 200))
623 def OnEraseBackground(self, e):
627 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
628 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
629 self.UpdateDrawing(True)
632 dc = wx.BufferedPaintDC(self, self.backBuffer)
634 def UpdateDrawing(self, force=False):
636 if not force and now - self.lastDraw < 1.0:
640 dc.SelectObject(self.backBuffer)
642 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
643 w, h = self.GetSizeTuple()
644 bgLinePen = wx.Pen('#A0A0A0')
645 tempPen = wx.Pen('#FF4040')
646 tempSPPen = wx.Pen('#FFA0A0')
647 tempPenBG = wx.Pen('#FFD0D0')
648 bedTempPen = wx.Pen('#4040FF')
649 bedTempSPPen = wx.Pen('#A0A0FF')
650 bedTempPenBG = wx.Pen('#D0D0FF')
652 #Draw the background up to the current temperatures.
658 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
659 x1 = int(w - (now - t))
660 for x in xrange(x0, x1 + 1):
661 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
662 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
664 dc.DrawLine(x, h, x, h - (t * h / 300))
665 dc.SetPen(bedTempPenBG)
666 dc.DrawLine(x, h, x, h - (bt * h / 300))
674 for x in xrange(w, 0, -30):
676 dc.DrawLine(x, 0, x, h)
678 for y in xrange(h - 1, 0, -h * 50 / 300):
680 dc.DrawLine(0, y, w, y)
681 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
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))