2 from __future__ import absolute_import
14 from wx.lib import buttons
16 from Cura.gui.util import webcam
17 from Cura.gui.util import taskbar
18 from Cura.util import machineCom
19 from Cura.util import gcodeInterpreter
20 from Cura.util.resources import getPathForImage
22 printWindowMonitorHandle = None
24 def printFile(filename):
25 global printWindowMonitorHandle
26 if printWindowMonitorHandle is None:
27 printWindowMonitorHandle = printProcessMonitor()
28 printWindowMonitorHandle.loadFile(filename)
31 def startPrintInterface(filename):
32 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
33 # 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).
35 printWindowHandle = printWindow()
36 printWindowHandle.Show(True)
37 printWindowHandle.Raise()
38 printWindowHandle.OnConnect(None)
39 t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
45 class printProcessMonitor():
49 def loadFile(self, filename):
50 if self.handle is None:
51 if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
52 cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
54 cmdList = [sys.executable, '-m', 'Cura.cura']
56 cmdList.append(filename)
57 if platform.system() == "Darwin":
58 if platform.machine() == 'i386':
59 cmdList.insert(0, 'arch')
60 cmdList.insert(1, '-i386')
61 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 stderr=subprocess.PIPE)
63 self.thread = threading.Thread(target=self.Monitor)
66 self.handle.stdin.write(filename + '\n')
70 line = p.stdout.readline()
73 line = p.stdout.readline()
79 class PrintCommandButton(buttons.GenBitmapButton):
80 def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
81 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
82 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
84 self.commandList = commandList
88 self.SetUseFocusIndicator(False)
90 self.Bind(wx.EVT_BUTTON, self.OnClick)
93 if self.parent.machineCom is None or self.parent.machineCom.isPrinting():
95 for cmd in self.commandList:
96 self.parent.machineCom.sendCommand(cmd)
100 class printWindow(wx.Frame):
101 "Main user interface window"
104 super(printWindow, self).__init__(None, -1, title='Printing')
105 self.machineCom = None
107 self.gcodeList = None
111 self.bufferLineCount = 4
113 self.feedrateRatioOuterWall = 1.0
114 self.feedrateRatioInnerWall = 1.0
115 self.feedrateRatioFill = 1.0
116 self.feedrateRatioSupport = 1.0
118 self.termHistory = []
119 self.termHistoryIdx = 0
122 if webcam.hasWebcamSupport():
123 self.cam = webcam.webcam()
124 if not self.cam.hasCamera():
127 self.SetSizer(wx.BoxSizer())
128 self.panel = wx.Panel(self)
129 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
130 self.sizer = wx.GridBagSizer(2, 2)
131 self.panel.SetSizer(self.sizer)
133 sb = wx.StaticBox(self.panel, label="Statistics")
134 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
136 self.powerWarningText = wx.StaticText(parent=self.panel,
138 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
139 style=wx.ALIGN_CENTER)
140 self.powerWarningText.SetBackgroundColour('red')
141 self.powerWarningText.SetForegroundColour('white')
142 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
143 self.powerManagement = power.PowerManagement()
144 self.powerWarningTimer = wx.Timer(self)
145 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
146 self.OnPowerWarningChange(None)
147 self.powerWarningTimer.Start(10000)
149 self.statsText = wx.StaticText(self.panel, -1,
150 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
151 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
153 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
155 self.connectButton = wx.Button(self.panel, -1, 'Connect')
156 #self.loadButton = wx.Button(self.panel, -1, 'Load')
157 self.printButton = wx.Button(self.panel, -1, 'Print')
158 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
159 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
160 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
161 self.progress = wx.Gauge(self.panel, -1)
163 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
164 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
165 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
166 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
167 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
168 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
169 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
171 nb = wx.Notebook(self.panel)
172 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
174 self.temperaturePanel = wx.Panel(nb)
175 sizer = wx.GridBagSizer(2, 2)
176 self.temperaturePanel.SetSizer(sizer)
178 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
179 self.temperatureSelect.SetRange(0, 400)
180 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
181 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
182 style=wx.SP_ARROW_KEYS)
183 self.bedTemperatureSelect.SetRange(0, 400)
184 self.bedTemperatureLabel.Show(False)
185 self.bedTemperatureSelect.Show(False)
187 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
189 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
190 sizer.Add(self.temperatureSelect, pos=(0, 1))
191 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
192 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
193 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
194 sizer.AddGrowableRow(2)
195 sizer.AddGrowableCol(1)
197 nb.AddPage(self.temperaturePanel, 'Temp')
199 self.directControlPanel = wx.Panel(nb)
201 sizer = wx.GridBagSizer(2, 2)
202 self.directControlPanel.SetSizer(sizer)
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
205 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
207 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
209 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
211 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
213 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
215 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
217 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
218 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
219 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
221 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
222 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
223 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
225 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
227 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
228 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
229 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
231 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
232 span=(1, 3), flag=wx.EXPAND)
233 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
234 span=(1, 3), flag=wx.EXPAND)
236 nb.AddPage(self.directControlPanel, 'Jog')
238 self.speedPanel = wx.Panel(nb)
239 sizer = wx.GridBagSizer(2, 2)
240 self.speedPanel.SetSizer(sizer)
242 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
243 self.outerWallSpeedSelect.SetRange(5, 1000)
244 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
245 self.innerWallSpeedSelect.SetRange(5, 1000)
246 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
247 self.fillSpeedSelect.SetRange(5, 1000)
248 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
249 self.supportSpeedSelect.SetRange(5, 1000)
251 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
252 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
253 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
254 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
255 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
256 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
257 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
258 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
259 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
260 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
261 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
262 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
264 nb.AddPage(self.speedPanel, 'Speed')
266 self.termPanel = wx.Panel(nb)
267 sizer = wx.GridBagSizer(2, 2)
268 self.termPanel.SetSizer(sizer)
270 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
271 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
272 self.termLog.SetFont(f)
273 self.termLog.SetEditable(0)
274 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
275 self.termInput.SetFont(f)
277 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
278 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
279 sizer.AddGrowableCol(0)
280 sizer.AddGrowableRow(0)
282 nb.AddPage(self.termPanel, 'Term')
285 self.camPage = wx.Panel(nb)
286 sizer = wx.GridBagSizer(2, 2)
287 self.camPage.SetSizer(sizer)
289 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelapse movie recording')
290 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
292 pages = self.cam.propertyPages()
293 self.cam.buttons = [self.timelapsEnable]
295 button = wx.Button(self.camPage, -1, page)
296 button.index = pages.index(page)
297 sizer.Add(button, pos=(1, pages.index(page)))
298 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
299 self.cam.buttons.append(button)
301 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
302 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
304 self.camPreview = wx.Panel(self.camPage)
305 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
307 nb.AddPage(self.camPage, 'Camera')
308 self.camPreview.timer = wx.Timer(self)
309 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
310 self.camPreview.timer.Start(500)
311 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
313 self.sizer.AddGrowableRow(6)
314 self.sizer.AddGrowableCol(3)
316 self.Bind(wx.EVT_CLOSE, self.OnClose)
317 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
318 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
319 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
320 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
321 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
322 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
324 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
325 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
327 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
328 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
329 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
330 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
331 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
332 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
338 self.statsText.SetMinSize(self.statsText.GetSize())
340 self.UpdateButtonStates()
342 #self.UpdateProgress()
344 def OnCameraTimer(self, e):
345 if not self.campreviewEnable.GetValue():
347 if self.machineCom != None and self.machineCom.isPrinting():
349 self.cam.takeNewImage()
350 self.camPreview.Refresh()
352 def OnCameraEraseBackground(self, e):
355 dc = wx.ClientDC(self)
356 rect = self.GetUpdateRegion().GetBox()
357 dc.SetClippingRect(rect)
358 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
359 if self.cam.getLastImage() != None:
360 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
362 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
366 def OnPropertyPageButton(self, e):
367 self.cam.openPropertyPage(e.GetEventObject().index)
369 def UpdateButtonStates(self):
370 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
371 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
372 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
373 self.machineCom.isPrinting() or self.machineCom.isPaused()))
374 self.pauseButton.Enable(
375 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
376 if self.machineCom != None and self.machineCom.isPaused():
377 self.pauseButton.SetLabel('Resume')
379 self.pauseButton.SetLabel('Pause')
380 self.cancelButton.Enable(
381 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
382 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
383 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
384 self.directControlPanel.Enable(
385 self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
386 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
388 for button in self.cam.buttons:
389 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
391 def UpdateProgress(self):
393 if self.gcode == None:
394 status += "Loading gcode...\n"
396 status += "Filament: %.2fm %.2fg\n" % (
397 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
398 cost = self.gcode.calculateCost()
400 status += "Filament cost: %s\n" % (cost)
401 status += "Estimated print time: %02d:%02d\n" % (
402 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
403 if self.machineCom == None or not self.machineCom.isPrinting():
404 self.progress.SetValue(0)
405 if self.gcodeList != None:
406 status += 'Line: -/%d\n' % (len(self.gcodeList))
408 printTime = self.machineCom.getPrintTime() / 60
409 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
410 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
411 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
412 if self.currentZ > 0:
413 status += 'Height: %0.1f\n' % (self.currentZ)
414 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
415 if printTimeLeft == None:
416 status += 'Print time left: Unknown\n'
418 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
419 self.progress.SetValue(self.machineCom.getPrintPos())
420 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
421 if self.machineCom != None:
422 if self.machineCom.getTemp() > 0:
423 status += 'Temp: %d\n' % (self.machineCom.getTemp())
424 if self.machineCom.getBedTemp() > 0:
425 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
426 self.bedTemperatureLabel.Show(True)
427 self.bedTemperatureSelect.Show(True)
428 self.temperaturePanel.Layout()
429 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
431 self.statsText.SetLabel(status.strip())
433 def OnConnect(self, e):
434 if self.machineCom != None:
435 self.machineCom.close()
436 self.machineCom = machineCom.MachineCom(callbackObject=self)
437 self.UpdateButtonStates()
438 taskbar.setBusy(self, True)
443 def OnPrint(self, e):
444 if self.machineCom == None or not self.machineCom.isOperational():
446 if self.gcodeList == None:
448 if self.machineCom.isPrinting():
451 if self.cam != None and self.timelapsEnable.GetValue():
452 self.cam.startTimelapse(self.filename[: self.filename.rfind('.')] + ".mpg")
453 self.machineCom.printGCode(self.gcodeList)
454 self.UpdateButtonStates()
456 def OnCancel(self, e):
457 self.pauseButton.SetLabel('Pause')
458 self.machineCom.cancelPrint()
459 self.machineCom.sendCommand("M84")
460 self.UpdateButtonStates()
462 def OnPause(self, e):
463 if self.machineCom.isPaused():
464 self.machineCom.setPause(False)
466 self.machineCom.setPause(True)
468 def OnMachineLog(self, e):
469 LogWindow('\n'.join(self.machineCom.getLog()))
471 def OnClose(self, e):
472 global printWindowHandle
473 printWindowHandle = None
474 if self.machineCom != None:
475 self.machineCom.close()
478 def OnTempChange(self, e):
479 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
481 def OnBedTempChange(self, e):
482 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
484 def OnSpeedChange(self, e):
485 if self.machineCom == None:
487 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
488 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
489 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
490 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
492 def AddTermLog(self, line):
493 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
494 l = len(self.termLog.GetValue())
495 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
497 def OnTermEnterLine(self, e):
498 line = self.termInput.GetValue()
501 self.termLog.AppendText('>%s\n' % (line))
502 self.machineCom.sendCommand(line)
503 self.termHistory.append(line)
504 self.termHistoryIdx = len(self.termHistory)
505 self.termInput.SetValue('')
507 def OnTermKey(self, e):
508 if len(self.termHistory) > 0:
509 if e.GetKeyCode() == wx.WXK_UP:
510 self.termHistoryIdx = self.termHistoryIdx - 1
511 if self.termHistoryIdx < 0:
512 self.termHistoryIdx = len(self.termHistory) - 1
513 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
514 if e.GetKeyCode() == wx.WXK_DOWN:
515 self.termHistoryIdx = self.termHistoryIdx - 1
516 if self.termHistoryIdx >= len(self.termHistory):
517 self.termHistoryIdx = 0
518 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
521 def OnPowerWarningChange(self, e):
522 type = self.powerManagement.get_providing_power_source_type()
523 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
524 self.powerWarningText.Hide()
527 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
528 self.powerWarningText.Show()
532 def LoadGCodeFile(self, filename):
533 if self.machineCom != None and self.machineCom.isPrinting():
535 #Send an initial M110 to reset the line counter to zero.
536 prevLineType = lineType = 'CUSTOM'
538 for line in open(filename, 'r'):
539 if line.startswith(';TYPE:'):
540 lineType = line[6:].strip()
542 line = line[0:line.find(';')]
545 if prevLineType != lineType:
546 gcodeList.append((line, lineType, ))
548 gcodeList.append(line)
549 prevLineType = lineType
550 gcode = gcodeInterpreter.gcode()
551 gcode.loadList(gcodeList)
552 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
553 self.filename = filename
555 self.gcodeList = gcodeList
557 wx.CallAfter(self.progress.SetRange, len(gcodeList))
558 wx.CallAfter(self.UpdateButtonStates)
559 wx.CallAfter(self.UpdateProgress)
560 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
562 def sendLine(self, lineNr):
563 if lineNr >= len(self.gcodeList):
565 line = self.gcodeList[lineNr]
567 if ('M104' in line or 'M109' in line) and 'S' in line:
568 n = int(re.search('S([0-9]*)', line).group(1))
569 wx.CallAfter(self.temperatureSelect.SetValue, n)
570 if ('M140' in line or 'M190' in line) and 'S' in line:
571 n = int(re.search('S([0-9]*)', line).group(1))
572 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
574 print "Unexpected error:", sys.exc_info()
575 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
576 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
579 def mcLog(self, message):
583 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
584 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
585 # ToFix, this causes problems with setting the temperature with the keyboard
586 # if self.temperatureSelect.GetValue() != targetTemp:
587 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
588 # if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
589 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
591 def mcStateChange(self, state):
592 if self.machineCom != None:
593 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
594 self.cam.endTimelapse()
595 if state == self.machineCom.STATE_OPERATIONAL:
596 taskbar.setBusy(self, False)
597 if self.machineCom.isClosedOrError():
598 taskbar.setBusy(self, False)
599 if self.machineCom.isPaused():
600 taskbar.setPause(self, True)
601 wx.CallAfter(self.UpdateButtonStates)
602 wx.CallAfter(self.UpdateProgress)
604 def mcMessage(self, message):
605 wx.CallAfter(self.AddTermLog, message)
607 def mcProgress(self, lineNr):
608 wx.CallAfter(self.UpdateProgress)
610 def mcZChange(self, newZ):
613 wx.CallAfter(self.cam.takeNewImage)
614 wx.CallAfter(self.camPreview.Refresh)
617 class temperatureGraph(wx.Panel):
618 def __init__(self, parent):
619 super(temperatureGraph, self).__init__(parent)
621 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
622 self.Bind(wx.EVT_SIZE, self.OnSize)
623 self.Bind(wx.EVT_PAINT, self.OnDraw)
625 self.lastDraw = time.time() - 1.0
627 self.backBuffer = None
628 self.addPoint(0, 0, 0, 0)
629 self.SetMinSize((320, 200))
631 def OnEraseBackground(self, e):
635 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
636 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
637 self.UpdateDrawing(True)
640 dc = wx.BufferedPaintDC(self, self.backBuffer)
642 def UpdateDrawing(self, force=False):
644 if not force and now - self.lastDraw < 1.0:
648 dc.SelectObject(self.backBuffer)
650 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
651 w, h = self.GetSizeTuple()
652 bgLinePen = wx.Pen('#A0A0A0')
653 tempPen = wx.Pen('#FF4040')
654 tempSPPen = wx.Pen('#FFA0A0')
655 tempPenBG = wx.Pen('#FFD0D0')
656 bedTempPen = wx.Pen('#4040FF')
657 bedTempSPPen = wx.Pen('#A0A0FF')
658 bedTempPenBG = wx.Pen('#D0D0FF')
660 #Draw the background up to the current temperatures.
666 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
667 x1 = int(w - (now - t))
668 for x in xrange(x0, x1 + 1):
669 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
670 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
672 dc.DrawLine(x, h, x, h - (t * h / 300))
673 dc.SetPen(bedTempPenBG)
674 dc.DrawLine(x, h, x, h - (bt * h / 300))
682 for x in xrange(w, 0, -30):
684 dc.DrawLine(x, 0, x, h)
686 for y in xrange(h - 1, 0, -h * 50 / 300):
688 dc.DrawLine(0, y, w, y)
689 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
691 dc.DrawLine(0, 0, w, 0)
692 dc.DrawLine(0, 0, 0, h)
700 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
701 x1 = int(w - (now - t))
702 for x in xrange(x0, x1 + 1):
703 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
704 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
705 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
706 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
708 dc.DrawPoint(x, h - (tSP * h / 300))
709 dc.SetPen(bedTempSPPen)
710 dc.DrawPoint(x, h - (btSP * h / 300))
712 dc.DrawPoint(x, h - (t * h / 300))
713 dc.SetPen(bedTempPen)
714 dc.DrawPoint(x, h - (bt * h / 300))
722 self.Refresh(eraseBackground=False)
725 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
728 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
731 if bedTempSP == None:
733 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
734 wx.CallAfter(self.UpdateDrawing)
737 class LogWindow(wx.Frame):
738 def __init__(self, logText):
739 super(LogWindow, self).__init__(None, title="Machine log")
740 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
741 self.SetSize((500, 400))