chiark / gitweb /
Fixed the imports.
[cura.git] / Cura / gui / printWindow.py
1 # coding=utf-8
2 from __future__ import absolute_import
3
4 import threading
5 import re
6 import subprocess
7 import sys
8 import time
9 import platform
10
11 import wx
12 from wx.lib import buttons
13
14 from Cura.gui import webcam
15 from Cura.gui import taskbar
16 from Cura.util import machineCom
17 from Cura.util import gcodeInterpreter
18 from Cura.util import power
19 from Cura.util.resources import getPathForImage
20
21 printWindowMonitorHandle = None
22
23 def printFile(filename):
24         global printWindowMonitorHandle
25         if printWindowMonitorHandle == None:
26                 printWindowMonitorHandle = printProcessMonitor()
27         printWindowMonitorHandle.loadFile(filename)
28
29
30 def startPrintInterface(filename):
31         #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
32         # 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).
33         app = wx.App(False)
34         printWindowHandle = printWindow()
35         printWindowHandle.Show(True)
36         printWindowHandle.Raise()
37         printWindowHandle.OnConnect(None)
38         t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
39         t.daemon = True
40         t.start()
41         app.MainLoop()
42
43
44 class printProcessMonitor():
45         def __init__(self):
46                 self.handle = None
47
48         def loadFile(self, filename):
49                 if self.handle == None:
50                         cmdList = [sys.executable, sys.argv[0], '-r', filename]
51                         if platform.system() == "Darwin":
52                                 if platform.machine() == 'i386':
53                                         cmdList.insert(0, 'arch')
54                                         cmdList.insert(1, '-i386')
55                         self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
56                                 stderr=subprocess.PIPE)
57                         self.thread = threading.Thread(target=self.Monitor)
58                         self.thread.start()
59                 else:
60                         self.handle.stdin.write(filename + '\n')
61
62         def Monitor(self):
63                 p = self.handle
64                 line = p.stdout.readline()
65                 while(len(line) > 0):
66                         #print line.rstrip()
67                         line = p.stdout.readline()
68                 p.communicate()
69                 self.handle = None
70                 self.thread = None
71
72
73 class PrintCommandButton(buttons.GenBitmapButton):
74         def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
75                 self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
76                 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
77
78                 self.commandList = commandList
79                 self.parent = parent
80
81                 self.SetBezelWidth(1)
82                 self.SetUseFocusIndicator(False)
83
84                 self.Bind(wx.EVT_BUTTON, self.OnClick)
85
86         def OnClick(self, e):
87                 if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
88                         return;
89                 for cmd in self.commandList:
90                         self.parent.machineCom.sendCommand(cmd)
91                 e.Skip()
92
93
94 class printWindow(wx.Frame):
95         "Main user interface window"
96
97         def __init__(self):
98                 super(printWindow, self).__init__(None, -1, title='Printing')
99                 self.machineCom = None
100                 self.gcode = None
101                 self.gcodeList = None
102                 self.sendList = []
103                 self.temp = None
104                 self.bedTemp = None
105                 self.bufferLineCount = 4
106                 self.sendCnt = 0
107                 self.feedrateRatioOuterWall = 1.0
108                 self.feedrateRatioInnerWall = 1.0
109                 self.feedrateRatioFill = 1.0
110                 self.feedrateRatioSupport = 1.0
111                 self.pause = False
112                 self.termHistory = []
113                 self.termHistoryIdx = 0
114
115                 self.cam = None
116                 if webcam.hasWebcamSupport():
117                         self.cam = webcam.webcam()
118                         if not self.cam.hasCamera():
119                                 self.cam = None
120
121                 #self.SetIcon(icon.getMainIcon())
122
123                 self.SetSizer(wx.BoxSizer())
124                 self.panel = wx.Panel(self)
125                 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
126                 self.sizer = wx.GridBagSizer(2, 2)
127                 self.panel.SetSizer(self.sizer)
128
129                 sb = wx.StaticBox(self.panel, label="Statistics")
130                 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
131
132                 self.powerWarningText = wx.StaticText(parent=self.panel,
133                         id=-1,
134                         label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
135                         style=wx.ALIGN_CENTER)
136                 self.powerWarningText.SetBackgroundColour('red')
137                 self.powerWarningText.SetForegroundColour('white')
138                 boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10))
139                 self.powerManagement = power.PowerManagement()
140                 self.powerWarningTimer = wx.Timer(self)
141                 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
142                 self.OnPowerWarningChange(None)
143                 self.powerWarningTimer.Start(10000)
144
145                 self.statsText = wx.StaticText(self.panel, -1,
146                         "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
147                 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
148
149                 self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
150
151                 self.connectButton = wx.Button(self.panel, -1, 'Connect')
152                 #self.loadButton = wx.Button(self.panel, -1, 'Load')
153                 self.printButton = wx.Button(self.panel, -1, 'Print')
154                 self.pauseButton = wx.Button(self.panel, -1, 'Pause')
155                 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
156                 self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
157                 self.progress = wx.Gauge(self.panel, -1)
158
159                 self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
160                 #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND)
161                 self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND)
162                 self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND)
163                 self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND)
164                 self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND)
165                 self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
166
167                 nb = wx.Notebook(self.panel)
168                 self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
169
170                 self.temperaturePanel = wx.Panel(nb)
171                 sizer = wx.GridBagSizer(2, 2)
172                 self.temperaturePanel.SetSizer(sizer)
173
174                 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
175                 self.temperatureSelect.SetRange(0, 400)
176                 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
177                 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
178                         style=wx.SP_ARROW_KEYS)
179                 self.bedTemperatureSelect.SetRange(0, 400)
180                 self.bedTemperatureLabel.Show(False)
181                 self.bedTemperatureSelect.Show(False)
182
183                 self.temperatureGraph = temperatureGraph(self.temperaturePanel)
184
185                 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
186                 sizer.Add(self.temperatureSelect, pos=(0, 1))
187                 sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
188                 sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
189                 sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
190                 sizer.AddGrowableRow(2)
191                 sizer.AddGrowableCol(1)
192
193                 nb.AddPage(self.temperaturePanel, 'Temp')
194
195                 self.directControlPanel = wx.Panel(nb)
196
197                 sizer = wx.GridBagSizer(2, 2)
198                 self.directControlPanel.SetSizer(sizer)
199                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3))
200                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3))
201                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3))
202
203                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3))
204                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3))
205                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3))
206
207                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0))
208                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1))
209                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2))
210
211                 sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3))
212
213                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4))
214                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5))
215                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6))
216
217                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8))
218                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8))
219                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8))
220
221                 sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8))
222
223                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8))
224                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8))
225                 sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8))
226
227                 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 10),
228                         span=(1, 3), flag=wx.EXPAND)
229                 sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
230                         span=(1, 3), flag=wx.EXPAND)
231
232                 nb.AddPage(self.directControlPanel, 'Jog')
233
234                 self.speedPanel = wx.Panel(nb)
235                 sizer = wx.GridBagSizer(2, 2)
236                 self.speedPanel.SetSizer(sizer)
237
238                 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
239                 self.outerWallSpeedSelect.SetRange(5, 1000)
240                 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
241                 self.innerWallSpeedSelect.SetRange(5, 1000)
242                 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
243                 self.fillSpeedSelect.SetRange(5, 1000)
244                 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
245                 self.supportSpeedSelect.SetRange(5, 1000)
246
247                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
248                 sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
249                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
250                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
251                 sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
252                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
253                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
254                 sizer.Add(self.fillSpeedSelect, pos=(2, 1))
255                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
256                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
257                 sizer.Add(self.supportSpeedSelect, pos=(3, 1))
258                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
259
260                 nb.AddPage(self.speedPanel, 'Speed')
261
262                 self.termPanel = wx.Panel(nb)
263                 sizer = wx.GridBagSizer(2, 2)
264                 self.termPanel.SetSizer(sizer)
265
266                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
267                 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
268                 self.termLog.SetFont(f)
269                 self.termLog.SetEditable(0)
270                 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)
271                 self.termInput.SetFont(f)
272
273                 sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND)
274                 sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND)
275                 sizer.AddGrowableCol(0)
276                 sizer.AddGrowableRow(0)
277
278                 nb.AddPage(self.termPanel, 'Term')
279
280                 if self.cam != None:
281                         self.camPage = wx.Panel(nb)
282                         sizer = wx.GridBagSizer(2, 2)
283                         self.camPage.SetSizer(sizer)
284
285                         self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
286                         sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
287
288                         pages = self.cam.propertyPages()
289                         self.cam.buttons = [self.timelapsEnable]
290                         for page in pages:
291                                 button = wx.Button(self.camPage, -1, page)
292                                 button.index = pages.index(page)
293                                 sizer.Add(button, pos=(1, pages.index(page)))
294                                 button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
295                                 self.cam.buttons.append(button)
296
297                         self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
298                         sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
299
300                         self.camPreview = wx.Panel(self.camPage)
301                         sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
302
303                         nb.AddPage(self.camPage, 'Camera')
304                         self.camPreview.timer = wx.Timer(self)
305                         self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
306                         self.camPreview.timer.Start(500)
307                         self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
308
309                 self.sizer.AddGrowableRow(6)
310                 self.sizer.AddGrowableCol(3)
311
312                 self.Bind(wx.EVT_CLOSE, self.OnClose)
313                 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
314                 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
315                 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
316                 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
317                 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
318                 self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
319
320                 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
321                 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
322
323                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)
324                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)
325                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)
326                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)
327                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)
328                 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
329
330                 self.Layout()
331                 self.Fit()
332                 self.Centre()
333
334                 self.statsText.SetMinSize(self.statsText.GetSize())
335
336                 self.UpdateButtonStates()
337
338         #self.UpdateProgress()
339
340         def OnCameraTimer(self, e):
341                 if not self.campreviewEnable.GetValue():
342                         return
343                 if self.machineCom != None and self.machineCom.isPrinting():
344                         return
345                 self.cam.takeNewImage()
346                 self.camPreview.Refresh()
347
348         def OnCameraEraseBackground(self, e):
349                 dc = e.GetDC()
350                 if not dc:
351                         dc = wx.ClientDC(self)
352                         rect = self.GetUpdateRegion().GetBox()
353                         dc.SetClippingRect(rect)
354                 dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
355                 if self.cam.getLastImage() != None:
356                         self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
357                         self.camPage.Fit()
358                         dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
359                 else:
360                         dc.Clear()
361
362         def OnPropertyPageButton(self, e):
363                 self.cam.openPropertyPage(e.GetEventObject().index)
364
365         def UpdateButtonStates(self):
366                 self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
367                 #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
368                 self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
369                 self.machineCom.isPrinting() or self.machineCom.isPaused()))
370                 self.pauseButton.Enable(
371                         self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
372                 if self.machineCom != None and self.machineCom.isPaused():
373                         self.pauseButton.SetLabel('Resume')
374                 else:
375                         self.pauseButton.SetLabel('Pause')
376                 self.cancelButton.Enable(
377                         self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
378                 self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
379                 self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
380                 self.directControlPanel.Enable(
381                         self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
382                 self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
383                 if self.cam != None:
384                         for button in self.cam.buttons:
385                                 button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
386
387         def UpdateProgress(self):
388                 status = ""
389                 if self.gcode == None:
390                         status += "Loading gcode...\n"
391                 else:
392                         status += "Filament: %.2fm %.2fg\n" % (
393                         self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
394                         cost = self.gcode.calculateCost()
395                         if cost != False:
396                                 status += "Filament cost: %s\n" % (cost)
397                         status += "Estimated print time: %02d:%02d\n" % (
398                         int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
399                 if self.machineCom == None or not self.machineCom.isPrinting():
400                         self.progress.SetValue(0)
401                         if self.gcodeList != None:
402                                 status += 'Line: -/%d\n' % (len(self.gcodeList))
403                 else:
404                         printTime = self.machineCom.getPrintTime() / 60
405                         printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate()
406                         status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList),
407                                                           self.machineCom.getPrintPos() * 100 / len(self.gcodeList))
408                         if self.currentZ > 0:
409                                 status += 'Height: %0.1f\n' % (self.currentZ)
410                         status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
411                         if printTimeLeft == None:
412                                 status += 'Print time left: Unknown\n'
413                         else:
414                                 status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
415                         self.progress.SetValue(self.machineCom.getPrintPos())
416                         taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
417                 if self.machineCom != None:
418                         if self.machineCom.getTemp() > 0:
419                                 status += 'Temp: %d\n' % (self.machineCom.getTemp())
420                         if self.machineCom.getBedTemp() > 0:
421                                 status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
422                                 self.bedTemperatureLabel.Show(True)
423                                 self.bedTemperatureSelect.Show(True)
424                                 self.temperaturePanel.Layout()
425                         status += 'Machine state:%s\n' % (self.machineCom.getStateString())
426
427                 self.statsText.SetLabel(status.strip())
428
429         def OnConnect(self, e):
430                 if self.machineCom != None:
431                         self.machineCom.close()
432                 self.machineCom = machineCom.MachineCom(callbackObject=self)
433                 self.UpdateButtonStates()
434                 taskbar.setBusy(self, True)
435
436         def OnLoad(self, e):
437                 pass
438
439         def OnPrint(self, e):
440                 if self.machineCom == None or not self.machineCom.isOperational():
441                         return
442                 if self.gcodeList == None:
443                         return
444                 if self.machineCom.isPrinting():
445                         return
446                 self.currentZ = -1
447                 if self.cam != None and self.timelapsEnable.GetValue():
448                         self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
449                 self.machineCom.printGCode(self.gcodeList)
450                 self.UpdateButtonStates()
451
452         def OnCancel(self, e):
453                 self.pauseButton.SetLabel('Pause')
454                 self.machineCom.cancelPrint()
455                 self.machineCom.sendCommand("M84")
456                 self.UpdateButtonStates()
457
458         def OnPause(self, e):
459                 if self.machineCom.isPaused():
460                         self.machineCom.setPause(False)
461                 else:
462                         self.machineCom.setPause(True)
463
464         def OnMachineLog(self, e):
465                 LogWindow('\n'.join(self.machineCom.getLog()))
466
467         def OnClose(self, e):
468                 global printWindowHandle
469                 printWindowHandle = None
470                 if self.machineCom != None:
471                         self.machineCom.close()
472                 self.Destroy()
473
474         def OnTempChange(self, e):
475                 self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))
476
477         def OnBedTempChange(self, e):
478                 self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
479
480         def OnSpeedChange(self, e):
481                 if self.machineCom == None:
482                         return
483                 self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
484                 self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
485                 self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0)
486                 self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
487
488         def AddTermLog(self, line):
489                 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
490                 l = len(self.termLog.GetValue())
491                 self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
492
493         def OnTermEnterLine(self, e):
494                 line = self.termInput.GetValue()
495                 if line == '':
496                         return
497                 self.termLog.AppendText('>%s\n' % (line))
498                 self.machineCom.sendCommand(line)
499                 self.termHistory.append(line)
500                 self.termHistoryIdx = len(self.termHistory)
501                 self.termInput.SetValue('')
502
503         def OnTermKey(self, e):
504                 if len(self.termHistory) > 0:
505                         if e.GetKeyCode() == wx.WXK_UP:
506                                 self.termHistoryIdx = self.termHistoryIdx - 1
507                                 if self.termHistoryIdx < 0:
508                                         self.termHistoryIdx = len(self.termHistory) - 1
509                                 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
510                         if e.GetKeyCode() == wx.WXK_DOWN:
511                                 self.termHistoryIdx = self.termHistoryIdx - 1
512                                 if self.termHistoryIdx >= len(self.termHistory):
513                                         self.termHistoryIdx = 0
514                                 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
515                 e.Skip()
516
517         def OnPowerWarningChange(self, e):
518                 type = self.powerManagement.get_providing_power_source_type()
519                 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
520                         self.powerWarningText.Hide()
521                         self.panel.Layout()
522                         self.Layout()
523                 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
524                         self.powerWarningText.Show()
525                         self.panel.Layout()
526                         self.Layout()
527
528         def LoadGCodeFile(self, filename):
529                 if self.machineCom != None and self.machineCom.isPrinting():
530                         return
531                 #Send an initial M110 to reset the line counter to zero.
532                 prevLineType = lineType = 'CUSTOM'
533                 gcodeList = ["M110"]
534                 for line in open(filename, 'r'):
535                         if line.startswith(';TYPE:'):
536                                 lineType = line[6:].strip()
537                         if ';' in line:
538                                 line = line[0:line.find(';')]
539                         line = line.strip()
540                         if len(line) > 0:
541                                 if prevLineType != lineType:
542                                         gcodeList.append((line, lineType, ))
543                                 else:
544                                         gcodeList.append(line)
545                                 prevLineType = lineType
546                 gcode = gcodeInterpreter.gcode()
547                 gcode.loadList(gcodeList)
548                 #print "Loaded: %s (%d)" % (filename, len(gcodeList))
549                 self.filename = filename
550                 self.gcode = gcode
551                 self.gcodeList = gcodeList
552
553                 wx.CallAfter(self.progress.SetRange, len(gcodeList))
554                 wx.CallAfter(self.UpdateButtonStates)
555                 wx.CallAfter(self.UpdateProgress)
556                 wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
557
558         def sendLine(self, lineNr):
559                 if lineNr >= len(self.gcodeList):
560                         return False
561                 line = self.gcodeList[lineNr]
562                 try:
563                         if ('M104' in line or 'M109' in line) and 'S' in line:
564                                 n = int(re.search('S([0-9]*)', line).group(1))
565                                 wx.CallAfter(self.temperatureSelect.SetValue, n)
566                         if ('M140' in line or 'M190' in line) and 'S' in line:
567                                 n = int(re.search('S([0-9]*)', line).group(1))
568                                 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)
569                 except:
570                         print "Unexpected error:", sys.exc_info()
571                 checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line)))
572                 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))
573                 return True
574
575         def mcLog(self, message):
576                 #print message
577                 pass
578
579         def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
580                 self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
581 #               ToFix, this causes problems with setting the temperature with the keyboard
582 #               if self.temperatureSelect.GetValue() != targetTemp:
583 #                       wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
584 #               if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
585 #                       wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
586
587         def mcStateChange(self, state):
588                 if self.machineCom != None:
589                         if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
590                                 self.cam.endTimelaps()
591                         if state == self.machineCom.STATE_OPERATIONAL:
592                                 taskbar.setBusy(self, False)
593                         if self.machineCom.isClosedOrError():
594                                 taskbar.setBusy(self, False)
595                         if self.machineCom.isPaused():
596                                 taskbar.setPause(self, True)
597                 wx.CallAfter(self.UpdateButtonStates)
598                 wx.CallAfter(self.UpdateProgress)
599
600         def mcMessage(self, message):
601                 wx.CallAfter(self.AddTermLog, message)
602
603         def mcProgress(self, lineNr):
604                 wx.CallAfter(self.UpdateProgress)
605
606         def mcZChange(self, newZ):
607                 self.currentZ = newZ
608                 if self.cam != None:
609                         wx.CallAfter(self.cam.takeNewImage)
610                         wx.CallAfter(self.camPreview.Refresh)
611
612
613 class temperatureGraph(wx.Panel):
614         def __init__(self, parent):
615                 super(temperatureGraph, self).__init__(parent)
616
617                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
618                 self.Bind(wx.EVT_SIZE, self.OnSize)
619                 self.Bind(wx.EVT_PAINT, self.OnDraw)
620
621                 self.lastDraw = time.time() - 1.0
622                 self.points = []
623                 self.backBuffer = None
624                 self.addPoint(0, 0, 0, 0)
625                 self.SetMinSize((320, 200))
626
627         def OnEraseBackground(self, e):
628                 pass
629
630         def OnSize(self, e):
631                 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
632                         self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
633                         self.UpdateDrawing(True)
634
635         def OnDraw(self, e):
636                 dc = wx.BufferedPaintDC(self, self.backBuffer)
637
638         def UpdateDrawing(self, force=False):
639                 now = time.time()
640                 if not force and now - self.lastDraw < 1.0:
641                         return
642                 self.lastDraw = now
643                 dc = wx.MemoryDC()
644                 dc.SelectObject(self.backBuffer)
645                 dc.Clear()
646                 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
647                 w, h = self.GetSizeTuple()
648                 bgLinePen = wx.Pen('#A0A0A0')
649                 tempPen = wx.Pen('#FF4040')
650                 tempSPPen = wx.Pen('#FFA0A0')
651                 tempPenBG = wx.Pen('#FFD0D0')
652                 bedTempPen = wx.Pen('#4040FF')
653                 bedTempSPPen = wx.Pen('#A0A0FF')
654                 bedTempPenBG = wx.Pen('#D0D0FF')
655
656                 #Draw the background up to the current temperatures.
657                 x0 = 0
658                 t0 = 0
659                 bt0 = 0
660                 tSP0 = 0
661                 btSP0 = 0
662                 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
663                         x1 = int(w - (now - t))
664                         for x in xrange(x0, x1 + 1):
665                                 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
666                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
667                                 dc.SetPen(tempPenBG)
668                                 dc.DrawLine(x, h, x, h - (t * h / 300))
669                                 dc.SetPen(bedTempPenBG)
670                                 dc.DrawLine(x, h, x, h - (bt * h / 300))
671                         t0 = temp
672                         bt0 = bedTemp
673                         tSP0 = tempSP
674                         btSP0 = bedTempSP
675                         x0 = x1 + 1
676
677                 #Draw the grid
678                 for x in xrange(w, 0, -30):
679                         dc.SetPen(bgLinePen)
680                         dc.DrawLine(x, 0, x, h)
681                 tmpNr = 0
682                 for y in xrange(h - 1, 0, -h * 50 / 300):
683                         dc.SetPen(bgLinePen)
684                         dc.DrawLine(0, y, w, y)
685                         dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
686                         tmpNr += 50
687                 dc.DrawLine(0, 0, w, 0)
688                 dc.DrawLine(0, 0, 0, h)
689
690                 #Draw the main lines
691                 x0 = 0
692                 t0 = 0
693                 bt0 = 0
694                 tSP0 = 0
695                 btSP0 = 0
696                 for temp, tempSP, bedTemp, bedTempSP, t in self.points:
697                         x1 = int(w - (now - t))
698                         for x in xrange(x0, x1 + 1):
699                                 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
700                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
701                                 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
702                                 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
703                                 dc.SetPen(tempSPPen)
704                                 dc.DrawPoint(x, h - (tSP * h / 300))
705                                 dc.SetPen(bedTempSPPen)
706                                 dc.DrawPoint(x, h - (btSP * h / 300))
707                                 dc.SetPen(tempPen)
708                                 dc.DrawPoint(x, h - (t * h / 300))
709                                 dc.SetPen(bedTempPen)
710                                 dc.DrawPoint(x, h - (bt * h / 300))
711                         t0 = temp
712                         bt0 = bedTemp
713                         tSP0 = tempSP
714                         btSP0 = bedTempSP
715                         x0 = x1 + 1
716
717                 del dc
718                 self.Refresh(eraseBackground=False)
719                 self.Update()
720
721                 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:
722                         self.points.pop(0)
723
724         def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
725                 if bedTemp == None:
726                         bedTemp = 0
727                 if bedTempSP == None:
728                         bedTempSP = 0
729                 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
730                 wx.CallAfter(self.UpdateDrawing)
731
732
733 class LogWindow(wx.Frame):
734         def __init__(self, logText):
735                 super(LogWindow, self).__init__(None, title="Machine log")
736                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
737                 self.SetSize((500, 400))
738                 self.Centre()
739                 self.Show(True)