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')
48 self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
49 self.thread = threading.Thread(target=self.Monitor)
\r
52 self.handle.stdin.write(filename + '\n')
\r
56 line = p.stdout.readline()
\r
57 while(len(line) > 0):
\r
58 #print line.rstrip()
\r
59 line = p.stdout.readline()
\r
64 class PrintCommandButton(buttons.GenBitmapButton):
\r
65 def __init__(self, parent, commandList, bitmapFilename, size=(20,20)):
\r
66 self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename)
\r
67 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
\r
69 self.commandList = commandList
\r
70 self.parent = parent
\r
72 self.SetBezelWidth(1)
\r
73 self.SetUseFocusIndicator(False)
\r
75 self.Bind(wx.EVT_BUTTON, self.OnClick)
\r
77 def OnClick(self, e):
\r
78 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
\r
80 for cmd in self.commandList:
\r
81 self.parent.machineCom.sendCommand(cmd)
\r
84 class printWindow(wx.Frame):
\r
85 "Main user interface window"
\r
87 super(printWindow, self).__init__(None, -1, title='Printing')
\r
88 self.machineCom = None
\r
90 self.gcodeList = None
\r
94 self.bufferLineCount = 4
\r
96 self.feedrateRatioOuterWall = 1.0
\r
97 self.feedrateRatioInnerWall = 1.0
\r
98 self.feedrateRatioFill = 1.0
\r
99 self.feedrateRatioSupport = 1.0
\r
101 self.termHistory = []
\r
102 self.termHistoryIdx = 0
\r
105 if webcam.hasWebcamSupport():
\r
106 self.cam = webcam.webcam()
\r
108 #self.SetIcon(icon.getMainIcon())
\r
110 self.SetSizer(wx.BoxSizer())
\r
111 self.panel = wx.Panel(self)
\r
112 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
113 self.sizer = wx.GridBagSizer(2, 2)
\r
114 self.panel.SetSizer(self.sizer)
\r
116 sb = wx.StaticBox(self.panel, label="Statistics")
\r
117 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
118 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
119 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
121 self.sizer.Add(boxsizer, pos=(0,0), span=(6,1), flag=wx.EXPAND)
\r
123 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
124 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
125 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
126 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
127 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
128 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
129 self.progress = wx.Gauge(self.panel, -1)
\r
131 self.sizer.Add(self.connectButton, pos=(0,1))
\r
132 #self.sizer.Add(self.loadButton, pos=(1,1))
\r
133 self.sizer.Add(self.printButton, pos=(2,1))
\r
134 self.sizer.Add(self.pauseButton, pos=(3,1))
\r
135 self.sizer.Add(self.cancelButton, pos=(4,1))
\r
136 self.sizer.Add(self.machineLogButton, pos=(5,1))
\r
137 self.sizer.Add(self.progress, pos=(6,0), span=(1,7), flag=wx.EXPAND)
\r
139 nb = wx.Notebook(self.panel)
\r
140 self.sizer.Add(nb, pos=(0,3), span=(6,4), flag=wx.EXPAND)
\r
142 self.temperaturePanel = wx.Panel(nb)
\r
143 sizer = wx.GridBagSizer(2, 2)
\r
144 self.temperaturePanel.SetSizer(sizer)
\r
146 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
147 self.temperatureSelect.SetRange(0, 400)
\r
148 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
149 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
150 self.bedTemperatureSelect.SetRange(0, 400)
\r
151 self.bedTemperatureLabel.Show(False)
\r
152 self.bedTemperatureSelect.Show(False)
\r
154 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
156 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
157 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
158 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
159 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
160 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
161 sizer.AddGrowableRow(2)
\r
162 sizer.AddGrowableCol(1)
\r
164 nb.AddPage(self.temperaturePanel, 'Temp')
\r
166 self.directControlPanel = wx.Panel(nb)
\r
168 sizer = wx.GridBagSizer(2, 2)
\r
169 self.directControlPanel.SetSizer(sizer)
\r
170 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
171 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
172 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
174 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
175 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
176 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
178 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
179 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
180 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
182 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
184 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
185 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
186 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
192 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
194 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
195 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
196 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
198 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
199 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
201 nb.AddPage(self.directControlPanel, 'Jog')
\r
203 self.speedPanel = wx.Panel(nb)
\r
204 sizer = wx.GridBagSizer(2, 2)
\r
205 self.speedPanel.SetSizer(sizer)
\r
207 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
208 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
209 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
210 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
211 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
212 self.fillSpeedSelect.SetRange(5, 1000)
\r
213 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
214 self.supportSpeedSelect.SetRange(5, 1000)
\r
216 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
217 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
218 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
219 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
220 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
221 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
222 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
223 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
224 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
225 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
226 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
227 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
229 nb.AddPage(self.speedPanel, 'Speed')
\r
231 self.termPanel = wx.Panel(nb)
\r
232 sizer = wx.GridBagSizer(2, 2)
\r
233 self.termPanel.SetSizer(sizer)
\r
235 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
236 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
237 self.termLog.SetFont(f)
\r
238 self.termLog.SetEditable(0)
\r
239 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
240 self.termInput.SetFont(f)
\r
242 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
243 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
244 sizer.AddGrowableCol(0)
\r
245 sizer.AddGrowableRow(0)
\r
247 nb.AddPage(self.termPanel, 'Term')
\r
249 if self.cam != None and self.cam.hasCamera():
\r
250 self.camPage = wx.Panel(nb)
\r
251 sizer = wx.GridBagSizer(2, 2)
\r
252 self.camPage.SetSizer(sizer)
\r
254 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
255 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
257 pages = self.cam.propertyPages()
\r
258 self.cam.buttons = [self.timelapsEnable]
\r
260 button = wx.Button(self.camPage, -1, page)
\r
261 button.index = pages.index(page)
\r
262 sizer.Add(button, pos=(1, pages.index(page)))
\r
263 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
264 self.cam.buttons.append(button)
\r
266 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
267 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
269 self.camPreview = wx.Panel(self.camPage)
\r
270 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
272 nb.AddPage(self.camPage, 'Camera')
\r
273 self.camPreview.timer = wx.Timer(self)
\r
274 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
275 self.camPreview.timer.Start(500)
\r
276 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
278 self.sizer.AddGrowableRow(5)
\r
279 self.sizer.AddGrowableCol(3)
\r
281 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
282 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
283 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
284 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
285 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
286 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
287 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
289 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
290 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
292 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
293 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
294 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
295 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
296 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
297 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
303 self.statsText.SetMinSize(self.statsText.GetSize())
\r
305 self.UpdateButtonStates()
\r
306 #self.UpdateProgress()
\r
308 def OnCameraTimer(self, e):
\r
309 if not self.campreviewEnable.GetValue():
\r
311 if self.machineCom != None and self.machineCom.isPrinting():
\r
313 self.cam.takeNewImage()
\r
314 self.camPreview.Refresh()
\r
316 def OnCameraEraseBackground(self, e):
\r
319 dc = wx.ClientDC(self)
\r
320 rect = self.GetUpdateRegion().GetBox()
\r
321 dc.SetClippingRect(rect)
\r
322 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
323 if self.cam.getLastImage() != None:
\r
324 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
326 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
330 def OnPropertyPageButton(self, e):
\r
331 self.cam.openPropertyPage(e.GetEventObject().index)
\r
333 def UpdateButtonStates(self):
\r
334 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
335 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
336 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
337 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
338 if self.machineCom != None and self.machineCom.isPaused():
\r
339 self.pauseButton.SetLabel('Resume')
\r
341 self.pauseButton.SetLabel('Pause')
\r
342 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
343 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
344 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
345 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
346 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isError())
\r
348 for button in self.cam.buttons:
\r
349 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
351 def UpdateProgress(self):
\r
353 if self.gcode == None:
\r
354 status += "Loading gcode...\n"
\r
356 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
357 cost = self.gcode.calculateCost()
\r
359 status += "Filament cost: %s\n" % (cost)
\r
360 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
361 if self.machineCom == None or not self.machineCom.isPrinting():
\r
362 self.progress.SetValue(0)
\r
363 if self.gcodeList != None:
\r
364 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
366 printTime = self.machineCom.getPrintTime() / 60
\r
367 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
368 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
369 if self.currentZ > 0:
\r
370 status += 'Height: %0.1f\n' % (self.currentZ)
\r
371 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
372 if printTimeLeft == None:
\r
373 status += 'Print time left: Unknown\n'
\r
375 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
376 self.progress.SetValue(self.machineCom.getPrintPos())
\r
377 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
378 if self.machineCom != None:
\r
379 if self.machineCom.getTemp() > 0:
\r
380 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
381 if self.machineCom.getBedTemp() > 0:
\r
382 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
383 self.bedTemperatureLabel.Show(True)
\r
384 self.bedTemperatureSelect.Show(True)
\r
385 self.temperaturePanel.Layout()
\r
386 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
388 self.statsText.SetLabel(status.strip())
\r
390 def OnConnect(self, e):
\r
391 if self.machineCom != None:
\r
392 self.machineCom.close()
\r
393 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
394 self.UpdateButtonStates()
\r
395 taskbar.setBusy(self, True)
\r
397 def OnLoad(self, e):
\r
400 def OnPrint(self, e):
\r
401 if self.machineCom == None or not self.machineCom.isOperational():
\r
403 if self.gcodeList == None:
\r
405 if self.machineCom.isPrinting():
\r
408 if self.cam != None and self.timelapsEnable.GetValue():
\r
409 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
410 self.machineCom.printGCode(self.gcodeList)
\r
411 self.UpdateButtonStates()
\r
413 def OnCancel(self, e):
\r
414 self.pauseButton.SetLabel('Pause')
\r
415 self.machineCom.cancelPrint()
\r
416 self.machineCom.sendCommand("M84")
\r
417 self.UpdateButtonStates()
\r
419 def OnPause(self, e):
\r
420 if self.machineCom.isPaused():
\r
421 self.machineCom.setPause(False)
\r
423 self.machineCom.setPause(True)
\r
425 def OnMachineLog(self, e):
\r
426 LogWindow('\n'.join(self.machineCom.getLog()))
\r
428 def OnClose(self, e):
\r
429 global printWindowHandle
\r
430 printWindowHandle = None
\r
431 if self.machineCom != None:
\r
432 self.machineCom.close()
\r
435 def OnTempChange(self, e):
\r
436 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
438 def OnBedTempChange(self, e):
\r
439 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
441 def OnSpeedChange(self, e):
\r
442 if self.machineCom == None:
\r
444 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
445 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
446 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
447 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
449 def AddTermLog(self, line):
\r
450 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
451 l = len(self.termLog.GetValue())
\r
452 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
454 def OnTermEnterLine(self, e):
\r
455 line = self.termInput.GetValue()
\r
458 self.termLog.AppendText('>%s\n' % (line))
\r
459 self.machineCom.sendCommand(line)
\r
460 self.termHistory.append(line)
\r
461 self.termHistoryIdx = len(self.termHistory)
\r
462 self.termInput.SetValue('')
\r
464 def OnTermKey(self, e):
\r
465 if len(self.termHistory) > 0:
\r
466 if e.GetKeyCode() == wx.WXK_UP:
\r
467 self.termHistoryIdx = self.termHistoryIdx - 1
\r
468 if self.termHistoryIdx < 0:
\r
469 self.termHistoryIdx = len(self.termHistory) - 1
\r
470 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
471 if e.GetKeyCode() == wx.WXK_DOWN:
\r
472 self.termHistoryIdx = self.termHistoryIdx - 1
\r
473 if self.termHistoryIdx >= len(self.termHistory):
\r
474 self.termHistoryIdx = 0
\r
475 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
478 def LoadGCodeFile(self, filename):
\r
479 if self.machineCom != None and self.machineCom.isPrinting():
\r
481 #Send an initial M110 to reset the line counter to zero.
\r
482 prevLineType = lineType = 'CUSTOM'
\r
483 gcodeList = ["M110"]
\r
484 for line in open(filename, 'r'):
\r
485 if line.startswith(';TYPE:'):
\r
486 lineType = line[6:].strip()
\r
488 line = line[0:line.find(';')]
\r
489 line = line.strip()
\r
491 if prevLineType != lineType:
\r
492 gcodeList.append((line, lineType, ))
\r
494 gcodeList.append(line)
\r
495 prevLineType = lineType
\r
496 gcode = gcodeInterpreter.gcode()
\r
497 gcode.loadList(gcodeList)
\r
498 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
499 self.filename = filename
\r
501 self.gcodeList = gcodeList
\r
503 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
504 wx.CallAfter(self.UpdateButtonStates)
\r
505 wx.CallAfter(self.UpdateProgress)
\r
506 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
508 def sendLine(self, lineNr):
\r
509 if lineNr >= len(self.gcodeList):
\r
511 line = self.gcodeList[lineNr]
\r
513 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
514 n = int(re.search('S([0-9]*)', line).group(1))
\r
515 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
516 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
517 n = int(re.search('S([0-9]*)', line).group(1))
\r
518 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
520 print "Unexpected error:", sys.exc_info()
\r
521 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
522 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
525 def mcLog(self, message):
\r
529 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
530 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
531 if self.temperatureSelect.GetValue() != targetTemp:
\r
532 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
533 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
534 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
536 def mcStateChange(self, state):
\r
537 if self.machineCom != None:
\r
538 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
539 self.cam.endTimelaps()
\r
540 if state == self.machineCom.STATE_OPERATIONAL:
\r
541 taskbar.setBusy(self, False)
\r
542 if self.machineCom.isClosedOrError():
\r
543 taskbar.setBusy(self, False)
\r
544 if self.machineCom.isPaused():
\r
545 taskbar.setPause(self, True)
\r
546 wx.CallAfter(self.UpdateButtonStates)
\r
547 wx.CallAfter(self.UpdateProgress)
\r
549 def mcMessage(self, message):
\r
550 wx.CallAfter(self.AddTermLog, message)
\r
552 def mcProgress(self, lineNr):
\r
553 wx.CallAfter(self.UpdateProgress)
\r
555 def mcZChange(self, newZ):
\r
556 self.currentZ = newZ
\r
557 if self.cam != None:
\r
558 wx.CallAfter(self.cam.takeNewImage)
\r
559 wx.CallAfter(self.camPreview.Refresh)
\r
561 class temperatureGraph(wx.Panel):
\r
562 def __init__(self, parent):
\r
563 super(temperatureGraph, self).__init__(parent)
\r
565 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
566 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
567 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
569 self.lastDraw = time.time() - 1.0
\r
571 self.backBuffer = None
\r
572 self.addPoint(0,0,0,0)
\r
573 self.SetMinSize((320,200))
\r
575 def OnEraseBackground(self, e):
\r
578 def OnSize(self, e):
\r
579 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
580 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
581 self.UpdateDrawing(True)
\r
583 def OnDraw(self, e):
\r
584 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
586 def UpdateDrawing(self, force = False):
\r
588 if not force and now - self.lastDraw < 1.0:
\r
590 self.lastDraw = now
\r
592 dc.SelectObject(self.backBuffer)
\r
594 w, h = self.GetSizeTuple()
\r
595 bgLinePen = wx.Pen('#A0A0A0')
\r
596 tempPen = wx.Pen('#FF4040')
\r
597 tempSPPen = wx.Pen('#FFA0A0')
\r
598 tempPenBG = wx.Pen('#FFD0D0')
\r
599 bedTempPen = wx.Pen('#4040FF')
\r
600 bedTempSPPen = wx.Pen('#A0A0FF')
\r
601 bedTempPenBG = wx.Pen('#D0D0FF')
\r
603 #Draw the background up to the current temperatures.
\r
609 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
610 x1 = int(w - (now - t))
\r
611 for x in xrange(x0, x1 + 1):
\r
612 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
613 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
614 dc.SetPen(tempPenBG)
\r
615 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
616 dc.SetPen(bedTempPenBG)
\r
617 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
625 for x in xrange(w, 0, -30):
\r
626 dc.SetPen(bgLinePen)
\r
627 dc.DrawLine(x, 0, x, h)
\r
628 for y in xrange(h-1, 0, -h * 50 / 300):
\r
629 dc.SetPen(bgLinePen)
\r
630 dc.DrawLine(0, y, w, y)
\r
631 dc.DrawLine(0, 0, w, 0)
\r
632 dc.DrawLine(0, 0, 0, h)
\r
634 #Draw the main lines
\r
640 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
641 x1 = int(w - (now - t))
\r
642 for x in xrange(x0, x1 + 1):
\r
643 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
644 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
645 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
646 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
647 dc.SetPen(tempSPPen)
\r
648 dc.DrawPoint(x, h - (tSP * h / 300))
\r
649 dc.SetPen(bedTempSPPen)
\r
650 dc.DrawPoint(x, h - (btSP * h / 300))
\r
652 dc.DrawPoint(x, h - (t * h / 300))
\r
653 dc.SetPen(bedTempPen)
\r
654 dc.DrawPoint(x, h - (bt * h / 300))
\r
662 self.Refresh(eraseBackground=False)
\r
665 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
668 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
669 if bedTemp == None:
\r
671 if bedTempSP == None:
\r
673 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
674 wx.CallAfter(self.UpdateDrawing)
\r
676 class LogWindow(wx.Frame):
\r
677 def __init__(self, logText):
\r
678 super(LogWindow, self).__init__(None, title="Machine log")
\r
679 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
680 self.SetSize((500,400))
\r