2 from __future__ import absolute_import
12 from wx.lib import buttons
14 from Cura.gui import webcam
15 from Cura.gui 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.SetIcon(icon.getMainIcon())
123 self.SetSizer(wx.BoxSizer())
124 self.panel = wx.Panel(self)
125 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
126 self.sizer = wx.GridBagSizer(2, 2)
127 self.panel.SetSizer(self.sizer)
129 sb = wx.StaticBox(self.panel, label="Statistics")
130 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
132 self.powerWarningText = wx.StaticText(parent=self.panel,
134 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
135 style=wx.ALIGN_CENTER)
136 self.powerWarningText.SetBackgroundColour('red')
137 self.powerWarningText.SetForegroundColour('white')
138 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
139 self.powerManagement = power.PowerManagement()
140 self.powerWarningTimer = wx.Timer(self)
141 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
142 self.OnPowerWarningChange(None)
143 self.powerWarningTimer.Start(10000)
145 self.statsText = wx.StaticText(self.panel, -1,
146 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
147 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
149 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
151 self.connectButton = wx.Button(self.panel, -1, 'Connect')
152 #self.loadButton = wx.Button(self.panel, -1, 'Load')
153 self.printButton = wx.Button(self.panel, -1, 'Print')
154 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
155 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
156 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
157 self.progress = wx.Gauge(self.panel, -1)
159 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
160 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
161 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
162 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
163 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
164 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
165 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
167 nb = wx.Notebook(self.panel)
168 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
170 self.temperaturePanel = wx.Panel(nb)
171 sizer = wx.GridBagSizer(2, 2)
172 self.temperaturePanel.SetSizer(sizer)
174 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
175 self.temperatureSelect.SetRange(0, 400)
176 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
177 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
178 style=wx.SP_ARROW_KEYS)
179 self.bedTemperatureSelect.SetRange(0, 400)
180 self.bedTemperatureLabel.Show(False)
181 self.bedTemperatureSelect.Show(False)
183 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
185 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
186 sizer.Add(self.temperatureSelect, pos=(0, 1))
187 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
188 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
189 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
190 sizer.AddGrowableRow(2)
191 sizer.AddGrowableCol(1)
193 nb.AddPage(self.temperaturePanel, 'Temp')
195 self.directControlPanel = wx.Panel(nb)
197 sizer = wx.GridBagSizer(2, 2)
198 self.directControlPanel.SetSizer(sizer)
199 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
200 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
201 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
205 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
207 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
209 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
211 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
213 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
214 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
215 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
217 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
218 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
219 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
221 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
223 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
224 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
225 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
227 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
228 span=(1, 3), flag=wx.EXPAND)
229 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
230 span=(1, 3), flag=wx.EXPAND)
232 nb.AddPage(self.directControlPanel, 'Jog')
234 self.speedPanel = wx.Panel(nb)
235 sizer = wx.GridBagSizer(2, 2)
236 self.speedPanel.SetSizer(sizer)
238 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
239 self.outerWallSpeedSelect.SetRange(5, 1000)
240 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
241 self.innerWallSpeedSelect.SetRange(5, 1000)
242 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
243 self.fillSpeedSelect.SetRange(5, 1000)
244 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
245 self.supportSpeedSelect.SetRange(5, 1000)
247 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
248 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
249 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
250 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
251 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
252 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
253 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
254 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
255 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
256 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
257 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
258 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
260 nb.AddPage(self.speedPanel, 'Speed')
262 self.termPanel = wx.Panel(nb)
263 sizer = wx.GridBagSizer(2, 2)
264 self.termPanel.SetSizer(sizer)
266 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
267 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
268 self.termLog.SetFont(f)
269 self.termLog.SetEditable(0)
270 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
271 self.termInput.SetFont(f)
273 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
274 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
275 sizer.AddGrowableCol(0)
276 sizer.AddGrowableRow(0)
278 nb.AddPage(self.termPanel, 'Term')
281 self.camPage = wx.Panel(nb)
282 sizer = wx.GridBagSizer(2, 2)
283 self.camPage.SetSizer(sizer)
285 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
286 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
288 pages = self.cam.propertyPages()
289 self.cam.buttons = [self.timelapsEnable]
291 button = wx.Button(self.camPage, -1, page)
292 button.index = pages.index(page)
293 sizer.Add(button, pos=(1, pages.index(page)))
294 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
295 self.cam.buttons.append(button)
297 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
298 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
300 self.camPreview = wx.Panel(self.camPage)
301 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
303 nb.AddPage(self.camPage, 'Camera')
304 self.camPreview.timer = wx.Timer(self)
305 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
306 self.camPreview.timer.Start(500)
307 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
309 self.sizer.AddGrowableRow(6)
310 self.sizer.AddGrowableCol(3)
312 self.Bind(wx.EVT_CLOSE, self.OnClose)
313 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
314 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
315 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
316 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
317 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
318 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
320 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
321 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
323 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
324 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
325 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
326 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
327 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
328 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
334 self.statsText.SetMinSize(self.statsText.GetSize())
336 self.UpdateButtonStates()
338 #self.UpdateProgress()
340 def OnCameraTimer(self, e):
341 if not self.campreviewEnable.GetValue():
343 if self.machineCom != None and self.machineCom.isPrinting():
345 self.cam.takeNewImage()
346 self.camPreview.Refresh()
348 def OnCameraEraseBackground(self, e):
351 dc = wx.ClientDC(self)
352 rect = self.GetUpdateRegion().GetBox()
353 dc.SetClippingRect(rect)
354 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
355 if self.cam.getLastImage() != None:
356 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
358 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
362 def OnPropertyPageButton(self, e):
363 self.cam.openPropertyPage(e.GetEventObject().index)
365 def UpdateButtonStates(self):
366 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
367 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
368 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
369 self.machineCom.isPrinting() or self.machineCom.isPaused()))
370 self.pauseButton.Enable(
371 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
372 if self.machineCom != None and self.machineCom.isPaused():
373 self.pauseButton.SetLabel('Resume')
375 self.pauseButton.SetLabel('Pause')
376 self.cancelButton.Enable(
377 self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
378 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
379 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
380 self.directControlPanel.Enable(
381 self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
382 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
384 for button in self.cam.buttons:
385 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
387 def UpdateProgress(self):
389 if self.gcode == None:
390 status += "Loading gcode...\n"
392 status += "Filament: %.2fm %.2fg\n" % (
393 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
394 cost = self.gcode.calculateCost()
396 status += "Filament cost: %s\n" % (cost)
397 status += "Estimated print time: %02d:%02d\n" % (
398 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
399 if self.machineCom == None or not self.machineCom.isPrinting():
400 self.progress.SetValue(0)
401 if self.gcodeList != None:
402 status += 'Line: -/%d\n' % (len(self.gcodeList))
404 printTime = self.machineCom.getPrintTime() / 60
405 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
406 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
407 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
408 if self.currentZ > 0:
409 status += 'Height: %0.1f\n' % (self.currentZ)
410 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
411 if printTimeLeft == None:
412 status += 'Print time left: Unknown\n'
414 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
415 self.progress.SetValue(self.machineCom.getPrintPos())
416 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
417 if self.machineCom != None:
418 if self.machineCom.getTemp() > 0:
419 status += 'Temp: %d\n' % (self.machineCom.getTemp())
420 if self.machineCom.getBedTemp() > 0:
421 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
422 self.bedTemperatureLabel.Show(True)
423 self.bedTemperatureSelect.Show(True)
424 self.temperaturePanel.Layout()
425 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
427 self.statsText.SetLabel(status.strip())
429 def OnConnect(self, e):
430 if self.machineCom != None:
431 self.machineCom.close()
432 self.machineCom = machineCom.MachineCom(callbackObject=self)
433 self.UpdateButtonStates()
434 taskbar.setBusy(self, True)
439 def OnPrint(self, e):
440 if self.machineCom == None or not self.machineCom.isOperational():
442 if self.gcodeList == None:
444 if self.machineCom.isPrinting():
447 if self.cam != None and self.timelapsEnable.GetValue():
448 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
449 self.machineCom.printGCode(self.gcodeList)
450 self.UpdateButtonStates()
452 def OnCancel(self, e):
453 self.pauseButton.SetLabel('Pause')
454 self.machineCom.cancelPrint()
455 self.machineCom.sendCommand("M84")
456 self.UpdateButtonStates()
458 def OnPause(self, e):
459 if self.machineCom.isPaused():
460 self.machineCom.setPause(False)
462 self.machineCom.setPause(True)
464 def OnMachineLog(self, e):
465 LogWindow('\n'.join(self.machineCom.getLog()))
467 def OnClose(self, e):
468 global printWindowHandle
469 printWindowHandle = None
470 if self.machineCom != None:
471 self.machineCom.close()
474 def OnTempChange(self, e):
475 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
477 def OnBedTempChange(self, e):
478 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
480 def OnSpeedChange(self, e):
481 if self.machineCom == None:
483 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
484 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
485 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
486 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
488 def AddTermLog(self, line):
489 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
490 l = len(self.termLog.GetValue())
491 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
493 def OnTermEnterLine(self, e):
494 line = self.termInput.GetValue()
497 self.termLog.AppendText('>%s\n' % (line))
498 self.machineCom.sendCommand(line)
499 self.termHistory.append(line)
500 self.termHistoryIdx = len(self.termHistory)
501 self.termInput.SetValue('')
503 def OnTermKey(self, e):
504 if len(self.termHistory) > 0:
505 if e.GetKeyCode() == wx.WXK_UP:
506 self.termHistoryIdx = self.termHistoryIdx - 1
507 if self.termHistoryIdx < 0:
508 self.termHistoryIdx = len(self.termHistory) - 1
509 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
510 if e.GetKeyCode() == wx.WXK_DOWN:
511 self.termHistoryIdx = self.termHistoryIdx - 1
512 if self.termHistoryIdx >= len(self.termHistory):
513 self.termHistoryIdx = 0
514 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
517 def OnPowerWarningChange(self, e):
518 type = self.powerManagement.get_providing_power_source_type()
519 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
520 self.powerWarningText.Hide()
523 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
524 self.powerWarningText.Show()
528 def LoadGCodeFile(self, filename):
529 if self.machineCom != None and self.machineCom.isPrinting():
531 #Send an initial M110 to reset the line counter to zero.
532 prevLineType = lineType = 'CUSTOM'
534 for line in open(filename, 'r'):
535 if line.startswith(';TYPE:'):
536 lineType = line[6:].strip()
538 line = line[0:line.find(';')]
541 if prevLineType != lineType:
542 gcodeList.append((line, lineType, ))
544 gcodeList.append(line)
545 prevLineType = lineType
546 gcode = gcodeInterpreter.gcode()
547 gcode.loadList(gcodeList)
548 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
549 self.filename = filename
551 self.gcodeList = gcodeList
553 wx.CallAfter(self.progress.SetRange, len(gcodeList))
554 wx.CallAfter(self.UpdateButtonStates)
555 wx.CallAfter(self.UpdateProgress)
556 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
558 def sendLine(self, lineNr):
559 if lineNr >= len(self.gcodeList):
561 line = self.gcodeList[lineNr]
563 if ('M104' in line or 'M109' in line) and 'S' in line:
564 n = int(re.search('S([0-9]*)', line).group(1))
565 wx.CallAfter(self.temperatureSelect.SetValue, n)
566 if ('M140' in line or 'M190' in line) and 'S' in line:
567 n = int(re.search('S([0-9]*)', line).group(1))
568 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
570 print "Unexpected error:", sys.exc_info()
571 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
572 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
575 def mcLog(self, message):
579 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
580 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
581 # ToFix, this causes problems with setting the temperature with the keyboard
582 # if self.temperatureSelect.GetValue() != targetTemp:
583 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
584 # if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
585 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
587 def mcStateChange(self, state):
588 if self.machineCom != None:
589 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
590 self.cam.endTimelaps()
591 if state == self.machineCom.STATE_OPERATIONAL:
592 taskbar.setBusy(self, False)
593 if self.machineCom.isClosedOrError():
594 taskbar.setBusy(self, False)
595 if self.machineCom.isPaused():
596 taskbar.setPause(self, True)
597 wx.CallAfter(self.UpdateButtonStates)
598 wx.CallAfter(self.UpdateProgress)
600 def mcMessage(self, message):
601 wx.CallAfter(self.AddTermLog, message)
603 def mcProgress(self, lineNr):
604 wx.CallAfter(self.UpdateProgress)
606 def mcZChange(self, newZ):
609 wx.CallAfter(self.cam.takeNewImage)
610 wx.CallAfter(self.camPreview.Refresh)
613 class temperatureGraph(wx.Panel):
614 def __init__(self, parent):
615 super(temperatureGraph, self).__init__(parent)
617 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
618 self.Bind(wx.EVT_SIZE, self.OnSize)
619 self.Bind(wx.EVT_PAINT, self.OnDraw)
621 self.lastDraw = time.time() - 1.0
623 self.backBuffer = None
624 self.addPoint(0, 0, 0, 0)
625 self.SetMinSize((320, 200))
627 def OnEraseBackground(self, e):
631 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
632 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
633 self.UpdateDrawing(True)
636 dc = wx.BufferedPaintDC(self, self.backBuffer)
638 def UpdateDrawing(self, force=False):
640 if not force and now - self.lastDraw < 1.0:
644 dc.SelectObject(self.backBuffer)
646 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
647 w, h = self.GetSizeTuple()
648 bgLinePen = wx.Pen('#A0A0A0')
649 tempPen = wx.Pen('#FF4040')
650 tempSPPen = wx.Pen('#FFA0A0')
651 tempPenBG = wx.Pen('#FFD0D0')
652 bedTempPen = wx.Pen('#4040FF')
653 bedTempSPPen = wx.Pen('#A0A0FF')
654 bedTempPenBG = wx.Pen('#D0D0FF')
656 #Draw the background up to the current temperatures.
662 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
663 x1 = int(w - (now - t))
664 for x in xrange(x0, x1 + 1):
665 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
666 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
668 dc.DrawLine(x, h, x, h - (t * h / 300))
669 dc.SetPen(bedTempPenBG)
670 dc.DrawLine(x, h, x, h - (bt * h / 300))
678 for x in xrange(w, 0, -30):
680 dc.DrawLine(x, 0, x, h)
682 for y in xrange(h - 1, 0, -h * 50 / 300):
684 dc.DrawLine(0, y, w, y)
685 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
687 dc.DrawLine(0, 0, w, 0)
688 dc.DrawLine(0, 0, 0, h)
696 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
697 x1 = int(w - (now - t))
698 for x in xrange(x0, x1 + 1):
699 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
700 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
701 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
702 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
704 dc.DrawPoint(x, h - (tSP * h / 300))
705 dc.SetPen(bedTempSPPen)
706 dc.DrawPoint(x, h - (btSP * h / 300))
708 dc.DrawPoint(x, h - (t * h / 300))
709 dc.SetPen(bedTempPen)
710 dc.DrawPoint(x, h - (bt * h / 300))
718 self.Refresh(eraseBackground=False)
721 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
724 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
727 if bedTempSP == None:
729 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
730 wx.CallAfter(self.UpdateDrawing)
733 class LogWindow(wx.Frame):
734 def __init__(self, logText):
735 super(LogWindow, self).__init__(None, title="Machine log")
736 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
737 self.SetSize((500, 400))