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 p = power.PowerManagement()
\r
122 if p.get_providing_power_source_type() != power.POWER_TYPE_AC:
\r
123 self.powerWarningText = wx.StaticText(parent=self.panel,
\r
125 label="Connect your computer to AC power\nIf it shuts down during printing, the product will be lost.",
\r
126 style=wx.ALIGN_CENTER)
\r
127 self.powerWarningText.SetBackgroundColour('red')
\r
128 self.powerWarningText.SetForegroundColour('white')
\r
129 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
\r
131 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
\r
132 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
\r
134 self.sizer.Add(boxsizer, pos=(0,0), span=(7,1), flag=wx.EXPAND)
\r
136 self.connectButton = wx.Button(self.panel, -1, 'Connect')
\r
137 #self.loadButton = wx.Button(self.panel, -1, 'Load')
\r
138 self.printButton = wx.Button(self.panel, -1, 'Print')
\r
139 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
\r
140 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
\r
141 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
\r
142 self.progress = wx.Gauge(self.panel, -1)
\r
144 self.sizer.Add(self.connectButton, pos=(1,1), flag=wx.EXPAND)
\r
145 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
\r
146 self.sizer.Add(self.printButton, pos=(2,1), flag=wx.EXPAND)
\r
147 self.sizer.Add(self.pauseButton, pos=(3,1), flag=wx.EXPAND)
\r
148 self.sizer.Add(self.cancelButton, pos=(4,1), flag=wx.EXPAND)
\r
149 self.sizer.Add(self.machineLogButton, pos=(5,1), flag=wx.EXPAND)
\r
150 self.sizer.Add(self.progress, pos=(7,0), span=(1,7), flag=wx.EXPAND)
\r
152 nb = wx.Notebook(self.panel)
\r
153 self.sizer.Add(nb, pos=(0,2), span=(7,4), flag=wx.EXPAND)
\r
155 self.temperaturePanel = wx.Panel(nb)
\r
156 sizer = wx.GridBagSizer(2, 2)
\r
157 self.temperaturePanel.SetSizer(sizer)
\r
159 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
160 self.temperatureSelect.SetRange(0, 400)
\r
161 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
\r
162 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
163 self.bedTemperatureSelect.SetRange(0, 400)
\r
164 self.bedTemperatureLabel.Show(False)
\r
165 self.bedTemperatureSelect.Show(False)
\r
167 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
\r
169 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))
\r
170 sizer.Add(self.temperatureSelect, pos=(0,1))
\r
171 sizer.Add(self.bedTemperatureLabel, pos=(1,0))
\r
172 sizer.Add(self.bedTemperatureSelect, pos=(1,1))
\r
173 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
174 sizer.AddGrowableRow(2)
\r
175 sizer.AddGrowableCol(1)
\r
177 nb.AddPage(self.temperaturePanel, 'Temp')
\r
179 self.directControlPanel = wx.Panel(nb)
\r
181 sizer = wx.GridBagSizer(2, 2)
\r
182 self.directControlPanel.SetSizer(sizer)
\r
183 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3))
\r
184 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3))
\r
185 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3))
\r
187 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3))
\r
188 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3))
\r
189 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3))
\r
191 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0))
\r
192 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1))
\r
193 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2))
\r
195 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3))
\r
197 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4))
\r
198 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5))
\r
199 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6))
\r
201 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8))
\r
202 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8))
\r
203 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8))
\r
205 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8))
\r
207 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8))
\r
208 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8))
\r
209 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8))
\r
211 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,10), span=(1,3), flag=wx.EXPAND)
\r
212 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
214 nb.AddPage(self.directControlPanel, 'Jog')
\r
216 self.speedPanel = wx.Panel(nb)
\r
217 sizer = wx.GridBagSizer(2, 2)
\r
218 self.speedPanel.SetSizer(sizer)
\r
220 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
221 self.outerWallSpeedSelect.SetRange(5, 1000)
\r
222 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
223 self.innerWallSpeedSelect.SetRange(5, 1000)
\r
224 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
225 self.fillSpeedSelect.SetRange(5, 1000)
\r
226 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)
\r
227 self.supportSpeedSelect.SetRange(5, 1000)
\r
229 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))
\r
230 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))
\r
231 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))
\r
232 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))
\r
233 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))
\r
234 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))
\r
235 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))
\r
236 sizer.Add(self.fillSpeedSelect, pos=(2,1))
\r
237 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))
\r
238 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))
\r
239 sizer.Add(self.supportSpeedSelect, pos=(3,1))
\r
240 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))
\r
242 nb.AddPage(self.speedPanel, 'Speed')
\r
244 self.termPanel = wx.Panel(nb)
\r
245 sizer = wx.GridBagSizer(2, 2)
\r
246 self.termPanel.SetSizer(sizer)
\r
248 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
\r
249 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
\r
250 self.termLog.SetFont(f)
\r
251 self.termLog.SetEditable(0)
\r
252 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
\r
253 self.termInput.SetFont(f)
\r
255 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)
\r
256 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)
\r
257 sizer.AddGrowableCol(0)
\r
258 sizer.AddGrowableRow(0)
\r
260 nb.AddPage(self.termPanel, 'Term')
\r
262 if self.cam != None:
\r
263 self.camPage = wx.Panel(nb)
\r
264 sizer = wx.GridBagSizer(2, 2)
\r
265 self.camPage.SetSizer(sizer)
\r
267 self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
\r
268 sizer.Add(self.timelapsEnable, pos=(0,0), span=(1,2), flag=wx.EXPAND)
\r
270 pages = self.cam.propertyPages()
\r
271 self.cam.buttons = [self.timelapsEnable]
\r
273 button = wx.Button(self.camPage, -1, page)
\r
274 button.index = pages.index(page)
\r
275 sizer.Add(button, pos=(1, pages.index(page)))
\r
276 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
\r
277 self.cam.buttons.append(button)
\r
279 self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
\r
280 sizer.Add(self.campreviewEnable, pos=(2,0), span=(1,2), flag=wx.EXPAND)
\r
282 self.camPreview = wx.Panel(self.camPage)
\r
283 sizer.Add(self.camPreview, pos=(3,0), span=(1,2), flag=wx.EXPAND)
\r
285 nb.AddPage(self.camPage, 'Camera')
\r
286 self.camPreview.timer = wx.Timer(self)
\r
287 self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
\r
288 self.camPreview.timer.Start(500)
\r
289 self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
\r
291 self.sizer.AddGrowableRow(5)
\r
292 self.sizer.AddGrowableCol(3)
\r
294 self.Bind(wx.EVT_CLOSE, self.OnClose)
\r
295 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
\r
296 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
\r
297 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
\r
298 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
\r
299 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
\r
300 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
\r
302 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
\r
303 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
\r
305 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
\r
306 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
\r
307 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
\r
308 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
\r
309 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
\r
310 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
\r
316 self.statsText.SetMinSize(self.statsText.GetSize())
\r
318 self.UpdateButtonStates()
\r
319 #self.UpdateProgress()
\r
321 def OnCameraTimer(self, e):
\r
322 if not self.campreviewEnable.GetValue():
\r
324 if self.machineCom != None and self.machineCom.isPrinting():
\r
326 self.cam.takeNewImage()
\r
327 self.camPreview.Refresh()
\r
329 def OnCameraEraseBackground(self, e):
\r
332 dc = wx.ClientDC(self)
\r
333 rect = self.GetUpdateRegion().GetBox()
\r
334 dc.SetClippingRect(rect)
\r
335 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
\r
336 if self.cam.getLastImage() != None:
\r
337 self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
\r
339 dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
\r
343 def OnPropertyPageButton(self, e):
\r
344 self.cam.openPropertyPage(e.GetEventObject().index)
\r
346 def UpdateButtonStates(self):
\r
347 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
\r
348 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
349 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
350 self.pauseButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
351 if self.machineCom != None and self.machineCom.isPaused():
\r
352 self.pauseButton.SetLabel('Resume')
\r
354 self.pauseButton.SetLabel('Pause')
\r
355 self.cancelButton.Enable(self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
\r
356 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
357 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
\r
358 self.directControlPanel.Enable(self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
\r
359 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
\r
360 if self.cam != None:
\r
361 for button in self.cam.buttons:
\r
362 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
\r
364 def UpdateProgress(self):
\r
366 if self.gcode == None:
\r
367 status += "Loading gcode...\n"
\r
369 status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
\r
370 cost = self.gcode.calculateCost()
\r
372 status += "Filament cost: %s\n" % (cost)
\r
373 status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
\r
374 if self.machineCom == None or not self.machineCom.isPrinting():
\r
375 self.progress.SetValue(0)
\r
376 if self.gcodeList != None:
\r
377 status += 'Line: -/%d\n' % (len(self.gcodeList))
\r
379 printTime = self.machineCom.getPrintTime() / 60
\r
380 printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
\r
381 status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
\r
382 if self.currentZ > 0:
\r
383 status += 'Height: %0.1f\n' % (self.currentZ)
\r
384 status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
\r
385 if printTimeLeft == None:
\r
386 status += 'Print time left: Unknown\n'
\r
388 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
\r
389 self.progress.SetValue(self.machineCom.getPrintPos())
\r
390 taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
\r
391 if self.machineCom != None:
\r
392 if self.machineCom.getTemp() > 0:
\r
393 status += 'Temp: %d\n' % (self.machineCom.getTemp())
\r
394 if self.machineCom.getBedTemp() > 0:
\r
395 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
\r
396 self.bedTemperatureLabel.Show(True)
\r
397 self.bedTemperatureSelect.Show(True)
\r
398 self.temperaturePanel.Layout()
\r
399 status += 'Machine state:%s\n' % (self.machineCom.getStateString())
\r
401 self.statsText.SetLabel(status.strip())
\r
403 def OnConnect(self, e):
\r
404 if self.machineCom != None:
\r
405 self.machineCom.close()
\r
406 self.machineCom = machineCom.MachineCom(callbackObject=self)
\r
407 self.UpdateButtonStates()
\r
408 taskbar.setBusy(self, True)
\r
410 def OnLoad(self, e):
\r
413 def OnPrint(self, e):
\r
414 if self.machineCom == None or not self.machineCom.isOperational():
\r
416 if self.gcodeList == None:
\r
418 if self.machineCom.isPrinting():
\r
421 if self.cam != None and self.timelapsEnable.GetValue():
\r
422 self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
\r
423 self.machineCom.printGCode(self.gcodeList)
\r
424 self.UpdateButtonStates()
\r
426 def OnCancel(self, e):
\r
427 self.pauseButton.SetLabel('Pause')
\r
428 self.machineCom.cancelPrint()
\r
429 self.machineCom.sendCommand("M84")
\r
430 self.UpdateButtonStates()
\r
432 def OnPause(self, e):
\r
433 if self.machineCom.isPaused():
\r
434 self.machineCom.setPause(False)
\r
436 self.machineCom.setPause(True)
\r
438 def OnMachineLog(self, e):
\r
439 LogWindow('\n'.join(self.machineCom.getLog()))
\r
441 def OnClose(self, e):
\r
442 global printWindowHandle
\r
443 printWindowHandle = None
\r
444 if self.machineCom != None:
\r
445 self.machineCom.close()
\r
448 def OnTempChange(self, e):
\r
449 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
\r
451 def OnBedTempChange(self, e):
\r
452 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
\r
454 def OnSpeedChange(self, e):
\r
455 if self.machineCom == None:
\r
457 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
\r
458 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
\r
459 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
\r
460 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
\r
462 def AddTermLog(self, line):
\r
463 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
\r
464 l = len(self.termLog.GetValue())
\r
465 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
\r
467 def OnTermEnterLine(self, e):
\r
468 line = self.termInput.GetValue()
\r
471 self.termLog.AppendText('>%s\n' % (line))
\r
472 self.machineCom.sendCommand(line)
\r
473 self.termHistory.append(line)
\r
474 self.termHistoryIdx = len(self.termHistory)
\r
475 self.termInput.SetValue('')
\r
477 def OnTermKey(self, e):
\r
478 if len(self.termHistory) > 0:
\r
479 if e.GetKeyCode() == wx.WXK_UP:
\r
480 self.termHistoryIdx = self.termHistoryIdx - 1
\r
481 if self.termHistoryIdx < 0:
\r
482 self.termHistoryIdx = len(self.termHistory) - 1
\r
483 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
484 if e.GetKeyCode() == wx.WXK_DOWN:
\r
485 self.termHistoryIdx = self.termHistoryIdx - 1
\r
486 if self.termHistoryIdx >= len(self.termHistory):
\r
487 self.termHistoryIdx = 0
\r
488 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
\r
491 def LoadGCodeFile(self, filename):
\r
492 if self.machineCom != None and self.machineCom.isPrinting():
\r
494 #Send an initial M110 to reset the line counter to zero.
\r
495 prevLineType = lineType = 'CUSTOM'
\r
496 gcodeList = ["M110"]
\r
497 for line in open(filename, 'r'):
\r
498 if line.startswith(';TYPE:'):
\r
499 lineType = line[6:].strip()
\r
501 line = line[0:line.find(';')]
\r
502 line = line.strip()
\r
504 if prevLineType != lineType:
\r
505 gcodeList.append((line, lineType, ))
\r
507 gcodeList.append(line)
\r
508 prevLineType = lineType
\r
509 gcode = gcodeInterpreter.gcode()
\r
510 gcode.loadList(gcodeList)
\r
511 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
\r
512 self.filename = filename
\r
514 self.gcodeList = gcodeList
\r
516 wx.CallAfter(self.progress.SetRange, len(gcodeList))
\r
517 wx.CallAfter(self.UpdateButtonStates)
\r
518 wx.CallAfter(self.UpdateProgress)
\r
519 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
\r
521 def sendLine(self, lineNr):
\r
522 if lineNr >= len(self.gcodeList):
\r
524 line = self.gcodeList[lineNr]
\r
526 if ('M104' in line or 'M109' in line) and 'S' in line:
\r
527 n = int(re.search('S([0-9]*)', line).group(1))
\r
528 wx.CallAfter(self.temperatureSelect.SetValue, n)
\r
529 if ('M140' in line or 'M190' in line) and 'S' in line:
\r
530 n = int(re.search('S([0-9]*)', line).group(1))
\r
531 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
\r
533 print "Unexpected error:", sys.exc_info()
\r
534 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
\r
535 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
\r
538 def mcLog(self, message):
\r
542 def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
\r
543 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
\r
544 if self.temperatureSelect.GetValue() != targetTemp:
\r
545 wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
\r
546 if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
\r
547 wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
\r
549 def mcStateChange(self, state):
\r
550 if self.machineCom != None:
\r
551 if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
\r
552 self.cam.endTimelaps()
\r
553 if state == self.machineCom.STATE_OPERATIONAL:
\r
554 taskbar.setBusy(self, False)
\r
555 if self.machineCom.isClosedOrError():
\r
556 taskbar.setBusy(self, False)
\r
557 if self.machineCom.isPaused():
\r
558 taskbar.setPause(self, True)
\r
559 wx.CallAfter(self.UpdateButtonStates)
\r
560 wx.CallAfter(self.UpdateProgress)
\r
562 def mcMessage(self, message):
\r
563 wx.CallAfter(self.AddTermLog, message)
\r
565 def mcProgress(self, lineNr):
\r
566 wx.CallAfter(self.UpdateProgress)
\r
568 def mcZChange(self, newZ):
\r
569 self.currentZ = newZ
\r
570 if self.cam != None:
\r
571 wx.CallAfter(self.cam.takeNewImage)
\r
572 wx.CallAfter(self.camPreview.Refresh)
\r
574 class temperatureGraph(wx.Panel):
\r
575 def __init__(self, parent):
\r
576 super(temperatureGraph, self).__init__(parent)
\r
578 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
\r
579 self.Bind(wx.EVT_SIZE, self.OnSize)
\r
580 self.Bind(wx.EVT_PAINT, self.OnDraw)
\r
582 self.lastDraw = time.time() - 1.0
\r
584 self.backBuffer = None
\r
585 self.addPoint(0,0,0,0)
\r
586 self.SetMinSize((320,200))
\r
588 def OnEraseBackground(self, e):
\r
591 def OnSize(self, e):
\r
592 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
\r
593 self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
\r
594 self.UpdateDrawing(True)
\r
596 def OnDraw(self, e):
\r
597 dc = wx.BufferedPaintDC(self, self.backBuffer)
\r
599 def UpdateDrawing(self, force = False):
\r
601 if not force and now - self.lastDraw < 1.0:
\r
603 self.lastDraw = now
\r
605 dc.SelectObject(self.backBuffer)
\r
607 w, h = self.GetSizeTuple()
\r
608 bgLinePen = wx.Pen('#A0A0A0')
\r
609 tempPen = wx.Pen('#FF4040')
\r
610 tempSPPen = wx.Pen('#FFA0A0')
\r
611 tempPenBG = wx.Pen('#FFD0D0')
\r
612 bedTempPen = wx.Pen('#4040FF')
\r
613 bedTempSPPen = wx.Pen('#A0A0FF')
\r
614 bedTempPenBG = wx.Pen('#D0D0FF')
\r
616 #Draw the background up to the current temperatures.
\r
622 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
623 x1 = int(w - (now - t))
\r
624 for x in xrange(x0, x1 + 1):
\r
625 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
626 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
627 dc.SetPen(tempPenBG)
\r
628 dc.DrawLine(x, h, x, h - (t * h / 300))
\r
629 dc.SetPen(bedTempPenBG)
\r
630 dc.DrawLine(x, h, x, h - (bt * h / 300))
\r
638 for x in xrange(w, 0, -30):
\r
639 dc.SetPen(bgLinePen)
\r
640 dc.DrawLine(x, 0, x, h)
\r
641 for y in xrange(h-1, 0, -h * 50 / 300):
\r
642 dc.SetPen(bgLinePen)
\r
643 dc.DrawLine(0, y, w, y)
\r
644 dc.DrawLine(0, 0, w, 0)
\r
645 dc.DrawLine(0, 0, 0, h)
\r
647 #Draw the main lines
\r
653 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
\r
654 x1 = int(w - (now - t))
\r
655 for x in xrange(x0, x1 + 1):
\r
656 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
\r
657 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
\r
658 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
\r
659 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
\r
660 dc.SetPen(tempSPPen)
\r
661 dc.DrawPoint(x, h - (tSP * h / 300))
\r
662 dc.SetPen(bedTempSPPen)
\r
663 dc.DrawPoint(x, h - (btSP * h / 300))
\r
665 dc.DrawPoint(x, h - (t * h / 300))
\r
666 dc.SetPen(bedTempPen)
\r
667 dc.DrawPoint(x, h - (bt * h / 300))
\r
675 self.Refresh(eraseBackground=False)
\r
678 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
\r
681 def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
\r
682 if bedTemp == None:
\r
684 if bedTempSP == None:
\r
686 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
\r
687 wx.CallAfter(self.UpdateDrawing)
\r
689 class LogWindow(wx.Frame):
\r
690 def __init__(self, logText):
\r
691 super(LogWindow, self).__init__(None, title="Machine log")
\r
692 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY)
\r
693 self.SetSize((500,400))
\r