2 from __future__ import absolute_import
12 from wx.lib import buttons
14 from Cura.gui.util import webcam
15 from Cura.gui.util import taskbar
16 from Cura.util import machineCom
17 from Cura.util import gcodeInterpreter
18 from Cura.util import power
19 from Cura.util.resources import getPathForImage
21 printWindowMonitorHandle = None
23 def printFile(filename):
24 global printWindowMonitorHandle
25 if printWindowMonitorHandle == None:
26 printWindowMonitorHandle = printProcessMonitor()
27 printWindowMonitorHandle.loadFile(filename)
30 def startPrintInterface(filename):
31 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
32 # 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).
34 printWindowHandle = printWindow()
35 printWindowHandle.Show(True)
36 printWindowHandle.Raise()
37 printWindowHandle.OnConnect(None)
38 t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
44 class printProcessMonitor():
48 def loadFile(self, filename):
49 if self.handle == None:
50 cmdList = [sys.executable, sys.argv[0], '-r', filename]
51 if platform.system() == "Darwin":
52 if platform.machine() == 'i386':
53 cmdList.insert(0, 'arch')
54 cmdList.insert(1, '-i386')
55 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
56 stderr=subprocess.PIPE)
57 self.thread = threading.Thread(target=self.Monitor)
60 self.handle.stdin.write(filename + '\n')
64 line = p.stdout.readline()
67 line = p.stdout.readline()
73 class PrintCommandButton(buttons.GenBitmapButton):
74 def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
75 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
76 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
78 self.commandList = commandList
82 self.SetUseFocusIndicator(False)
84 self.Bind(wx.EVT_BUTTON, self.OnClick)
87 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
89 for cmd in self.commandList:
90 self.parent.machineCom.sendCommand(cmd)
94 class printWindow(wx.Frame):
95 "Main user interface window"
98 super(printWindow, self).__init__(None, -1, title='Printing')
99 self.machineCom = None
101 self.gcodeList = None
105 self.bufferLineCount = 4
107 self.feedrateRatioOuterWall = 1.0
108 self.feedrateRatioInnerWall = 1.0
109 self.feedrateRatioFill = 1.0
110 self.feedrateRatioSupport = 1.0
112 self.termHistory = []
113 self.termHistoryIdx = 0
116 if webcam.hasWebcamSupport():
117 self.cam = webcam.webcam()
118 if not self.cam.hasCamera():
121 self.SetSizer(wx.BoxSizer())
122 self.panel = wx.Panel(self)
123 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
124 self.sizer = wx.GridBagSizer(2, 2)
125 self.panel.SetSizer(self.sizer)
127 sb = wx.StaticBox(self.panel, label="Statistics")
128 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
130 self.powerWarningText = wx.StaticText(parent=self.panel,
132 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
133 style=wx.ALIGN_CENTER)
134 self.powerWarningText.SetBackgroundColour('red')
135 self.powerWarningText.SetForegroundColour('white')
136 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
137 self.powerManagement = power.PowerManagement()
138 self.powerWarningTimer = wx.Timer(self)
139 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
140 self.OnPowerWarningChange(None)
141 self.powerWarningTimer.Start(10000)
143 self.statsText = wx.StaticText(self.panel, -1,
144 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
145 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
147 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
149 self.connectButton = wx.Button(self.panel, -1, 'Connect')
150 #self.loadButton = wx.Button(self.panel, -1, 'Load')
151 self.printButton = wx.Button(self.panel, -1, 'Print')
152 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
153 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
154 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
155 self.progress = wx.Gauge(self.panel, -1)
157 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
158 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
159 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
160 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
161 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
162 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
163 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
165 nb = wx.Notebook(self.panel)
166 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
168 self.temperaturePanel = wx.Panel(nb)
169 sizer = wx.GridBagSizer(2, 2)
170 self.temperaturePanel.SetSizer(sizer)
172 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
173 self.temperatureSelect.SetRange(0, 400)
174 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
175 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
176 style=wx.SP_ARROW_KEYS)
177 self.bedTemperatureSelect.SetRange(0, 400)
178 self.bedTemperatureLabel.Show(False)
179 self.bedTemperatureSelect.Show(False)
181 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
183 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
184 sizer.Add(self.temperatureSelect, pos=(0, 1))
185 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
186 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
187 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
188 sizer.AddGrowableRow(2)
189 sizer.AddGrowableCol(1)
191 nb.AddPage(self.temperaturePanel, 'Temp')
193 self.directControlPanel = wx.Panel(nb)
195 sizer = wx.GridBagSizer(2, 2)
196 self.directControlPanel.SetSizer(sizer)
197 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
198 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
199 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
201 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
205 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
206 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
207 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
209 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
211 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
213 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
215 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
216 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
217 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
219 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
221 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
222 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
223 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
225 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
226 span=(1, 3), flag=wx.EXPAND)
227 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
228 span=(1, 3), flag=wx.EXPAND)
230 nb.AddPage(self.directControlPanel, 'Jog')
232 self.speedPanel = wx.Panel(nb)
233 sizer = wx.GridBagSizer(2, 2)
234 self.speedPanel.SetSizer(sizer)
236 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
237 self.outerWallSpeedSelect.SetRange(5, 1000)
238 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
239 self.innerWallSpeedSelect.SetRange(5, 1000)
240 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
241 self.fillSpeedSelect.SetRange(5, 1000)
242 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
243 self.supportSpeedSelect.SetRange(5, 1000)
245 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
246 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
247 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
248 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
249 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
250 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
251 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
252 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
253 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
254 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
255 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
256 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
258 nb.AddPage(self.speedPanel, 'Speed')
260 self.termPanel = wx.Panel(nb)
261 sizer = wx.GridBagSizer(2, 2)
262 self.termPanel.SetSizer(sizer)
264 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
265 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
266 self.termLog.SetFont(f)
267 self.termLog.SetEditable(0)
268 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
269 self.termInput.SetFont(f)
271 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
272 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
273 sizer.AddGrowableCol(0)
274 sizer.AddGrowableRow(0)
276 nb.AddPage(self.termPanel, 'Term')
279 self.camPage = wx.Panel(nb)
280 sizer = wx.GridBagSizer(2, 2)
281 self.camPage.SetSizer(sizer)
283 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
284 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
286 pages = self.cam.propertyPages()
287 self.cam.buttons = [self.timelapsEnable]
289 button = wx.Button(self.camPage, -1, page)
290 button.index = pages.index(page)
291 sizer.Add(button, pos=(1, pages.index(page)))
292 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
293 self.cam.buttons.append(button)
295 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
296 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
298 self.camPreview = wx.Panel(self.camPage)
299 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
301 nb.AddPage(self.camPage, 'Camera')
302 self.camPreview.timer = wx.Timer(self)
303 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
304 self.camPreview.timer.Start(500)
305 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
307 self.sizer.AddGrowableRow(6)
308 self.sizer.AddGrowableCol(3)
310 self.Bind(wx.EVT_CLOSE, self.OnClose)
311 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
312 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
313 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
314 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
315 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
316 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
318 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
319 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
321 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
322 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
323 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
324 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
325 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
326 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
332 self.statsText.SetMinSize(self.statsText.GetSize())
334 self.UpdateButtonStates()
336 #self.UpdateProgress()
338 def OnCameraTimer(self, e):
339 if not self.campreviewEnable.GetValue():
341 if self.machineCom != None and self.machineCom.isPrinting():
343 self.cam.takeNewImage()
344 self.camPreview.Refresh()
346 def OnCameraEraseBackground(self, e):
349 dc = wx.ClientDC(self)
350 rect = self.GetUpdateRegion().GetBox()
351 dc.SetClippingRect(rect)
352 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
353 if self.cam.getLastImage() != None:
354 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
356 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
360 def OnPropertyPageButton(self, e):
361 self.cam.openPropertyPage(e.GetEventObject().index)
363 def UpdateButtonStates(self):
364 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
365 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
366 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
367 self.machineCom.isPrinting() or self.machineCom.isPaused()))
368 self.pauseButton.Enable(
369 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
370 if self.machineCom != None and self.machineCom.isPaused():
371 self.pauseButton.SetLabel('Resume')
373 self.pauseButton.SetLabel('Pause')
374 self.cancelButton.Enable(
375 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
376 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
377 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
378 self.directControlPanel.Enable(
379 self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
380 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
382 for button in self.cam.buttons:
383 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
385 def UpdateProgress(self):
387 if self.gcode == None:
388 status += "Loading gcode...\n"
390 status += "Filament: %.2fm %.2fg\n" % (
391 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
392 cost = self.gcode.calculateCost()
394 status += "Filament cost: %s\n" % (cost)
395 status += "Estimated print time: %02d:%02d\n" % (
396 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
397 if self.machineCom == None or not self.machineCom.isPrinting():
398 self.progress.SetValue(0)
399 if self.gcodeList != None:
400 status += 'Line: -/%d\n' % (len(self.gcodeList))
402 printTime = self.machineCom.getPrintTime() / 60
403 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
404 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
405 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
406 if self.currentZ > 0:
407 status += 'Height: %0.1f\n' % (self.currentZ)
408 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
409 if printTimeLeft == None:
410 status += 'Print time left: Unknown\n'
412 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
413 self.progress.SetValue(self.machineCom.getPrintPos())
414 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
415 if self.machineCom != None:
416 if self.machineCom.getTemp() > 0:
417 status += 'Temp: %d\n' % (self.machineCom.getTemp())
418 if self.machineCom.getBedTemp() > 0:
419 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
420 self.bedTemperatureLabel.Show(True)
421 self.bedTemperatureSelect.Show(True)
422 self.temperaturePanel.Layout()
423 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
425 self.statsText.SetLabel(status.strip())
427 def OnConnect(self, e):
428 if self.machineCom != None:
429 self.machineCom.close()
430 self.machineCom = machineCom.MachineCom(callbackObject=self)
431 self.UpdateButtonStates()
432 taskbar.setBusy(self, True)
437 def OnPrint(self, e):
438 if self.machineCom == None or not self.machineCom.isOperational():
440 if self.gcodeList == None:
442 if self.machineCom.isPrinting():
445 if self.cam != None and self.timelapsEnable.GetValue():
446 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
447 self.machineCom.printGCode(self.gcodeList)
448 self.UpdateButtonStates()
450 def OnCancel(self, e):
451 self.pauseButton.SetLabel('Pause')
452 self.machineCom.cancelPrint()
453 self.machineCom.sendCommand("M84")
454 self.UpdateButtonStates()
456 def OnPause(self, e):
457 if self.machineCom.isPaused():
458 self.machineCom.setPause(False)
460 self.machineCom.setPause(True)
462 def OnMachineLog(self, e):
463 LogWindow('\n'.join(self.machineCom.getLog()))
465 def OnClose(self, e):
466 global printWindowHandle
467 printWindowHandle = None
468 if self.machineCom != None:
469 self.machineCom.close()
472 def OnTempChange(self, e):
473 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
475 def OnBedTempChange(self, e):
476 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
478 def OnSpeedChange(self, e):
479 if self.machineCom == None:
481 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
482 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
483 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
484 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
486 def AddTermLog(self, line):
487 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
488 l = len(self.termLog.GetValue())
489 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
491 def OnTermEnterLine(self, e):
492 line = self.termInput.GetValue()
495 self.termLog.AppendText('>%s\n' % (line))
496 self.machineCom.sendCommand(line)
497 self.termHistory.append(line)
498 self.termHistoryIdx = len(self.termHistory)
499 self.termInput.SetValue('')
501 def OnTermKey(self, e):
502 if len(self.termHistory) > 0:
503 if e.GetKeyCode() == wx.WXK_UP:
504 self.termHistoryIdx = self.termHistoryIdx - 1
505 if self.termHistoryIdx < 0:
506 self.termHistoryIdx = len(self.termHistory) - 1
507 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
508 if e.GetKeyCode() == wx.WXK_DOWN:
509 self.termHistoryIdx = self.termHistoryIdx - 1
510 if self.termHistoryIdx >= len(self.termHistory):
511 self.termHistoryIdx = 0
512 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
515 def OnPowerWarningChange(self, e):
516 type = self.powerManagement.get_providing_power_source_type()
517 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
518 self.powerWarningText.Hide()
521 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
522 self.powerWarningText.Show()
526 def LoadGCodeFile(self, filename):
527 if self.machineCom != None and self.machineCom.isPrinting():
529 #Send an initial M110 to reset the line counter to zero.
530 prevLineType = lineType = 'CUSTOM'
532 for line in open(filename, 'r'):
533 if line.startswith(';TYPE:'):
534 lineType = line[6:].strip()
536 line = line[0:line.find(';')]
539 if prevLineType != lineType:
540 gcodeList.append((line, lineType, ))
542 gcodeList.append(line)
543 prevLineType = lineType
544 gcode = gcodeInterpreter.gcode()
545 gcode.loadList(gcodeList)
546 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
547 self.filename = filename
549 self.gcodeList = gcodeList
551 wx.CallAfter(self.progress.SetRange, len(gcodeList))
552 wx.CallAfter(self.UpdateButtonStates)
553 wx.CallAfter(self.UpdateProgress)
554 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
556 def sendLine(self, lineNr):
557 if lineNr >= len(self.gcodeList):
559 line = self.gcodeList[lineNr]
561 if ('M104' in line or 'M109' in line) and 'S' in line:
562 n = int(re.search('S([0-9]*)', line).group(1))
563 wx.CallAfter(self.temperatureSelect.SetValue, n)
564 if ('M140' in line or 'M190' in line) and 'S' in line:
565 n = int(re.search('S([0-9]*)', line).group(1))
566 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
568 print "Unexpected error:", sys.exc_info()
569 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
570 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
573 def mcLog(self, message):
577 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
578 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
579 # ToFix, this causes problems with setting the temperature with the keyboard
580 # if self.temperatureSelect.GetValue() != targetTemp:
581 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
582 # if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
583 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
585 def mcStateChange(self, state):
586 if self.machineCom != None:
587 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
588 self.cam.endTimelaps()
589 if state == self.machineCom.STATE_OPERATIONAL:
590 taskbar.setBusy(self, False)
591 if self.machineCom.isClosedOrError():
592 taskbar.setBusy(self, False)
593 if self.machineCom.isPaused():
594 taskbar.setPause(self, True)
595 wx.CallAfter(self.UpdateButtonStates)
596 wx.CallAfter(self.UpdateProgress)
598 def mcMessage(self, message):
599 wx.CallAfter(self.AddTermLog, message)
601 def mcProgress(self, lineNr):
602 wx.CallAfter(self.UpdateProgress)
604 def mcZChange(self, newZ):
607 wx.CallAfter(self.cam.takeNewImage)
608 wx.CallAfter(self.camPreview.Refresh)
611 class temperatureGraph(wx.Panel):
612 def __init__(self, parent):
613 super(temperatureGraph, self).__init__(parent)
615 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
616 self.Bind(wx.EVT_SIZE, self.OnSize)
617 self.Bind(wx.EVT_PAINT, self.OnDraw)
619 self.lastDraw = time.time() - 1.0
621 self.backBuffer = None
622 self.addPoint(0, 0, 0, 0)
623 self.SetMinSize((320, 200))
625 def OnEraseBackground(self, e):
629 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
630 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
631 self.UpdateDrawing(True)
634 dc = wx.BufferedPaintDC(self, self.backBuffer)
636 def UpdateDrawing(self, force=False):
638 if not force and now - self.lastDraw < 1.0:
642 dc.SelectObject(self.backBuffer)
644 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
645 w, h = self.GetSizeTuple()
646 bgLinePen = wx.Pen('#A0A0A0')
647 tempPen = wx.Pen('#FF4040')
648 tempSPPen = wx.Pen('#FFA0A0')
649 tempPenBG = wx.Pen('#FFD0D0')
650 bedTempPen = wx.Pen('#4040FF')
651 bedTempSPPen = wx.Pen('#A0A0FF')
652 bedTempPenBG = wx.Pen('#D0D0FF')
654 #Draw the background up to the current temperatures.
660 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
661 x1 = int(w - (now - t))
662 for x in xrange(x0, x1 + 1):
663 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
664 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
666 dc.DrawLine(x, h, x, h - (t * h / 300))
667 dc.SetPen(bedTempPenBG)
668 dc.DrawLine(x, h, x, h - (bt * h / 300))
676 for x in xrange(w, 0, -30):
678 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.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
685 dc.DrawLine(0, 0, w, 0)
686 dc.DrawLine(0, 0, 0, h)
694 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
695 x1 = int(w - (now - t))
696 for x in xrange(x0, x1 + 1):
697 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
698 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
699 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
700 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
702 dc.DrawPoint(x, h - (tSP * h / 300))
703 dc.SetPen(bedTempSPPen)
704 dc.DrawPoint(x, h - (btSP * h / 300))
706 dc.DrawPoint(x, h - (t * h / 300))
707 dc.SetPen(bedTempPen)
708 dc.DrawPoint(x, h - (bt * h / 300))
716 self.Refresh(eraseBackground=False)
719 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
722 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
725 if bedTempSP == None:
727 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
728 wx.CallAfter(self.UpdateDrawing)
731 class LogWindow(wx.Frame):
732 def __init__(self, logText):
733 super(LogWindow, self).__init__(None, title="Machine log")
734 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
735 self.SetSize((500, 400))