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
106 if not self.cam.hasCamera():
\r
109 #self.SetIcon(icon.getMainIcon())
\r
111 self.SetSizer(wx.BoxSizer())
\r
112 self.panel = wx.Panel(self)
\r
113 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
114 self.sizer = wx.GridBagSizer(2, 2)
\r
115 self.panel.SetSizer(self.sizer)
\r
117 sb = wx.StaticBox(self.panel, label="Statistics")
\r
118 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
119 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
120 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
122 self.sizer.Add(boxsizer, pos=(0,0), span=(7,1), flag=wx.EXPAND)
\r
124 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
125 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
126 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
127 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
128 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
129 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
130 self.progress = wx.Gauge(self.panel, -1)
\r
132 self.sizer.Add(self.connectButton, pos=(1,1), flag=wx.EXPAND)
\r
133 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
\r
134 self.sizer.Add(self.printButton, pos=(2,1), flag=wx.EXPAND)
\r
135 self.sizer.Add(self.pauseButton, pos=(3,1), flag=wx.EXPAND)
\r
136 self.sizer.Add(self.cancelButton, pos=(4,1), flag=wx.EXPAND)
\r
137 self.sizer.Add(self.machineLogButton, pos=(5,1), flag=wx.EXPAND)
\r
138 self.sizer.Add(self.progress, pos=(7,0), span=(1,7), flag=wx.EXPAND)
\r
140 nb = wx.Notebook(self.panel)
\r
141 self.sizer.Add(nb, pos=(0,2), span=(7,4), flag=wx.EXPAND)
\r
143 self.temperaturePanel = wx.Panel(nb)
\r
144 sizer = wx.GridBagSizer(2, 2)
\r
145 self.temperaturePanel.SetSizer(sizer)
\r
147 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
148 self.temperatureSelect.SetRange(0, 400)
\r
149 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
150 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
151 self.bedTemperatureSelect.SetRange(0, 400)
\r
152 self.bedTemperatureLabel.Show(False)
\r
153 self.bedTemperatureSelect.Show(False)
\r
155 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
157 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
158 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
159 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
160 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
161 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
162 sizer.AddGrowableRow(2)
\r
163 sizer.AddGrowableCol(1)
\r
165 nb.AddPage(self.temperaturePanel, 'Temp')
\r
167 self.directControlPanel = wx.Panel(nb)
\r
169 sizer = wx.GridBagSizer(2, 2)
\r
170 self.directControlPanel.SetSizer(sizer)
\r
171 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
172 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
173 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
175 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
176 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
177 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
179 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
180 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
181 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
183 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
185 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
186 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
187 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
191 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
193 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
195 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
196 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
197 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
199 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
200 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
202 nb.AddPage(self.directControlPanel, 'Jog')
\r
204 self.speedPanel = wx.Panel(nb)
\r
205 sizer = wx.GridBagSizer(2, 2)
\r
206 self.speedPanel.SetSizer(sizer)
\r
208 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
209 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
210 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
211 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
212 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
213 self.fillSpeedSelect.SetRange(5, 1000)
\r
214 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
215 self.supportSpeedSelect.SetRange(5, 1000)
\r
217 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
218 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
219 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
220 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
221 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
222 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
223 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
224 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
225 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
226 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
227 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
228 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
230 nb.AddPage(self.speedPanel, 'Speed')
\r
232 self.termPanel = wx.Panel(nb)
\r
233 sizer = wx.GridBagSizer(2, 2)
\r
234 self.termPanel.SetSizer(sizer)
\r
236 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
237 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
238 self.termLog.SetFont(f)
\r
239 self.termLog.SetEditable(0)
\r
240 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
241 self.termInput.SetFont(f)
\r
243 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
244 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
245 sizer.AddGrowableCol(0)
\r
246 sizer.AddGrowableRow(0)
\r
248 nb.AddPage(self.termPanel, 'Term')
\r
250 if self.cam != None:
\r
251 self.camPage = wx.Panel(nb)
\r
252 sizer = wx.GridBagSizer(2, 2)
\r
253 self.camPage.SetSizer(sizer)
\r
255 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
256 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
258 pages = self.cam.propertyPages()
\r
259 self.cam.buttons = [self.timelapsEnable]
\r
261 button = wx.Button(self.camPage, -1, page)
\r
262 button.index = pages.index(page)
\r
263 sizer.Add(button, pos=(1, pages.index(page)))
\r
264 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
265 self.cam.buttons.append(button)
\r
267 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
268 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
270 self.camPreview = wx.Panel(self.camPage)
\r
271 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
273 nb.AddPage(self.camPage, 'Camera')
\r
274 self.camPreview.timer = wx.Timer(self)
\r
275 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
276 self.camPreview.timer.Start(500)
\r
277 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
279 self.sizer.AddGrowableRow(5)
\r
280 self.sizer.AddGrowableCol(3)
\r
282 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
283 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
284 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
285 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
286 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
287 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
288 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
290 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
291 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
293 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
294 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
295 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
296 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
297 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
298 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
304 self.statsText.SetMinSize(self.statsText.GetSize())
\r
306 self.UpdateButtonStates()
\r
307 #self.UpdateProgress()
\r
309 def OnCameraTimer(self, e):
\r
310 if not self.campreviewEnable.GetValue():
\r
312 if self.machineCom != None and self.machineCom.isPrinting():
\r
314 self.cam.takeNewImage()
\r
315 self.camPreview.Refresh()
\r
317 def OnCameraEraseBackground(self, e):
\r
320 dc = wx.ClientDC(self)
\r
321 rect = self.GetUpdateRegion().GetBox()
\r
322 dc.SetClippingRect(rect)
\r
323 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
324 if self.cam.getLastImage() != None:
\r
325 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
327 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
331 def OnPropertyPageButton(self, e):
\r
332 self.cam.openPropertyPage(e.GetEventObject().index)
\r
334 def UpdateButtonStates(self):
\r
335 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
336 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
337 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
338 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
339 if self.machineCom != None and self.machineCom.isPaused():
\r
340 self.pauseButton.SetLabel('Resume')
\r
342 self.pauseButton.SetLabel('Pause')
\r
343 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
344 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
345 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
346 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
347 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
\r
348 if self.cam != None:
\r
349 for button in self.cam.buttons:
\r
350 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
352 def UpdateProgress(self):
\r
354 if self.gcode == None:
\r
355 status += "Loading gcode...\n"
\r
357 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
358 cost = self.gcode.calculateCost()
\r
360 status += "Filament cost: %s\n" % (cost)
\r
361 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
362 if self.machineCom == None or not self.machineCom.isPrinting():
\r
363 self.progress.SetValue(0)
\r
364 if self.gcodeList != None:
\r
365 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
367 printTime = self.machineCom.getPrintTime() / 60
\r
368 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
369 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
370 if self.currentZ > 0:
\r
371 status += 'Height: %0.1f\n' % (self.currentZ)
\r
372 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
373 if printTimeLeft == None:
\r
374 status += 'Print time left: Unknown\n'
\r
376 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
377 self.progress.SetValue(self.machineCom.getPrintPos())
\r
378 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
379 if self.machineCom != None:
\r
380 if self.machineCom.getTemp() > 0:
\r
381 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
382 if self.machineCom.getBedTemp() > 0:
\r
383 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
384 self.bedTemperatureLabel.Show(True)
\r
385 self.bedTemperatureSelect.Show(True)
\r
386 self.temperaturePanel.Layout()
\r
387 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
389 self.statsText.SetLabel(status.strip())
\r
391 def OnConnect(self, e):
\r
392 if self.machineCom != None:
\r
393 self.machineCom.close()
\r
394 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
395 self.UpdateButtonStates()
\r
396 taskbar.setBusy(self, True)
\r
398 def OnLoad(self, e):
\r
401 def OnPrint(self, e):
\r
402 if self.machineCom == None or not self.machineCom.isOperational():
\r
404 if self.gcodeList == None:
\r
406 if self.machineCom.isPrinting():
\r
409 if self.cam != None and self.timelapsEnable.GetValue():
\r
410 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
411 self.machineCom.printGCode(self.gcodeList)
\r
412 self.UpdateButtonStates()
\r
414 def OnCancel(self, e):
\r
415 self.pauseButton.SetLabel('Pause')
\r
416 self.machineCom.cancelPrint()
\r
417 self.machineCom.sendCommand("M84")
\r
418 self.UpdateButtonStates()
\r
420 def OnPause(self, e):
\r
421 if self.machineCom.isPaused():
\r
422 self.machineCom.setPause(False)
\r
424 self.machineCom.setPause(True)
\r
426 def OnMachineLog(self, e):
\r
427 LogWindow('\n'.join(self.machineCom.getLog()))
\r
429 def OnClose(self, e):
\r
430 global printWindowHandle
\r
431 printWindowHandle = None
\r
432 if self.machineCom != None:
\r
433 self.machineCom.close()
\r
436 def OnTempChange(self, e):
\r
437 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
439 def OnBedTempChange(self, e):
\r
440 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
442 def OnSpeedChange(self, e):
\r
443 if self.machineCom == None:
\r
445 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
446 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
447 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
448 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
450 def AddTermLog(self, line):
\r
451 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
452 l = len(self.termLog.GetValue())
\r
453 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
455 def OnTermEnterLine(self, e):
\r
456 line = self.termInput.GetValue()
\r
459 self.termLog.AppendText('>%s\n' % (line))
\r
460 self.machineCom.sendCommand(line)
\r
461 self.termHistory.append(line)
\r
462 self.termHistoryIdx = len(self.termHistory)
\r
463 self.termInput.SetValue('')
\r
465 def OnTermKey(self, e):
\r
466 if len(self.termHistory) > 0:
\r
467 if e.GetKeyCode() == wx.WXK_UP:
\r
468 self.termHistoryIdx = self.termHistoryIdx - 1
\r
469 if self.termHistoryIdx < 0:
\r
470 self.termHistoryIdx = len(self.termHistory) - 1
\r
471 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
472 if e.GetKeyCode() == wx.WXK_DOWN:
\r
473 self.termHistoryIdx = self.termHistoryIdx - 1
\r
474 if self.termHistoryIdx >= len(self.termHistory):
\r
475 self.termHistoryIdx = 0
\r
476 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
479 def LoadGCodeFile(self, filename):
\r
480 if self.machineCom != None and self.machineCom.isPrinting():
\r
482 #Send an initial M110 to reset the line counter to zero.
\r
483 prevLineType = lineType = 'CUSTOM'
\r
484 gcodeList = ["M110"]
\r
485 for line in open(filename, 'r'):
\r
486 if line.startswith(';TYPE:'):
\r
487 lineType = line[6:].strip()
\r
489 line = line[0:line.find(';')]
\r
490 line = line.strip()
\r
492 if prevLineType != lineType:
\r
493 gcodeList.append((line, lineType, ))
\r
495 gcodeList.append(line)
\r
496 prevLineType = lineType
\r
497 gcode = gcodeInterpreter.gcode()
\r
498 gcode.loadList(gcodeList)
\r
499 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
500 self.filename = filename
\r
502 self.gcodeList = gcodeList
\r
504 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
505 wx.CallAfter(self.UpdateButtonStates)
\r
506 wx.CallAfter(self.UpdateProgress)
\r
507 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
509 def sendLine(self, lineNr):
\r
510 if lineNr >= len(self.gcodeList):
\r
512 line = self.gcodeList[lineNr]
\r
514 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
515 n = int(re.search('S([0-9]*)', line).group(1))
\r
516 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
517 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
518 n = int(re.search('S([0-9]*)', line).group(1))
\r
519 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
521 print "Unexpected error:", sys.exc_info()
\r
522 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
523 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
526 def mcLog(self, message):
\r
530 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
531 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
532 if self.temperatureSelect.GetValue() != targetTemp:
\r
533 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
534 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
535 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
537 def mcStateChange(self, state):
\r
538 if self.machineCom != None:
\r
539 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
540 self.cam.endTimelaps()
\r
541 if state == self.machineCom.STATE_OPERATIONAL:
\r
542 taskbar.setBusy(self, False)
\r
543 if self.machineCom.isClosedOrError():
\r
544 taskbar.setBusy(self, False)
\r
545 if self.machineCom.isPaused():
\r
546 taskbar.setPause(self, True)
\r
547 wx.CallAfter(self.UpdateButtonStates)
\r
548 wx.CallAfter(self.UpdateProgress)
\r
550 def mcMessage(self, message):
\r
551 wx.CallAfter(self.AddTermLog, message)
\r
553 def mcProgress(self, lineNr):
\r
554 wx.CallAfter(self.UpdateProgress)
\r
556 def mcZChange(self, newZ):
\r
557 self.currentZ = newZ
\r
558 if self.cam != None:
\r
559 wx.CallAfter(self.cam.takeNewImage)
\r
560 wx.CallAfter(self.camPreview.Refresh)
\r
562 class temperatureGraph(wx.Panel):
\r
563 def __init__(self, parent):
\r
564 super(temperatureGraph, self).__init__(parent)
\r
566 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
567 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
568 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
570 self.lastDraw = time.time() - 1.0
\r
572 self.backBuffer = None
\r
573 self.addPoint(0,0,0,0)
\r
574 self.SetMinSize((320,200))
\r
576 def OnEraseBackground(self, e):
\r
579 def OnSize(self, e):
\r
580 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
581 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
582 self.UpdateDrawing(True)
\r
584 def OnDraw(self, e):
\r
585 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
587 def UpdateDrawing(self, force = False):
\r
589 if not force and now - self.lastDraw < 1.0:
\r
591 self.lastDraw = now
\r
593 dc.SelectObject(self.backBuffer)
\r
595 w, h = self.GetSizeTuple()
\r
596 bgLinePen = wx.Pen('#A0A0A0')
\r
597 tempPen = wx.Pen('#FF4040')
\r
598 tempSPPen = wx.Pen('#FFA0A0')
\r
599 tempPenBG = wx.Pen('#FFD0D0')
\r
600 bedTempPen = wx.Pen('#4040FF')
\r
601 bedTempSPPen = wx.Pen('#A0A0FF')
\r
602 bedTempPenBG = wx.Pen('#D0D0FF')
\r
604 #Draw the background up to the current temperatures.
\r
610 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
611 x1 = int(w - (now - t))
\r
612 for x in xrange(x0, x1 + 1):
\r
613 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
614 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
615 dc.SetPen(tempPenBG)
\r
616 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
617 dc.SetPen(bedTempPenBG)
\r
618 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
626 for x in xrange(w, 0, -30):
\r
627 dc.SetPen(bgLinePen)
\r
628 dc.DrawLine(x, 0, x, h)
\r
629 for y in xrange(h-1, 0, -h * 50 / 300):
\r
630 dc.SetPen(bgLinePen)
\r
631 dc.DrawLine(0, y, w, y)
\r
632 dc.DrawLine(0, 0, w, 0)
\r
633 dc.DrawLine(0, 0, 0, h)
\r
635 #Draw the main lines
\r
641 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
642 x1 = int(w - (now - t))
\r
643 for x in xrange(x0, x1 + 1):
\r
644 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
645 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
646 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
647 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
648 dc.SetPen(tempSPPen)
\r
649 dc.DrawPoint(x, h - (tSP * h / 300))
\r
650 dc.SetPen(bedTempSPPen)
\r
651 dc.DrawPoint(x, h - (btSP * h / 300))
\r
653 dc.DrawPoint(x, h - (t * h / 300))
\r
654 dc.SetPen(bedTempPen)
\r
655 dc.DrawPoint(x, h - (bt * h / 300))
\r
663 self.Refresh(eraseBackground=False)
\r
666 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
669 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
670 if bedTemp == None:
\r
672 if bedTempSP == None:
\r
674 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
675 wx.CallAfter(self.UpdateDrawing)
\r
677 class LogWindow(wx.Frame):
\r
678 def __init__(self, logText):
\r
679 super(LogWindow, self).__init__(None, title="Machine log")
\r
680 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
681 self.SetSize((500,400))
\r