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,))
44 class printProcessMonitor():
48 def loadFile(self, filename):
49 if self.handle is None:
50 if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
51 cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
53 cmdList = [sys.executable, '-m', 'Cura.cura']
55 cmdList.append(filename)
56 if platform.system() == "Darwin":
57 if platform.machine() == 'i386':
58 cmdList.insert(0, 'arch')
59 cmdList.insert(1, '-i386')
60 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stderr=subprocess.PIPE)
62 self.thread = threading.Thread(target=self.Monitor)
65 self.handle.stdin.write(filename + '\n')
69 line = p.stdout.readline()
72 line = p.stdout.readline()
73 line = p.stderr.readline()
76 line = p.stderr.readline()
82 class PrintCommandButton(buttons.GenBitmapButton):
83 def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
84 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
85 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
87 self.commandList = commandList
91 self.SetUseFocusIndicator(False)
93 self.Bind(wx.EVT_BUTTON, self.OnClick)
96 if self.parent.machineCom is None or self.parent.machineCom.isPrinting():
98 for cmd in self.commandList:
99 self.parent.machineCom.sendCommand(cmd)
103 class printWindow(wx.Frame):
104 "Main user interface window"
107 super(printWindow, self).__init__(None, -1, title='Printing')
108 self.machineCom = None
110 self.gcodeList = None
114 self.bufferLineCount = 4
116 self.feedrateRatioOuterWall = 1.0
117 self.feedrateRatioInnerWall = 1.0
118 self.feedrateRatioFill = 1.0
119 self.feedrateRatioSupport = 1.0
121 self.termHistory = []
122 self.termHistoryIdx = 0
125 if webcam.hasWebcamSupport():
126 self.cam = webcam.webcam()
127 if not self.cam.hasCamera():
130 self.SetSizer(wx.BoxSizer())
131 self.panel = wx.Panel(self)
132 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
133 self.sizer = wx.GridBagSizer(2, 2)
134 self.panel.SetSizer(self.sizer)
136 sb = wx.StaticBox(self.panel, label="Statistics")
137 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
139 self.powerWarningText = wx.StaticText(parent=self.panel,
141 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
142 style=wx.ALIGN_CENTER)
143 self.powerWarningText.SetBackgroundColour('red')
144 self.powerWarningText.SetForegroundColour('white')
145 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
146 self.powerManagement = power.PowerManagement()
147 self.powerWarningTimer = wx.Timer(self)
148 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
149 self.OnPowerWarningChange(None)
150 self.powerWarningTimer.Start(10000)
152 self.statsText = wx.StaticText(self.panel, -1,
153 "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
154 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
156 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
158 self.connectButton = wx.Button(self.panel, -1, 'Connect')
159 #self.loadButton = wx.Button(self.panel, -1, 'Load')
160 self.printButton = wx.Button(self.panel, -1, 'Print')
161 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
162 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
163 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
164 self.progress = wx.Gauge(self.panel, -1)
166 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
167 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
168 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
169 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
170 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
171 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
172 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
174 nb = wx.Notebook(self.panel)
175 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
177 self.temperaturePanel = wx.Panel(nb)
178 sizer = wx.GridBagSizer(2, 2)
179 self.temperaturePanel.SetSizer(sizer)
181 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
182 self.temperatureSelect.SetRange(0, 400)
183 self.temperatureHeatUpPLA = wx.Button(self.temperaturePanel, -1, '210C')
184 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
185 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
186 style=wx.SP_ARROW_KEYS)
187 self.bedTemperatureSelect.SetRange(0, 400)
188 self.bedTemperatureLabel.Show(False)
189 self.bedTemperatureSelect.Show(False)
191 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
193 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
194 sizer.Add(self.temperatureSelect, pos=(0, 1))
195 sizer.Add(self.temperatureHeatUpPLA, pos=(0, 2))
196 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
197 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
198 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 3), flag=wx.EXPAND)
199 sizer.AddGrowableRow(2)
200 sizer.AddGrowableCol(2)
202 nb.AddPage(self.temperaturePanel, 'Temp')
204 self.directControlPanel = wx.Panel(nb)
206 sizer = wx.GridBagSizer(2, 2)
207 self.directControlPanel.SetSizer(sizer)
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
209 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
210 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
213 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
214 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
216 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
217 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
218 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
220 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
222 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
223 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
224 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
226 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
227 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
228 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
230 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
232 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
233 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
234 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
236 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
237 span=(1, 3), flag=wx.EXPAND)
238 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
239 span=(1, 3), flag=wx.EXPAND)
241 nb.AddPage(self.directControlPanel, 'Jog')
243 self.speedPanel = wx.Panel(nb)
244 sizer = wx.GridBagSizer(2, 2)
245 self.speedPanel.SetSizer(sizer)
247 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
248 self.outerWallSpeedSelect.SetRange(5, 1000)
249 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
250 self.innerWallSpeedSelect.SetRange(5, 1000)
251 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
252 self.fillSpeedSelect.SetRange(5, 1000)
253 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
254 self.supportSpeedSelect.SetRange(5, 1000)
256 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
257 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
258 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
259 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
260 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
261 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
262 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
263 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
264 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
265 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
266 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
267 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
269 nb.AddPage(self.speedPanel, 'Speed')
271 self.termPanel = wx.Panel(nb)
272 sizer = wx.GridBagSizer(2, 2)
273 self.termPanel.SetSizer(sizer)
275 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
276 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
277 self.termLog.SetFont(f)
278 self.termLog.SetEditable(0)
279 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
280 self.termInput.SetFont(f)
282 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
283 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
284 sizer.AddGrowableCol(0)
285 sizer.AddGrowableRow(0)
287 nb.AddPage(self.termPanel, 'Term')
289 if self.cam is not None:
290 self.camPage = wx.Panel(nb)
291 sizer = wx.GridBagSizer(2, 2)
292 self.camPage.SetSizer(sizer)
294 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelapse movie recording')
295 sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
297 pages = self.cam.propertyPages()
298 self.cam.buttons = [self.timelapsEnable]
300 button = wx.Button(self.camPage, -1, page)
301 button.index = pages.index(page)
302 sizer.Add(button, pos=(1, pages.index(page)))
303 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
304 self.cam.buttons.append(button)
306 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
307 sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
309 self.camPreview = wx.Panel(self.camPage)
310 sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
312 nb.AddPage(self.camPage, 'Camera')
313 self.camPreview.timer = wx.Timer(self)
314 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
315 self.camPreview.timer.Start(500)
316 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
318 self.sizer.AddGrowableRow(6)
319 self.sizer.AddGrowableCol(3)
321 self.Bind(wx.EVT_CLOSE, self.OnClose)
322 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
323 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
324 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
325 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
326 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
327 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
329 self.Bind(wx.EVT_BUTTON, lambda e: (self.temperatureSelect.SetValue(210), self.machineCom.sendCommand("M104 S210")), self.temperatureHeatUpPLA)
330 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
331 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
333 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
334 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
335 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
336 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
337 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
338 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
344 self.statsText.SetMinSize(self.statsText.GetSize())
346 self.UpdateButtonStates()
348 #self.UpdateProgress()
350 def OnCameraTimer(self, e):
351 if not self.campreviewEnable.GetValue():
353 if self.machineCom is not None and self.machineCom.isPrinting():
355 self.cam.takeNewImage()
356 self.camPreview.Refresh()
358 def OnCameraEraseBackground(self, e):
361 dc = wx.ClientDC(self)
362 rect = self.GetUpdateRegion().GetBox()
363 dc.SetClippingRect(rect)
364 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
365 if self.cam.getLastImage() is not None:
366 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
368 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
372 def OnPropertyPageButton(self, e):
373 self.cam.openPropertyPage(e.GetEventObject().index)
375 def UpdateButtonStates(self):
376 self.connectButton.Enable(self.machineCom is None or self.machineCom.isClosedOrError())
377 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
378 self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
379 self.machineCom.isPrinting() or self.machineCom.isPaused()))
380 self.temperatureHeatUpPLA.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
381 self.machineCom.isPrinting() or self.machineCom.isPaused()))
382 self.pauseButton.Enable(
383 self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
384 if self.machineCom is not None and self.machineCom.isPaused():
385 self.pauseButton.SetLabel('Resume')
387 self.pauseButton.SetLabel('Pause')
388 self.cancelButton.Enable(
389 self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
390 self.temperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
391 self.bedTemperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
392 self.directControlPanel.Enable(
393 self.machineCom is not None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
394 self.machineLogButton.Show(self.machineCom is not None and self.machineCom.isClosedOrError())
396 for button in self.cam.buttons:
397 button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
399 def UpdateProgress(self):
401 if self.gcode == None:
402 status += "Loading gcode...\n"
404 status += "Filament: %.2fm %.2fg\n" % (
405 self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
406 cost = self.gcode.calculateCost()
408 status += "Filament cost: %s\n" % (cost)
409 status += "Estimated print time: %02d:%02d\n" % (
410 int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
411 if self.machineCom is None or not self.machineCom.isPrinting():
412 self.progress.SetValue(0)
413 if self.gcodeList is not None:
414 status += 'Line: -/%d\n' % (len(self.gcodeList))
416 printTime = self.machineCom.getPrintTime() / 60
417 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
418 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
419 self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
420 if self.currentZ > 0:
421 status += 'Height: %0.1f\n' % (self.currentZ)
422 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
423 if printTimeLeft == None:
424 status += 'Print time left: Unknown\n'
426 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
427 self.progress.SetValue(self.machineCom.getPrintPos())
428 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
429 if self.machineCom != None:
430 if self.machineCom.getTemp() > 0:
431 status += 'Temp: %d\n' % (self.machineCom.getTemp())
432 if self.machineCom.getBedTemp() > 0:
433 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
434 self.bedTemperatureLabel.Show(True)
435 self.bedTemperatureSelect.Show(True)
436 self.temperaturePanel.Layout()
437 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
439 self.statsText.SetLabel(status.strip())
441 def OnConnect(self, e):
442 if self.machineCom is not None:
443 self.machineCom.close()
444 self.machineCom = machineCom.MachineCom(callbackObject=self)
445 self.UpdateButtonStates()
446 taskbar.setBusy(self, True)
451 def OnPrint(self, e):
452 if self.machineCom is None or not self.machineCom.isOperational():
454 if self.gcodeList is None:
456 if self.machineCom.isPrinting():
459 if self.cam is not None and self.timelapsEnable.GetValue():
460 self.cam.startTimelapse(self.filename[: self.filename.rfind('.')] + ".mpg")
461 self.machineCom.printGCode(self.gcodeList)
462 self.UpdateButtonStates()
464 def OnCancel(self, e):
465 self.pauseButton.SetLabel('Pause')
466 self.machineCom.cancelPrint()
467 self.machineCom.sendCommand("M84")
468 self.machineCom.sendCommand("M104 S0")
469 self.UpdateButtonStates()
471 def OnPause(self, e):
472 if self.machineCom.isPaused():
473 self.machineCom.setPause(False)
475 self.machineCom.setPause(True)
477 def OnMachineLog(self, e):
478 LogWindow('\n'.join(self.machineCom.getLog()))
480 def OnClose(self, e):
481 global printWindowHandle
482 printWindowHandle = None
483 if self.machineCom is not None:
484 self.machineCom.close()
487 def OnTempChange(self, e):
488 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
490 def OnBedTempChange(self, e):
491 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
493 def OnSpeedChange(self, e):
494 if self.machineCom is None:
496 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
497 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
498 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
499 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
501 def AddTermLog(self, line):
502 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
503 l = len(self.termLog.GetValue())
504 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
506 def OnTermEnterLine(self, e):
507 line = self.termInput.GetValue()
510 self.termLog.AppendText('>%s\n' % (line))
511 self.machineCom.sendCommand(line)
512 self.termHistory.append(line)
513 self.termHistoryIdx = len(self.termHistory)
514 self.termInput.SetValue('')
516 def OnTermKey(self, e):
517 if len(self.termHistory) > 0:
518 if e.GetKeyCode() == wx.WXK_UP:
519 self.termHistoryIdx = self.termHistoryIdx - 1
520 if self.termHistoryIdx < 0:
521 self.termHistoryIdx = len(self.termHistory) - 1
522 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
523 if e.GetKeyCode() == wx.WXK_DOWN:
524 self.termHistoryIdx = self.termHistoryIdx - 1
525 if self.termHistoryIdx >= len(self.termHistory):
526 self.termHistoryIdx = 0
527 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
530 def OnPowerWarningChange(self, e):
531 type = self.powerManagement.get_providing_power_source_type()
532 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
533 self.powerWarningText.Hide()
536 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
537 self.powerWarningText.Show()
541 def LoadGCodeFile(self, filename):
542 if self.machineCom is not None and self.machineCom.isPrinting():
544 #Send an initial M110 to reset the line counter to zero.
545 prevLineType = lineType = 'CUSTOM'
547 for line in open(filename, 'r'):
548 if line.startswith(';TYPE:'):
549 lineType = line[6:].strip()
551 line = line[0:line.find(';')]
554 if prevLineType != lineType:
555 gcodeList.append((line, lineType, ))
557 gcodeList.append(line)
558 prevLineType = lineType
559 gcode = gcodeInterpreter.gcode()
560 gcode.loadList(gcodeList)
561 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
562 self.filename = filename
564 self.gcodeList = gcodeList
566 wx.CallAfter(self.progress.SetRange, len(gcodeList))
567 wx.CallAfter(self.UpdateButtonStates)
568 wx.CallAfter(self.UpdateProgress)
569 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
571 def sendLine(self, lineNr):
572 if lineNr >= len(self.gcodeList):
574 line = self.gcodeList[lineNr]
576 if ('M104' in line or 'M109' in line) and 'S' in line:
577 n = int(re.search('S([0-9]*)', line).group(1))
578 wx.CallAfter(self.temperatureSelect.SetValue, n)
579 if ('M140' in line or 'M190' in line) and 'S' in line:
580 n = int(re.search('S([0-9]*)', line).group(1))
581 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
583 print "Unexpected error:", sys.exc_info()
584 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
585 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
588 def mcLog(self, message):
592 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
593 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
594 wx.CallAfter(self._mcTempUpdate, temp, bedTemp, targetTemp, bedTargetTemp)
596 def _mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
597 if self.temperatureSelect.GetValue() != targetTemp and wx.Window.FindFocus() != self.temperatureSelect:
598 self.temperatureSelect.SetValue(targetTemp)
599 if self.bedTemperatureSelect.GetValue() != bedTargetTemp and wx.Window.FindFocus() != self.bedTemperatureSelect:
600 self.bedTemperatureSelect.SetValue(bedTargetTemp)
602 def mcStateChange(self, state):
603 if self.machineCom is not None:
604 if state == self.machineCom.STATE_OPERATIONAL and self.cam is not None:
605 self.cam.endTimelapse()
606 if state == self.machineCom.STATE_OPERATIONAL:
607 taskbar.setBusy(self, False)
608 if self.machineCom.isClosedOrError():
609 taskbar.setBusy(self, False)
610 if self.machineCom.isPaused():
611 taskbar.setPause(self, True)
612 wx.CallAfter(self.UpdateButtonStates)
613 wx.CallAfter(self.UpdateProgress)
615 def mcMessage(self, message):
616 wx.CallAfter(self.AddTermLog, message)
618 def mcProgress(self, lineNr):
619 wx.CallAfter(self.UpdateProgress)
621 def mcZChange(self, newZ):
623 if self.cam is not None:
624 wx.CallAfter(self.cam.takeNewImage)
625 wx.CallAfter(self.camPreview.Refresh)
628 class temperatureGraph(wx.Panel):
629 def __init__(self, parent):
630 super(temperatureGraph, self).__init__(parent)
632 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
633 self.Bind(wx.EVT_SIZE, self.OnSize)
634 self.Bind(wx.EVT_PAINT, self.OnDraw)
636 self.lastDraw = time.time() - 1.0
638 self.backBuffer = None
639 self.addPoint(0, 0, 0, 0)
640 self.SetMinSize((320, 200))
642 def OnEraseBackground(self, e):
646 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
647 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
648 self.UpdateDrawing(True)
651 dc = wx.BufferedPaintDC(self, self.backBuffer)
653 def UpdateDrawing(self, force=False):
655 if not force and now - self.lastDraw < 1.0:
659 dc.SelectObject(self.backBuffer)
661 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
662 w, h = self.GetSizeTuple()
663 bgLinePen = wx.Pen('#A0A0A0')
664 tempPen = wx.Pen('#FF4040')
665 tempSPPen = wx.Pen('#FFA0A0')
666 tempPenBG = wx.Pen('#FFD0D0')
667 bedTempPen = wx.Pen('#4040FF')
668 bedTempSPPen = wx.Pen('#A0A0FF')
669 bedTempPenBG = wx.Pen('#D0D0FF')
671 #Draw the background up to the current temperatures.
677 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
678 x1 = int(w - (now - t))
679 for x in xrange(x0, x1 + 1):
680 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
681 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
683 dc.DrawLine(x, h, x, h - (t * h / 300))
684 dc.SetPen(bedTempPenBG)
685 dc.DrawLine(x, h, x, h - (bt * h / 300))
693 for x in xrange(w, 0, -30):
695 dc.DrawLine(x, 0, x, h)
697 for y in xrange(h - 1, 0, -h * 50 / 300):
699 dc.DrawLine(0, y, w, y)
700 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
702 dc.DrawLine(0, 0, w, 0)
703 dc.DrawLine(0, 0, 0, h)
711 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
712 x1 = int(w - (now - t))
713 for x in xrange(x0, x1 + 1):
714 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
715 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
716 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
717 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
719 dc.DrawPoint(x, h - (tSP * h / 300))
720 dc.SetPen(bedTempSPPen)
721 dc.DrawPoint(x, h - (btSP * h / 300))
723 dc.DrawPoint(x, h - (t * h / 300))
724 dc.SetPen(bedTempPen)
725 dc.DrawPoint(x, h - (bt * h / 300))
733 self.Refresh(eraseBackground=False)
736 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
739 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
742 if bedTempSP == None:
744 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
745 wx.CallAfter(self.UpdateDrawing)
748 class LogWindow(wx.Frame):
749 def __init__(self, logText):
750 super(LogWindow, self).__init__(None, title="Machine log")
751 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
752 self.SetSize((500, 400))