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
14 from util import power
\r
16 printWindowMonitorHandle = None
\r
18 def printFile(filename):
\r
19 global printWindowMonitorHandle
\r
20 if printWindowMonitorHandle == None:
\r
21 printWindowMonitorHandle = printProcessMonitor()
\r
22 printWindowMonitorHandle.loadFile(filename)
\r
24 def startPrintInterface(filename):
\r
25 #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
\r
26 # 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
28 printWindowHandle = printWindow()
\r
29 printWindowHandle.Show(True)
\r
30 printWindowHandle.Raise()
\r
31 printWindowHandle.OnConnect(None)
\r
32 t = threading.Thread(target=printWindowHandle.LoadGCodeFile,args=(filename,))
\r
37 class printProcessMonitor():
\r
41 def loadFile(self, filename):
\r
42 if self.handle == None:
43 cmdList = [sys.executable, sys.argv[0], '-r', filename]
44 if platform.system() == "Darwin":
45 if platform.machine() == 'i386':
46 cmdList.insert(0, 'arch')
47 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
107 if not self.cam.hasCamera():
\r
110 #self.SetIcon(icon.getMainIcon())
\r
112 self.SetSizer(wx.BoxSizer())
\r
113 self.panel = wx.Panel(self)
\r
114 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
\r
115 self.sizer = wx.GridBagSizer(2, 2)
\r
116 self.panel.SetSizer(self.sizer)
\r
118 sb = wx.StaticBox(self.panel, label="Statistics")
\r
119 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
\r
121 self.powerWarningText = wx.StaticText(parent=self.panel,
\r
123 label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
\r
124 style=wx.ALIGN_CENTER)
\r
125 self.powerWarningText.SetBackgroundColour('red')
\r
126 self.powerWarningText.SetForegroundColour('white')
\r
127 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
\r
128 self.powerManagement = power.PowerManagement()
\r
129 self.powerWarningTimer = wx.Timer(self)
\r
130 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
\r
131 self.OnPowerWarningChange(None)
\r
132 self.powerWarningTimer.Start(10000)
\r
134 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
135 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
137 self.sizer.Add(boxsizer, pos=(0,0), span=(7,1), flag=wx.EXPAND)
\r
139 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
140 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
141 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
142 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
143 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
144 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
145 self.progress = wx.Gauge(self.panel, -1)
\r
147 self.sizer.Add(self.connectButton, pos=(1,1), flag=wx.EXPAND)
\r
148 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
\r
149 self.sizer.Add(self.printButton, pos=(2,1), flag=wx.EXPAND)
\r
150 self.sizer.Add(self.pauseButton, pos=(3,1), flag=wx.EXPAND)
\r
151 self.sizer.Add(self.cancelButton, pos=(4,1), flag=wx.EXPAND)
\r
152 self.sizer.Add(self.machineLogButton, pos=(5,1), flag=wx.EXPAND)
\r
153 self.sizer.Add(self.progress, pos=(7,0), span=(1,7), flag=wx.EXPAND)
\r
155 nb = wx.Notebook(self.panel)
\r
156 self.sizer.Add(nb, pos=(0,2), span=(7,4), flag=wx.EXPAND)
\r
158 self.temperaturePanel = wx.Panel(nb)
\r
159 sizer = wx.GridBagSizer(2, 2)
\r
160 self.temperaturePanel.SetSizer(sizer)
\r
162 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
163 self.temperatureSelect.SetRange(0, 400)
\r
164 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
165 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
166 self.bedTemperatureSelect.SetRange(0, 400)
\r
167 self.bedTemperatureLabel.Show(False)
\r
168 self.bedTemperatureSelect.Show(False)
\r
170 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
172 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
173 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
174 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
175 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
176 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
177 sizer.AddGrowableRow(2)
\r
178 sizer.AddGrowableCol(1)
\r
180 nb.AddPage(self.temperaturePanel, 'Temp')
\r
182 self.directControlPanel = wx.Panel(nb)
\r
184 sizer = wx.GridBagSizer(2, 2)
\r
185 self.directControlPanel.SetSizer(sizer)
\r
186 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
187 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
190 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
191 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
192 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
194 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
195 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
196 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
198 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
200 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
201 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
204 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
205 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
206 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
208 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
210 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
211 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
212 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
214 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
215 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
217 nb.AddPage(self.directControlPanel, 'Jog')
\r
219 self.speedPanel = wx.Panel(nb)
\r
220 sizer = wx.GridBagSizer(2, 2)
\r
221 self.speedPanel.SetSizer(sizer)
\r
223 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
224 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
225 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
226 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
227 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
228 self.fillSpeedSelect.SetRange(5, 1000)
\r
229 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
230 self.supportSpeedSelect.SetRange(5, 1000)
\r
232 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
233 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
234 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
235 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
236 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
237 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
238 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
239 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
240 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
241 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
242 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
243 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
245 nb.AddPage(self.speedPanel, 'Speed')
\r
247 self.termPanel = wx.Panel(nb)
\r
248 sizer = wx.GridBagSizer(2, 2)
\r
249 self.termPanel.SetSizer(sizer)
\r
251 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
252 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
253 self.termLog.SetFont(f)
\r
254 self.termLog.SetEditable(0)
\r
255 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
256 self.termInput.SetFont(f)
\r
258 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
259 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
260 sizer.AddGrowableCol(0)
\r
261 sizer.AddGrowableRow(0)
\r
263 nb.AddPage(self.termPanel, 'Term')
\r
265 if self.cam != None:
\r
266 self.camPage = wx.Panel(nb)
\r
267 sizer = wx.GridBagSizer(2, 2)
\r
268 self.camPage.SetSizer(sizer)
\r
270 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
271 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
273 pages = self.cam.propertyPages()
\r
274 self.cam.buttons = [self.timelapsEnable]
\r
276 button = wx.Button(self.camPage, -1, page)
\r
277 button.index = pages.index(page)
\r
278 sizer.Add(button, pos=(1, pages.index(page)))
\r
279 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
280 self.cam.buttons.append(button)
\r
282 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
283 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
285 self.camPreview = wx.Panel(self.camPage)
\r
286 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
288 nb.AddPage(self.camPage, 'Camera')
\r
289 self.camPreview.timer = wx.Timer(self)
\r
290 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
291 self.camPreview.timer.Start(500)
\r
292 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
294 self.sizer.AddGrowableRow(6)
\r
295 self.sizer.AddGrowableCol(3)
\r
297 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
298 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
299 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
300 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
301 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
302 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
303 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
305 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
306 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
308 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
309 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
310 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
311 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
312 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
313 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
319 self.statsText.SetMinSize(self.statsText.GetSize())
\r
321 self.UpdateButtonStates()
\r
322 #self.UpdateProgress()
\r
324 def OnCameraTimer(self, e):
\r
325 if not self.campreviewEnable.GetValue():
\r
327 if self.machineCom != None and self.machineCom.isPrinting():
\r
329 self.cam.takeNewImage()
\r
330 self.camPreview.Refresh()
\r
332 def OnCameraEraseBackground(self, e):
\r
335 dc = wx.ClientDC(self)
\r
336 rect = self.GetUpdateRegion().GetBox()
\r
337 dc.SetClippingRect(rect)
\r
338 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
339 if self.cam.getLastImage() != None:
\r
340 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
342 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
346 def OnPropertyPageButton(self, e):
\r
347 self.cam.openPropertyPage(e.GetEventObject().index)
\r
349 def UpdateButtonStates(self):
\r
350 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
351 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
352 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
353 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
354 if self.machineCom != None and self.machineCom.isPaused():
\r
355 self.pauseButton.SetLabel('Resume')
\r
357 self.pauseButton.SetLabel('Pause')
\r
358 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
359 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
360 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
361 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
362 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
\r
363 if self.cam != None:
\r
364 for button in self.cam.buttons:
\r
365 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
367 def UpdateProgress(self):
\r
369 if self.gcode == None:
\r
370 status += "Loading gcode...\n"
\r
372 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
373 cost = self.gcode.calculateCost()
\r
375 status += "Filament cost: %s\n" % (cost)
\r
376 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
377 if self.machineCom == None or not self.machineCom.isPrinting():
\r
378 self.progress.SetValue(0)
\r
379 if self.gcodeList != None:
\r
380 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
382 printTime = self.machineCom.getPrintTime() / 60
\r
383 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
384 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
385 if self.currentZ > 0:
\r
386 status += 'Height: %0.1f\n' % (self.currentZ)
\r
387 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
388 if printTimeLeft == None:
\r
389 status += 'Print time left: Unknown\n'
\r
391 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
392 self.progress.SetValue(self.machineCom.getPrintPos())
\r
393 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
394 if self.machineCom != None:
\r
395 if self.machineCom.getTemp() > 0:
\r
396 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
397 if self.machineCom.getBedTemp() > 0:
\r
398 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
399 self.bedTemperatureLabel.Show(True)
\r
400 self.bedTemperatureSelect.Show(True)
\r
401 self.temperaturePanel.Layout()
\r
402 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
404 self.statsText.SetLabel(status.strip())
\r
406 def OnConnect(self, e):
\r
407 if self.machineCom != None:
\r
408 self.machineCom.close()
\r
409 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
410 self.UpdateButtonStates()
\r
411 taskbar.setBusy(self, True)
\r
413 def OnLoad(self, e):
\r
416 def OnPrint(self, e):
\r
417 if self.machineCom == None or not self.machineCom.isOperational():
\r
419 if self.gcodeList == None:
\r
421 if self.machineCom.isPrinting():
\r
424 if self.cam != None and self.timelapsEnable.GetValue():
\r
425 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
426 self.machineCom.printGCode(self.gcodeList)
\r
427 self.UpdateButtonStates()
\r
429 def OnCancel(self, e):
\r
430 self.pauseButton.SetLabel('Pause')
\r
431 self.machineCom.cancelPrint()
\r
432 self.machineCom.sendCommand("M84")
\r
433 self.UpdateButtonStates()
\r
435 def OnPause(self, e):
\r
436 if self.machineCom.isPaused():
\r
437 self.machineCom.setPause(False)
\r
439 self.machineCom.setPause(True)
\r
441 def OnMachineLog(self, e):
\r
442 LogWindow('\n'.join(self.machineCom.getLog()))
\r
444 def OnClose(self, e):
\r
445 global printWindowHandle
\r
446 printWindowHandle = None
\r
447 if self.machineCom != None:
\r
448 self.machineCom.close()
\r
451 def OnTempChange(self, e):
\r
452 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
454 def OnBedTempChange(self, e):
\r
455 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
457 def OnSpeedChange(self, e):
\r
458 if self.machineCom == None:
\r
460 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
461 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
462 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
463 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
465 def AddTermLog(self, line):
\r
466 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
467 l = len(self.termLog.GetValue())
\r
468 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
470 def OnTermEnterLine(self, e):
\r
471 line = self.termInput.GetValue()
\r
474 self.termLog.AppendText('>%s\n' % (line))
\r
475 self.machineCom.sendCommand(line)
\r
476 self.termHistory.append(line)
\r
477 self.termHistoryIdx = len(self.termHistory)
\r
478 self.termInput.SetValue('')
\r
480 def OnTermKey(self, e):
\r
481 if len(self.termHistory) > 0:
\r
482 if e.GetKeyCode() == wx.WXK_UP:
\r
483 self.termHistoryIdx = self.termHistoryIdx - 1
\r
484 if self.termHistoryIdx < 0:
\r
485 self.termHistoryIdx = len(self.termHistory) - 1
\r
486 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
487 if e.GetKeyCode() == wx.WXK_DOWN:
\r
488 self.termHistoryIdx = self.termHistoryIdx - 1
\r
489 if self.termHistoryIdx >= len(self.termHistory):
\r
490 self.termHistoryIdx = 0
\r
491 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
494 def OnPowerWarningChange(self, e):
\r
495 type = self.powerManagement.get_providing_power_source_type()
\r
496 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
\r
497 self.powerWarningText.Hide()
\r
498 self.panel.Layout()
\r
500 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
\r
501 self.powerWarningText.Show()
\r
502 self.panel.Layout()
\r
505 def LoadGCodeFile(self, filename):
\r
506 if self.machineCom != None and self.machineCom.isPrinting():
\r
508 #Send an initial M110 to reset the line counter to zero.
\r
509 prevLineType = lineType = 'CUSTOM'
\r
510 gcodeList = ["M110"]
\r
511 for line in open(filename, 'r'):
\r
512 if line.startswith(';TYPE:'):
\r
513 lineType = line[6:].strip()
\r
515 line = line[0:line.find(';')]
\r
516 line = line.strip()
\r
518 if prevLineType != lineType:
\r
519 gcodeList.append((line, lineType, ))
\r
521 gcodeList.append(line)
\r
522 prevLineType = lineType
\r
523 gcode = gcodeInterpreter.gcode()
\r
524 gcode.loadList(gcodeList)
\r
525 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
526 self.filename = filename
\r
528 self.gcodeList = gcodeList
\r
530 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
531 wx.CallAfter(self.UpdateButtonStates)
\r
532 wx.CallAfter(self.UpdateProgress)
\r
533 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
535 def sendLine(self, lineNr):
\r
536 if lineNr >= len(self.gcodeList):
\r
538 line = self.gcodeList[lineNr]
\r
540 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
541 n = int(re.search('S([0-9]*)', line).group(1))
\r
542 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
543 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
544 n = int(re.search('S([0-9]*)', line).group(1))
\r
545 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
547 print "Unexpected error:", sys.exc_info()
\r
548 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
549 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
552 def mcLog(self, message):
\r
556 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
557 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
558 #ToFix, this causes problems with setting the temperature with the keyboard
\r
559 #if self.temperatureSelect.GetValue() != targetTemp:
\r
560 # wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
561 #if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
562 # wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
564 def mcStateChange(self, state):
\r
565 if self.machineCom != None:
\r
566 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
567 self.cam.endTimelaps()
\r
568 if state == self.machineCom.STATE_OPERATIONAL:
\r
569 taskbar.setBusy(self, False)
\r
570 if self.machineCom.isClosedOrError():
\r
571 taskbar.setBusy(self, False)
\r
572 if self.machineCom.isPaused():
\r
573 taskbar.setPause(self, True)
\r
574 wx.CallAfter(self.UpdateButtonStates)
\r
575 wx.CallAfter(self.UpdateProgress)
\r
577 def mcMessage(self, message):
\r
578 wx.CallAfter(self.AddTermLog, message)
\r
580 def mcProgress(self, lineNr):
\r
581 wx.CallAfter(self.UpdateProgress)
\r
583 def mcZChange(self, newZ):
\r
584 self.currentZ = newZ
\r
585 if self.cam != None:
\r
586 wx.CallAfter(self.cam.takeNewImage)
\r
587 wx.CallAfter(self.camPreview.Refresh)
\r
589 class temperatureGraph(wx.Panel):
\r
590 def __init__(self, parent):
\r
591 super(temperatureGraph, self).__init__(parent)
\r
593 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
594 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
595 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
597 self.lastDraw = time.time() - 1.0
\r
599 self.backBuffer = None
\r
600 self.addPoint(0,0,0,0)
\r
601 self.SetMinSize((320,200))
\r
603 def OnEraseBackground(self, e):
\r
606 def OnSize(self, e):
\r
607 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
608 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
609 self.UpdateDrawing(True)
\r
611 def OnDraw(self, e):
\r
612 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
614 def UpdateDrawing(self, force = False):
\r
616 if not force and now - self.lastDraw < 1.0:
\r
618 self.lastDraw = now
\r
620 dc.SelectObject(self.backBuffer)
\r
622 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
\r
623 w, h = self.GetSizeTuple()
\r
624 bgLinePen = wx.Pen('#A0A0A0')
\r
625 tempPen = wx.Pen('#FF4040')
\r
626 tempSPPen = wx.Pen('#FFA0A0')
\r
627 tempPenBG = wx.Pen('#FFD0D0')
\r
628 bedTempPen = wx.Pen('#4040FF')
\r
629 bedTempSPPen = wx.Pen('#A0A0FF')
\r
630 bedTempPenBG = wx.Pen('#D0D0FF')
\r
632 #Draw the background up to the current temperatures.
\r
638 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
639 x1 = int(w - (now - t))
\r
640 for x in xrange(x0, x1 + 1):
\r
641 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
642 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
643 dc.SetPen(tempPenBG)
\r
644 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
645 dc.SetPen(bedTempPenBG)
\r
646 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
654 for x in xrange(w, 0, -30):
\r
655 dc.SetPen(bgLinePen)
\r
656 dc.DrawLine(x, 0, x, h)
\r
658 for y in xrange(h-1, 0, -h * 50 / 300):
\r
659 dc.SetPen(bgLinePen)
\r
660 dc.DrawLine(0, y, w, y)
\r
661 dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
\r
663 dc.DrawLine(0, 0, w, 0)
\r
664 dc.DrawLine(0, 0, 0, h)
\r
666 #Draw the main lines
\r
672 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
673 x1 = int(w - (now - t))
\r
674 for x in xrange(x0, x1 + 1):
\r
675 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
676 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
677 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
678 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
679 dc.SetPen(tempSPPen)
\r
680 dc.DrawPoint(x, h - (tSP * h / 300))
\r
681 dc.SetPen(bedTempSPPen)
\r
682 dc.DrawPoint(x, h - (btSP * h / 300))
\r
684 dc.DrawPoint(x, h - (t * h / 300))
\r
685 dc.SetPen(bedTempPen)
\r
686 dc.DrawPoint(x, h - (bt * h / 300))
\r
694 self.Refresh(eraseBackground=False)
\r
697 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
700 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
701 if bedTemp == None:
\r
703 if bedTempSP == None:
\r
705 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
706 wx.CallAfter(self.UpdateDrawing)
\r
708 class LogWindow(wx.Frame):
\r
709 def __init__(self, logText):
\r
710 super(LogWindow, self).__init__(None, title="Machine log")
\r
711 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
712 self.SetSize((500,400))
\r