1 from __future__ import absolute_import
\r
4 import wx, threading, re, subprocess, sys, os, time
\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:
\r
42 self.handle = subprocess.Popen([sys.executable, sys.argv[0], '-r', filename], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
\r
43 self.thread = threading.Thread(target=self.Monitor)
\r
46 self.handle.stdin.write(filename + '\n')
\r
50 line = p.stdout.readline()
\r
51 while(len(line) > 0):
\r
52 #print line.rstrip()
\r
53 line = p.stdout.readline()
\r
58 class PrintCommandButton(buttons.GenBitmapButton):
\r
59 def __init__(self, parent, commandList, bitmapFilename, size=(20,20)):
\r
60 self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename)
\r
61 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
\r
63 self.commandList = commandList
\r
64 self.parent = parent
\r
66 self.SetBezelWidth(1)
\r
67 self.SetUseFocusIndicator(False)
\r
69 self.Bind(wx.EVT_BUTTON, self.OnClick)
\r
71 def OnClick(self, e):
\r
72 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
\r
74 for cmd in self.commandList:
\r
75 self.parent.machineCom.sendCommand(cmd)
\r
78 class printWindow(wx.Frame):
\r
79 "Main user interface window"
\r
81 super(printWindow, self).__init__(None, -1, title='Printing')
\r
82 self.machineCom = None
\r
84 self.gcodeList = None
\r
88 self.bufferLineCount = 4
\r
90 self.feedrateRatioOuterWall = 1.0
\r
91 self.feedrateRatioInnerWall = 1.0
\r
92 self.feedrateRatioFill = 1.0
\r
93 self.feedrateRatioSupport = 1.0
\r
95 self.termHistory = []
\r
96 self.termHistoryIdx = 0
\r
99 if webcam.hasWebcamSupport():
\r
100 self.cam = webcam.webcam()
\r
102 #self.SetIcon(icon.getMainIcon())
\r
104 self.SetSizer(wx.BoxSizer())
\r
105 self.panel = wx.Panel(self)
\r
106 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
107 self.sizer = wx.GridBagSizer(2, 2)
\r
108 self.panel.SetSizer(self.sizer)
\r
110 sb = wx.StaticBox(self.panel, label="Statistics")
\r
111 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
112 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
113 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
115 self.sizer.Add(boxsizer, pos=(0,0), span=(6,1), flag=wx.EXPAND)
\r
117 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
118 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
119 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
120 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
121 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
122 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
123 self.progress = wx.Gauge(self.panel, -1)
\r
125 self.sizer.Add(self.connectButton, pos=(0,1))
\r
126 #self.sizer.Add(self.loadButton, pos=(1,1))
\r
127 self.sizer.Add(self.printButton, pos=(2,1))
\r
128 self.sizer.Add(self.pauseButton, pos=(3,1))
\r
129 self.sizer.Add(self.cancelButton, pos=(4,1))
\r
130 self.sizer.Add(self.machineLogButton, pos=(5,1))
\r
131 self.sizer.Add(self.progress, pos=(6,0), span=(1,7), flag=wx.EXPAND)
\r
133 nb = wx.Notebook(self.panel)
\r
134 self.sizer.Add(nb, pos=(0,3), span=(6,4), flag=wx.EXPAND)
\r
136 self.temperaturePanel = wx.Panel(nb)
\r
137 sizer = wx.GridBagSizer(2, 2)
\r
138 self.temperaturePanel.SetSizer(sizer)
\r
140 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
141 self.temperatureSelect.SetRange(0, 400)
\r
142 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
143 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
144 self.bedTemperatureSelect.SetRange(0, 400)
\r
145 self.bedTemperatureLabel.Show(False)
\r
146 self.bedTemperatureSelect.Show(False)
\r
148 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
150 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
151 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
152 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
153 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
154 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
155 sizer.AddGrowableRow(2)
\r
156 sizer.AddGrowableCol(1)
\r
158 nb.AddPage(self.temperaturePanel, 'Temp')
\r
160 self.directControlPanel = wx.Panel(nb)
\r
162 sizer = wx.GridBagSizer(2, 2)
\r
163 self.directControlPanel.SetSizer(sizer)
\r
164 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
165 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
166 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
168 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
169 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
170 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
172 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
173 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
174 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
176 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
178 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
179 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
180 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
182 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
183 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
184 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
186 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
192 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
193 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
195 nb.AddPage(self.directControlPanel, 'Jog')
\r
197 self.speedPanel = wx.Panel(nb)
\r
198 sizer = wx.GridBagSizer(2, 2)
\r
199 self.speedPanel.SetSizer(sizer)
\r
201 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
202 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
203 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
204 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
205 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
206 self.fillSpeedSelect.SetRange(5, 1000)
\r
207 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
208 self.supportSpeedSelect.SetRange(5, 1000)
\r
210 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
211 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
212 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
213 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
214 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
215 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
216 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
217 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
218 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
219 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
220 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
221 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
223 nb.AddPage(self.speedPanel, 'Speed')
\r
225 self.termPanel = wx.Panel(nb)
\r
226 sizer = wx.GridBagSizer(2, 2)
\r
227 self.termPanel.SetSizer(sizer)
\r
229 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
230 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
231 self.termLog.SetFont(f)
\r
232 self.termLog.SetEditable(0)
\r
233 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
234 self.termInput.SetFont(f)
\r
236 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
237 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
238 sizer.AddGrowableCol(0)
\r
239 sizer.AddGrowableRow(0)
\r
241 nb.AddPage(self.termPanel, 'Term')
\r
243 if self.cam != None and self.cam.hasCamera():
\r
244 self.camPage = wx.Panel(nb)
\r
245 sizer = wx.GridBagSizer(2, 2)
\r
246 self.camPage.SetSizer(sizer)
\r
248 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
249 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
251 pages = self.cam.propertyPages()
\r
252 self.cam.buttons = [self.timelapsEnable]
\r
254 button = wx.Button(self.camPage, -1, page)
\r
255 button.index = pages.index(page)
\r
256 sizer.Add(button, pos=(1, pages.index(page)))
\r
257 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
258 self.cam.buttons.append(button)
\r
260 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
261 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
263 self.camPreview = wx.Panel(self.camPage)
\r
264 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
266 nb.AddPage(self.camPage, 'Camera')
\r
267 self.camPreview.timer = wx.Timer(self)
\r
268 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
269 self.camPreview.timer.Start(500)
\r
270 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
272 self.sizer.AddGrowableRow(5)
\r
273 self.sizer.AddGrowableCol(3)
\r
275 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
276 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
277 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
278 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
279 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
280 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
281 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
283 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
284 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
286 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
287 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
288 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
289 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
290 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
291 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
297 self.statsText.SetMinSize(self.statsText.GetSize())
\r
299 self.UpdateButtonStates()
\r
300 #self.UpdateProgress()
\r
302 def OnCameraTimer(self, e):
\r
303 if not self.campreviewEnable.GetValue():
\r
305 if self.machineCom != None and self.machineCom.isPrinting():
\r
307 self.cam.takeNewImage()
\r
308 self.camPreview.Refresh()
\r
310 def OnCameraEraseBackground(self, e):
\r
313 dc = wx.ClientDC(self)
\r
314 rect = self.GetUpdateRegion().GetBox()
\r
315 dc.SetClippingRect(rect)
\r
316 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
317 if self.cam.getLastImage() != None:
\r
318 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
320 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
324 def OnPropertyPageButton(self, e):
\r
325 self.cam.openPropertyPage(e.GetEventObject().index)
\r
327 def UpdateButtonStates(self):
\r
328 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
329 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
330 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
331 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
332 if self.machineCom != None and self.machineCom.isPaused():
\r
333 self.pauseButton.SetLabel('Resume')
\r
335 self.pauseButton.SetLabel('Pause')
\r
336 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
337 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
338 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
339 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
340 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isError())
\r
342 for button in self.cam.buttons:
\r
343 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
345 def UpdateProgress(self):
\r
347 if self.gcode == None:
\r
348 status += "Loading gcode...\n"
\r
350 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
351 cost = self.gcode.calculateCost()
\r
353 status += "Filament cost: %s\n" % (cost)
\r
354 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
355 if self.machineCom == None or not self.machineCom.isPrinting():
\r
356 self.progress.SetValue(0)
\r
357 if self.gcodeList != None:
\r
358 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
360 printTime = self.machineCom.getPrintTime() / 60
\r
361 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
362 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
363 if self.currentZ > 0:
\r
364 status += 'Height: %0.1f\n' % (self.currentZ)
\r
365 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
366 if printTimeLeft == None:
\r
367 status += 'Print time left: Unknown\n'
\r
369 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
370 self.progress.SetValue(self.machineCom.getPrintPos())
\r
371 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
372 if self.machineCom != None:
\r
373 if self.machineCom.getTemp() > 0:
\r
374 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
375 if self.machineCom.getBedTemp() > 0:
\r
376 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
377 self.bedTemperatureLabel.Show(True)
\r
378 self.bedTemperatureSelect.Show(True)
\r
379 self.temperaturePanel.Layout()
\r
380 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
382 self.statsText.SetLabel(status.strip())
\r
384 def OnConnect(self, e):
\r
385 if self.machineCom != None:
\r
386 self.machineCom.close()
\r
387 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
388 self.UpdateButtonStates()
\r
389 taskbar.setBusy(self, True)
\r
391 def OnLoad(self, e):
\r
394 def OnPrint(self, e):
\r
395 if self.machineCom == None or not self.machineCom.isOperational():
\r
397 if self.gcodeList == None:
\r
399 if self.machineCom.isPrinting():
\r
402 if self.cam != None and self.timelapsEnable.GetValue():
\r
403 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
404 self.machineCom.printGCode(self.gcodeList)
\r
405 self.UpdateButtonStates()
\r
407 def OnCancel(self, e):
\r
408 self.pauseButton.SetLabel('Pause')
\r
409 self.machineCom.cancelPrint()
\r
410 self.machineCom.sendCommand("M84")
\r
411 self.UpdateButtonStates()
\r
413 def OnPause(self, e):
\r
414 if self.machineCom.isPaused():
\r
415 self.machineCom.setPause(False)
\r
417 self.machineCom.setPause(True)
\r
419 def OnMachineLog(self, e):
\r
420 LogWindow('\n'.join(self.machineCom.getLog()))
\r
422 def OnClose(self, e):
\r
423 global printWindowHandle
\r
424 printWindowHandle = None
\r
425 if self.machineCom != None:
\r
426 self.machineCom.close()
\r
429 def OnTempChange(self, e):
\r
430 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
432 def OnBedTempChange(self, e):
\r
433 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
435 def OnSpeedChange(self, e):
\r
436 if self.machineCom == None:
\r
438 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
439 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
440 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
441 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
443 def AddTermLog(self, line):
\r
444 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
445 l = len(self.termLog.GetValue())
\r
446 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
448 def OnTermEnterLine(self, e):
\r
449 line = self.termInput.GetValue()
\r
452 self.termLog.AppendText('>%s\n' % (line))
\r
453 self.machineCom.sendCommand(line)
\r
454 self.termHistory.append(line)
\r
455 self.termHistoryIdx = len(self.termHistory)
\r
456 self.termInput.SetValue('')
\r
458 def OnTermKey(self, e):
\r
459 if len(self.termHistory) > 0:
\r
460 if e.GetKeyCode() == wx.WXK_UP:
\r
461 self.termHistoryIdx = self.termHistoryIdx - 1
\r
462 if self.termHistoryIdx < 0:
\r
463 self.termHistoryIdx = len(self.termHistory) - 1
\r
464 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
465 if e.GetKeyCode() == wx.WXK_DOWN:
\r
466 self.termHistoryIdx = self.termHistoryIdx - 1
\r
467 if self.termHistoryIdx >= len(self.termHistory):
\r
468 self.termHistoryIdx = 0
\r
469 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
472 def LoadGCodeFile(self, filename):
\r
473 if self.machineCom != None and self.machineCom.isPrinting():
\r
475 #Send an initial M110 to reset the line counter to zero.
\r
476 prevLineType = lineType = 'CUSTOM'
\r
477 gcodeList = ["M110"]
\r
478 for line in open(filename, 'r'):
\r
479 if line.startswith(';TYPE:'):
\r
480 lineType = line[6:].strip()
\r
482 line = line[0:line.find(';')]
\r
483 line = line.strip()
\r
485 if prevLineType != lineType:
\r
486 gcodeList.append((line, lineType, ))
\r
488 gcodeList.append(line)
\r
489 prevLineType = lineType
\r
490 gcode = gcodeInterpreter.gcode()
\r
491 gcode.loadList(gcodeList)
\r
492 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
493 self.filename = filename
\r
495 self.gcodeList = gcodeList
\r
497 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
498 wx.CallAfter(self.UpdateButtonStates)
\r
499 wx.CallAfter(self.UpdateProgress)
\r
500 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
502 def sendLine(self, lineNr):
\r
503 if lineNr >= len(self.gcodeList):
\r
505 line = self.gcodeList[lineNr]
\r
507 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
508 n = int(re.search('S([0-9]*)', line).group(1))
\r
509 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
510 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
511 n = int(re.search('S([0-9]*)', line).group(1))
\r
512 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
514 print "Unexpected error:", sys.exc_info()
\r
515 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
516 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
519 def mcLog(self, message):
\r
523 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
524 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
525 if self.temperatureSelect.GetValue() != targetTemp:
\r
526 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
527 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
528 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
530 def mcStateChange(self, state):
\r
531 if self.machineCom != None:
\r
532 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
533 self.cam.endTimelaps()
\r
534 if state == self.machineCom.STATE_OPERATIONAL:
\r
535 taskbar.setBusy(self, False)
\r
536 if self.machineCom.isClosedOrError():
\r
537 taskbar.setBusy(self, False)
\r
538 if self.machineCom.isPaused():
\r
539 taskbar.setPause(self, True)
\r
540 wx.CallAfter(self.UpdateButtonStates)
\r
541 wx.CallAfter(self.UpdateProgress)
\r
543 def mcMessage(self, message):
\r
544 wx.CallAfter(self.AddTermLog, message)
\r
546 def mcProgress(self, lineNr):
\r
547 wx.CallAfter(self.UpdateProgress)
\r
549 def mcZChange(self, newZ):
\r
550 self.currentZ = newZ
\r
551 if self.cam != None:
\r
552 wx.CallAfter(self.cam.takeNewImage)
\r
553 wx.CallAfter(self.camPreview.Refresh)
\r
555 class temperatureGraph(wx.Panel):
\r
556 def __init__(self, parent):
\r
557 super(temperatureGraph, self).__init__(parent)
\r
559 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
560 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
561 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
563 self.lastDraw = time.time() - 1.0
\r
565 self.backBuffer = None
\r
566 self.addPoint(0,0,0,0)
\r
567 self.SetMinSize((320,200))
\r
569 def OnEraseBackground(self, e):
\r
572 def OnSize(self, e):
\r
573 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
574 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
575 self.UpdateDrawing(True)
\r
577 def OnDraw(self, e):
\r
578 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
580 def UpdateDrawing(self, force = False):
\r
582 if not force and now - self.lastDraw < 1.0:
\r
584 self.lastDraw = now
\r
586 dc.SelectObject(self.backBuffer)
\r
588 w, h = self.GetSizeTuple()
\r
589 bgLinePen = wx.Pen('#A0A0A0')
\r
590 tempPen = wx.Pen('#FF4040')
\r
591 tempSPPen = wx.Pen('#FFA0A0')
\r
592 tempPenBG = wx.Pen('#FFD0D0')
\r
593 bedTempPen = wx.Pen('#4040FF')
\r
594 bedTempSPPen = wx.Pen('#A0A0FF')
\r
595 bedTempPenBG = wx.Pen('#D0D0FF')
\r
597 #Draw the background up to the current temperatures.
\r
603 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
604 x1 = int(w - (now - t))
\r
605 for x in xrange(x0, x1 + 1):
\r
606 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
607 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
608 dc.SetPen(tempPenBG)
\r
609 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
610 dc.SetPen(bedTempPenBG)
\r
611 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
619 for x in xrange(w, 0, -30):
\r
620 dc.SetPen(bgLinePen)
\r
621 dc.DrawLine(x, 0, x, h)
\r
622 for y in xrange(h-1, 0, -h * 50 / 300):
\r
623 dc.SetPen(bgLinePen)
\r
624 dc.DrawLine(0, y, w, y)
\r
625 dc.DrawLine(0, 0, w, 0)
\r
626 dc.DrawLine(0, 0, 0, h)
\r
628 #Draw the main lines
\r
634 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
635 x1 = int(w - (now - t))
\r
636 for x in xrange(x0, x1 + 1):
\r
637 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
638 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
639 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
640 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
641 dc.SetPen(tempSPPen)
\r
642 dc.DrawPoint(x, h - (tSP * h / 300))
\r
643 dc.SetPen(bedTempSPPen)
\r
644 dc.DrawPoint(x, h - (btSP * h / 300))
\r
646 dc.DrawPoint(x, h - (t * h / 300))
\r
647 dc.SetPen(bedTempPen)
\r
648 dc.DrawPoint(x, h - (bt * h / 300))
\r
656 self.Refresh(eraseBackground=False)
\r
659 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
662 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
663 if bedTemp == None:
\r
665 if bedTempSP == None:
\r
667 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
668 wx.CallAfter(self.UpdateDrawing)
\r
670 class LogWindow(wx.Frame):
\r
671 def __init__(self, logText):
\r
672 super(LogWindow, self).__init__(None, title="Machine log")
\r
673 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
674 self.SetSize((500,400))
\r