1 from __future__ import absolute_import
\r
4 import wx, threading, re, subprocess, sys, os, time, platform
\r
5 from wx.lib import buttons
\r
8 from gui import toolbarUtil
\r
9 from gui import webcam
\r
10 from gui import taskbar
\r
11 from util import machineCom
\r
12 from util import profile
\r
13 from util import gcodeInterpreter
\r
15 printWindowMonitorHandle = None
\r
17 def printFile(filename):
\r
18 global printWindowMonitorHandle
\r
19 if printWindowMonitorHandle == None:
\r
20 printWindowMonitorHandle = printProcessMonitor()
\r
21 printWindowMonitorHandle.loadFile(filename)
\r
23 def startPrintInterface(filename):
\r
24 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
\r
25 # 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).
\r
27 printWindowHandle = printWindow()
\r
28 printWindowHandle.Show(True)
\r
29 printWindowHandle.Raise()
\r
30 printWindowHandle.OnConnect(None)
\r
31 t = threading.Thread(target=printWindowHandle.LoadGCodeFile,args=(filename,))
\r
36 class printProcessMonitor():
\r
40 def loadFile(self, filename):
\r
41 if self.handle == None:
42 cmdList = [sys.executable, sys.argv[0], '-r', filename]
43 if platform.system() == "Darwin":
44 if platform.machine() == 'i386':
45 cmdList.insert(0, 'arch')
46 cmdList.insert(1, '-i386')
47 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
48 self.thread = threading.Thread(target=self.Monitor)
\r
51 self.handle.stdin.write(filename + '\n')
\r
55 line = p.stdout.readline()
\r
56 while(len(line) > 0):
\r
57 #print line.rstrip()
\r
58 line = p.stdout.readline()
\r
63 class PrintCommandButton(buttons.GenBitmapButton):
\r
64 def __init__(self, parent, commandList, bitmapFilename, size=(20,20)):
\r
65 self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename)
\r
66 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
\r
68 self.commandList = commandList
\r
69 self.parent = parent
\r
71 self.SetBezelWidth(1)
\r
72 self.SetUseFocusIndicator(False)
\r
74 self.Bind(wx.EVT_BUTTON, self.OnClick)
\r
76 def OnClick(self, e):
\r
77 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
\r
79 for cmd in self.commandList:
\r
80 self.parent.machineCom.sendCommand(cmd)
\r
83 class printWindow(wx.Frame):
\r
84 "Main user interface window"
\r
86 super(printWindow, self).__init__(None, -1, title='Printing')
\r
87 self.machineCom = None
\r
89 self.gcodeList = None
\r
93 self.bufferLineCount = 4
\r
95 self.feedrateRatioOuterWall = 1.0
\r
96 self.feedrateRatioInnerWall = 1.0
\r
97 self.feedrateRatioFill = 1.0
\r
98 self.feedrateRatioSupport = 1.0
\r
100 self.termHistory = []
\r
101 self.termHistoryIdx = 0
\r
104 if webcam.hasWebcamSupport():
\r
105 self.cam = webcam.webcam()
\r
107 #self.SetIcon(icon.getMainIcon())
\r
109 self.SetSizer(wx.BoxSizer())
\r
110 self.panel = wx.Panel(self)
\r
111 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
112 self.sizer = wx.GridBagSizer(2, 2)
\r
113 self.panel.SetSizer(self.sizer)
\r
115 sb = wx.StaticBox(self.panel, label="Statistics")
\r
116 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
117 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
118 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
120 self.sizer.Add(boxsizer, pos=(0,0), span=(6,1), flag=wx.EXPAND)
\r
122 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
123 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
124 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
125 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
126 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
127 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
128 self.progress = wx.Gauge(self.panel, -1)
\r
130 self.sizer.Add(self.connectButton, pos=(0,1))
\r
131 #self.sizer.Add(self.loadButton, pos=(1,1))
\r
132 self.sizer.Add(self.printButton, pos=(2,1))
\r
133 self.sizer.Add(self.pauseButton, pos=(3,1))
\r
134 self.sizer.Add(self.cancelButton, pos=(4,1))
\r
135 self.sizer.Add(self.machineLogButton, pos=(5,1))
\r
136 self.sizer.Add(self.progress, pos=(6,0), span=(1,7), flag=wx.EXPAND)
\r
138 nb = wx.Notebook(self.panel)
\r
139 self.sizer.Add(nb, pos=(0,3), span=(6,4), flag=wx.EXPAND)
\r
141 self.temperaturePanel = wx.Panel(nb)
\r
142 sizer = wx.GridBagSizer(2, 2)
\r
143 self.temperaturePanel.SetSizer(sizer)
\r
145 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
146 self.temperatureSelect.SetRange(0, 400)
\r
147 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
148 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
149 self.bedTemperatureSelect.SetRange(0, 400)
\r
150 self.bedTemperatureLabel.Show(False)
\r
151 self.bedTemperatureSelect.Show(False)
\r
153 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
155 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
156 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
157 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
158 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
159 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
160 sizer.AddGrowableRow(2)
\r
161 sizer.AddGrowableCol(1)
\r
163 nb.AddPage(self.temperaturePanel, 'Temp')
\r
165 self.directControlPanel = wx.Panel(nb)
\r
167 sizer = wx.GridBagSizer(2, 2)
\r
168 self.directControlPanel.SetSizer(sizer)
\r
169 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
170 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
171 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
173 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
174 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
175 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
177 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
178 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
179 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
181 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
183 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
184 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
185 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
187 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
191 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
193 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
194 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
195 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
197 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
198 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60,20)), pos=(2,10), span=(1,3), flag=wx.EXPAND)
\r
200 nb.AddPage(self.directControlPanel, 'Jog')
\r
202 self.speedPanel = wx.Panel(nb)
\r
203 sizer = wx.GridBagSizer(2, 2)
\r
204 self.speedPanel.SetSizer(sizer)
\r
206 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
207 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
208 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
209 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
210 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
211 self.fillSpeedSelect.SetRange(5, 1000)
\r
212 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
213 self.supportSpeedSelect.SetRange(5, 1000)
\r
215 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
216 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
217 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
218 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
219 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
220 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
221 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
222 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
223 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
224 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
225 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
226 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
228 nb.AddPage(self.speedPanel, 'Speed')
\r
230 self.termPanel = wx.Panel(nb)
\r
231 sizer = wx.GridBagSizer(2, 2)
\r
232 self.termPanel.SetSizer(sizer)
\r
234 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
235 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
236 self.termLog.SetFont(f)
\r
237 self.termLog.SetEditable(0)
\r
238 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
239 self.termInput.SetFont(f)
\r
241 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
242 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
243 sizer.AddGrowableCol(0)
\r
244 sizer.AddGrowableRow(0)
\r
246 nb.AddPage(self.termPanel, 'Term')
\r
248 if self.cam != None and self.cam.hasCamera():
\r
249 self.camPage = wx.Panel(nb)
\r
250 sizer = wx.GridBagSizer(2, 2)
\r
251 self.camPage.SetSizer(sizer)
\r
253 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
254 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
256 pages = self.cam.propertyPages()
\r
257 self.cam.buttons = [self.timelapsEnable]
\r
259 button = wx.Button(self.camPage, -1, page)
\r
260 button.index = pages.index(page)
\r
261 sizer.Add(button, pos=(1, pages.index(page)))
\r
262 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
263 self.cam.buttons.append(button)
\r
265 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
266 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
268 self.camPreview = wx.Panel(self.camPage)
\r
269 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
271 nb.AddPage(self.camPage, 'Camera')
\r
272 self.camPreview.timer = wx.Timer(self)
\r
273 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
274 self.camPreview.timer.Start(500)
\r
275 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
277 self.sizer.AddGrowableRow(5)
\r
278 self.sizer.AddGrowableCol(3)
\r
280 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
281 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
282 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
283 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
284 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
285 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
286 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
288 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
289 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
291 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
292 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
293 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
294 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
295 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
296 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
302 self.statsText.SetMinSize(self.statsText.GetSize())
\r
304 self.UpdateButtonStates()
\r
305 #self.UpdateProgress()
\r
307 def OnCameraTimer(self, e):
\r
308 if not self.campreviewEnable.GetValue():
\r
310 if self.machineCom != None and self.machineCom.isPrinting():
\r
312 self.cam.takeNewImage()
\r
313 self.camPreview.Refresh()
\r
315 def OnCameraEraseBackground(self, e):
\r
318 dc = wx.ClientDC(self)
\r
319 rect = self.GetUpdateRegion().GetBox()
\r
320 dc.SetClippingRect(rect)
\r
321 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
322 if self.cam.getLastImage() != None:
\r
323 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
325 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
329 def OnPropertyPageButton(self, e):
\r
330 self.cam.openPropertyPage(e.GetEventObject().index)
\r
332 def UpdateButtonStates(self):
\r
333 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
334 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
335 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
336 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
337 if self.machineCom != None and self.machineCom.isPaused():
\r
338 self.pauseButton.SetLabel('Resume')
\r
340 self.pauseButton.SetLabel('Pause')
\r
341 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
342 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
343 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
344 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
345 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isError())
\r
347 for button in self.cam.buttons:
\r
348 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
350 def UpdateProgress(self):
\r
352 if self.gcode == None:
\r
353 status += "Loading gcode...\n"
\r
355 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
356 cost = self.gcode.calculateCost()
\r
358 status += "Filament cost: %s\n" % (cost)
\r
359 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
360 if self.machineCom == None or not self.machineCom.isPrinting():
\r
361 self.progress.SetValue(0)
\r
362 if self.gcodeList != None:
\r
363 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
365 printTime = self.machineCom.getPrintTime() / 60
\r
366 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
367 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
368 if self.currentZ > 0:
\r
369 status += 'Height: %0.1f\n' % (self.currentZ)
\r
370 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
371 if printTimeLeft == None:
\r
372 status += 'Print time left: Unknown\n'
\r
374 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
375 self.progress.SetValue(self.machineCom.getPrintPos())
\r
376 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
377 if self.machineCom != None:
\r
378 if self.machineCom.getTemp() > 0:
\r
379 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
380 if self.machineCom.getBedTemp() > 0:
\r
381 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
382 self.bedTemperatureLabel.Show(True)
\r
383 self.bedTemperatureSelect.Show(True)
\r
384 self.temperaturePanel.Layout()
\r
385 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
387 self.statsText.SetLabel(status.strip())
\r
389 def OnConnect(self, e):
\r
390 if self.machineCom != None:
\r
391 self.machineCom.close()
\r
392 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
393 self.UpdateButtonStates()
\r
394 taskbar.setBusy(self, True)
\r
396 def OnLoad(self, e):
\r
399 def OnPrint(self, e):
\r
400 if self.machineCom == None or not self.machineCom.isOperational():
\r
402 if self.gcodeList == None:
\r
404 if self.machineCom.isPrinting():
\r
407 if self.cam != None and self.timelapsEnable.GetValue():
\r
408 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
409 self.machineCom.printGCode(self.gcodeList)
\r
410 self.UpdateButtonStates()
\r
412 def OnCancel(self, e):
\r
413 self.pauseButton.SetLabel('Pause')
\r
414 self.machineCom.cancelPrint()
\r
415 self.machineCom.sendCommand("M84")
\r
416 self.UpdateButtonStates()
\r
418 def OnPause(self, e):
\r
419 if self.machineCom.isPaused():
\r
420 self.machineCom.setPause(False)
\r
422 self.machineCom.setPause(True)
\r
424 def OnMachineLog(self, e):
\r
425 LogWindow('\n'.join(self.machineCom.getLog()))
\r
427 def OnClose(self, e):
\r
428 global printWindowHandle
\r
429 printWindowHandle = None
\r
430 if self.machineCom != None:
\r
431 self.machineCom.close()
\r
434 def OnTempChange(self, e):
\r
435 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
437 def OnBedTempChange(self, e):
\r
438 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
440 def OnSpeedChange(self, e):
\r
441 if self.machineCom == None:
\r
443 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
444 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
445 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
446 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
448 def AddTermLog(self, line):
\r
449 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
450 l = len(self.termLog.GetValue())
\r
451 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
453 def OnTermEnterLine(self, e):
\r
454 line = self.termInput.GetValue()
\r
457 self.termLog.AppendText('>%s\n' % (line))
\r
458 self.machineCom.sendCommand(line)
\r
459 self.termHistory.append(line)
\r
460 self.termHistoryIdx = len(self.termHistory)
\r
461 self.termInput.SetValue('')
\r
463 def OnTermKey(self, e):
\r
464 if len(self.termHistory) > 0:
\r
465 if e.GetKeyCode() == wx.WXK_UP:
\r
466 self.termHistoryIdx = self.termHistoryIdx - 1
\r
467 if self.termHistoryIdx < 0:
\r
468 self.termHistoryIdx = len(self.termHistory) - 1
\r
469 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
470 if e.GetKeyCode() == wx.WXK_DOWN:
\r
471 self.termHistoryIdx = self.termHistoryIdx - 1
\r
472 if self.termHistoryIdx >= len(self.termHistory):
\r
473 self.termHistoryIdx = 0
\r
474 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
477 def LoadGCodeFile(self, filename):
\r
478 if self.machineCom != None and self.machineCom.isPrinting():
\r
480 #Send an initial M110 to reset the line counter to zero.
\r
481 prevLineType = lineType = 'CUSTOM'
\r
482 gcodeList = ["M110"]
\r
483 for line in open(filename, 'r'):
\r
484 if line.startswith(';TYPE:'):
\r
485 lineType = line[6:].strip()
\r
487 line = line[0:line.find(';')]
\r
488 line = line.strip()
\r
490 if prevLineType != lineType:
\r
491 gcodeList.append((line, lineType, ))
\r
493 gcodeList.append(line)
\r
494 prevLineType = lineType
\r
495 gcode = gcodeInterpreter.gcode()
\r
496 gcode.loadList(gcodeList)
\r
497 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
498 self.filename = filename
\r
500 self.gcodeList = gcodeList
\r
502 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
503 wx.CallAfter(self.UpdateButtonStates)
\r
504 wx.CallAfter(self.UpdateProgress)
\r
505 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
507 def sendLine(self, lineNr):
\r
508 if lineNr >= len(self.gcodeList):
\r
510 line = self.gcodeList[lineNr]
\r
512 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
513 n = int(re.search('S([0-9]*)', line).group(1))
\r
514 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
515 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
516 n = int(re.search('S([0-9]*)', line).group(1))
\r
517 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
519 print "Unexpected error:", sys.exc_info()
\r
520 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
521 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
524 def mcLog(self, message):
\r
528 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
529 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
530 if self.temperatureSelect.GetValue() != targetTemp:
\r
531 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
532 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
533 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
535 def mcStateChange(self, state):
\r
536 if self.machineCom != None:
\r
537 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
538 self.cam.endTimelaps()
\r
539 if state == self.machineCom.STATE_OPERATIONAL:
\r
540 taskbar.setBusy(self, False)
\r
541 if self.machineCom.isClosedOrError():
\r
542 taskbar.setBusy(self, False)
\r
543 if self.machineCom.isPaused():
\r
544 taskbar.setPause(self, True)
\r
545 wx.CallAfter(self.UpdateButtonStates)
\r
546 wx.CallAfter(self.UpdateProgress)
\r
548 def mcMessage(self, message):
\r
549 wx.CallAfter(self.AddTermLog, message)
\r
551 def mcProgress(self, lineNr):
\r
552 wx.CallAfter(self.UpdateProgress)
\r
554 def mcZChange(self, newZ):
\r
555 self.currentZ = newZ
\r
556 if self.cam != None:
\r
557 wx.CallAfter(self.cam.takeNewImage)
\r
558 wx.CallAfter(self.camPreview.Refresh)
\r
560 class temperatureGraph(wx.Panel):
\r
561 def __init__(self, parent):
\r
562 super(temperatureGraph, self).__init__(parent)
\r
564 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
565 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
566 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
568 self.lastDraw = time.time() - 1.0
\r
570 self.backBuffer = None
\r
571 self.addPoint(0,0,0,0)
\r
572 self.SetMinSize((320,200))
\r
574 def OnEraseBackground(self, e):
\r
577 def OnSize(self, e):
\r
578 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
579 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
580 self.UpdateDrawing(True)
\r
582 def OnDraw(self, e):
\r
583 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
585 def UpdateDrawing(self, force = False):
\r
587 if not force and now - self.lastDraw < 1.0:
\r
589 self.lastDraw = now
\r
591 dc.SelectObject(self.backBuffer)
\r
593 w, h = self.GetSizeTuple()
\r
594 bgLinePen = wx.Pen('#A0A0A0')
\r
595 tempPen = wx.Pen('#FF4040')
\r
596 tempSPPen = wx.Pen('#FFA0A0')
\r
597 tempPenBG = wx.Pen('#FFD0D0')
\r
598 bedTempPen = wx.Pen('#4040FF')
\r
599 bedTempSPPen = wx.Pen('#A0A0FF')
\r
600 bedTempPenBG = wx.Pen('#D0D0FF')
\r
602 #Draw the background up to the current temperatures.
\r
608 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
609 x1 = int(w - (now - t))
\r
610 for x in xrange(x0, x1 + 1):
\r
611 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
612 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
613 dc.SetPen(tempPenBG)
\r
614 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
615 dc.SetPen(bedTempPenBG)
\r
616 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
624 for x in xrange(w, 0, -30):
\r
625 dc.SetPen(bgLinePen)
\r
626 dc.DrawLine(x, 0, x, h)
\r
627 for y in xrange(h-1, 0, -h * 50 / 300):
\r
628 dc.SetPen(bgLinePen)
\r
629 dc.DrawLine(0, y, w, y)
\r
630 dc.DrawLine(0, 0, w, 0)
\r
631 dc.DrawLine(0, 0, 0, h)
\r
633 #Draw the main lines
\r
639 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
640 x1 = int(w - (now - t))
\r
641 for x in xrange(x0, x1 + 1):
\r
642 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
643 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
644 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
645 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
646 dc.SetPen(tempSPPen)
\r
647 dc.DrawPoint(x, h - (tSP * h / 300))
\r
648 dc.SetPen(bedTempSPPen)
\r
649 dc.DrawPoint(x, h - (btSP * h / 300))
\r
651 dc.DrawPoint(x, h - (t * h / 300))
\r
652 dc.SetPen(bedTempPen)
\r
653 dc.DrawPoint(x, h - (bt * h / 300))
\r
661 self.Refresh(eraseBackground=False)
\r
664 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
667 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
668 if bedTemp == None:
\r
670 if bedTempSP == None:
\r
672 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
673 wx.CallAfter(self.UpdateDrawing)
\r
675 class LogWindow(wx.Frame):
\r
676 def __init__(self, logText):
\r
677 super(LogWindow, self).__init__(None, title="Machine log")
\r
678 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
679 self.SetSize((500,400))
\r