chiark / gitweb /
Workaround for #160, as on MacOS you seem to be able to press the jog buttons during...
[cura.git] / Cura / gui / printWindow.py
1 from __future__ import absolute_import\r
2 import __init__\r
3 \r
4 import wx, threading, re, subprocess, sys, os, time\r
5 from wx.lib import buttons\r
6 \r
7 from gui import icon\r
8 from gui import toolbarUtil\r
9 from util import machineCom\r
10 from util import profile\r
11 from util import gcodeInterpreter\r
12 \r
13 printWindowMonitorHandle = None\r
14 \r
15 def printFile(filename):\r
16         global printWindowMonitorHandle\r
17         if printWindowMonitorHandle == None:\r
18                 printWindowMonitorHandle = printProcessMonitor()\r
19         printWindowMonitorHandle.loadFile(filename)\r
20 \r
21 \r
22 def startPrintInterface(filename):\r
23         #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.\r
24         # 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
25         app = wx.App(False)\r
26         printWindowHandle = printWindow()\r
27         printWindowHandle.Show(True)\r
28         printWindowHandle.Raise()\r
29         printWindowHandle.OnConnect(None)\r
30         t = threading.Thread(target=printWindowHandle.LoadGCodeFile,args=(filename,))\r
31         t.daemon = True\r
32         t.start()\r
33         app.MainLoop()\r
34 \r
35 class printProcessMonitor():\r
36         def __init__(self):\r
37                 self.handle = None\r
38         \r
39         def loadFile(self, filename):\r
40                 if self.handle == None:\r
41                         self.handle = subprocess.Popen([sys.executable, sys.argv[0], '-r', filename], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\r
42                         self.thread = threading.Thread(target=self.Monitor)\r
43                         self.thread.start()\r
44                 else:\r
45                         self.handle.stdin.write(filename + '\n')\r
46         \r
47         def Monitor(self):\r
48                 p = self.handle\r
49                 line = p.stdout.readline()\r
50                 while(len(line) > 0):\r
51                         print line.rstrip()\r
52                         line = p.stdout.readline()\r
53                 p.wait()\r
54                 self.handle = None\r
55                 self.thread = None\r
56 \r
57 class PrintCommandButton(buttons.GenBitmapButton):\r
58         def __init__(self, parent, command, bitmapFilename, size=(20,20)):\r
59                 self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename)\r
60                 super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)\r
61 \r
62                 self.command = command\r
63                 self.parent = parent\r
64 \r
65                 self.SetBezelWidth(1)\r
66                 self.SetUseFocusIndicator(False)\r
67 \r
68                 self.Bind(wx.EVT_BUTTON, self.OnClick)\r
69 \r
70         def OnClick(self, e):\r
71                 if self.parent.printIdx != None:\r
72                         return;\r
73                 self.parent.sendCommand("G91")\r
74                 self.parent.sendCommand(self.command)\r
75                 self.parent.sendCommand("G90")\r
76                 e.Skip()\r
77 \r
78 class printWindow(wx.Frame):\r
79         "Main user interface window"\r
80         def __init__(self):\r
81                 super(printWindow, self).__init__(None, -1, title='Printing')\r
82                 self.machineCom = None\r
83                 self.machineConnected = False\r
84                 self.thread = None\r
85                 self.gcode = None\r
86                 self.gcodeList = None\r
87                 self.sendList = []\r
88                 self.printIdx = None\r
89                 self.temp = None\r
90                 self.bedTemp = None\r
91                 self.bufferLineCount = 4\r
92                 self.sendCnt = 0\r
93                 self.feedrateRatioOuterWall = 1.0\r
94                 self.feedrateRatioInnerWall = 1.0\r
95                 self.feedrateRatioFill = 1.0\r
96                 self.feedrateRatioSupport = 1.0\r
97                 self.pause = False\r
98                 self.termHistory = []\r
99                 self.termHistoryIdx = 0\r
100 \r
101                 #self.SetIcon(icon.getMainIcon())\r
102                 \r
103                 self.SetSizer(wx.BoxSizer())\r
104                 self.panel = wx.Panel(self)\r
105                 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)\r
106                 self.sizer = wx.GridBagSizer(2, 2)\r
107                 self.panel.SetSizer(self.sizer)\r
108                 \r
109                 sb = wx.StaticBox(self.panel, label="Statistics")\r
110                 boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)\r
111                 self.statsText = wx.StaticText(self.panel, -1, "Filament: ####.##m #.##g\nPrint time: #####:##")\r
112                 boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)\r
113                 \r
114                 self.sizer.Add(boxsizer, pos=(0,0), span=(5,1), flag=wx.EXPAND)\r
115                 \r
116                 self.connectButton = wx.Button(self.panel, -1, 'Connect')\r
117                 #self.loadButton = wx.Button(self.panel, -1, 'Load GCode')\r
118                 self.printButton = wx.Button(self.panel, -1, 'Print GCode')\r
119                 self.pauseButton = wx.Button(self.panel, -1, 'Pause')\r
120                 self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')\r
121                 self.progress = wx.Gauge(self.panel, -1)\r
122                 \r
123                 self.sizer.Add(self.connectButton, pos=(0,1))\r
124                 #self.sizer.Add(self.loadButton, pos=(1,1))\r
125                 self.sizer.Add(self.printButton, pos=(2,1))\r
126                 self.sizer.Add(self.pauseButton, pos=(3,1))\r
127                 self.sizer.Add(self.cancelButton, pos=(4,1))\r
128                 self.sizer.Add(self.progress, pos=(5,0), span=(1,2), flag=wx.EXPAND)\r
129 \r
130                 nb = wx.Notebook(self.panel)\r
131                 self.sizer.Add(nb, pos=(0,3), span=(7,4))\r
132                 \r
133                 self.temperaturePanel = wx.Panel(nb)\r
134                 sizer = wx.GridBagSizer(2, 2)\r
135                 self.temperaturePanel.SetSizer(sizer)\r
136 \r
137                 self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
138                 self.temperatureSelect.SetRange(0, 400)\r
139                 self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")\r
140                 self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
141                 self.bedTemperatureSelect.SetRange(0, 400)\r
142                 self.bedTemperatureLabel.Show(False)\r
143                 self.bedTemperatureSelect.Show(False)\r
144                 \r
145                 self.temperatureGraph = temperatureGraph(self.temperaturePanel)\r
146                 \r
147                 sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0,0))\r
148                 sizer.Add(self.temperatureSelect, pos=(0,1))\r
149                 sizer.Add(self.bedTemperatureLabel, pos=(1,0))\r
150                 sizer.Add(self.bedTemperatureSelect, pos=(1,1))\r
151                 sizer.Add(self.temperatureGraph, pos=(2,0), span=(1,2), flag=wx.EXPAND)\r
152                 sizer.AddGrowableRow(2)\r
153                 sizer.AddGrowableCol(0)\r
154 \r
155                 nb.AddPage(self.temperaturePanel, 'Temp')\r
156 \r
157                 self.directControlPanel = wx.Panel(nb)\r
158                 \r
159                 sizer = wx.GridBagSizer(2, 2)\r
160                 self.directControlPanel.SetSizer(sizer)\r
161                 sizer.Add(PrintCommandButton(self, 'G1 Y100 F6000', 'print-move-y100.png'), pos=(0,3))\r
162                 sizer.Add(PrintCommandButton(self, 'G1 Y10 F6000', 'print-move-y10.png'), pos=(1,3))\r
163                 sizer.Add(PrintCommandButton(self, 'G1 Y1 F6000', 'print-move-y1.png'), pos=(2,3))\r
164 \r
165                 sizer.Add(PrintCommandButton(self, 'G1 Y-1 F6000', 'print-move-y-1.png'), pos=(4,3))\r
166                 sizer.Add(PrintCommandButton(self, 'G1 Y-10 F6000', 'print-move-y-10.png'), pos=(5,3))\r
167                 sizer.Add(PrintCommandButton(self, 'G1 Y-100 F6000', 'print-move-y-100.png'), pos=(6,3))\r
168 \r
169                 sizer.Add(PrintCommandButton(self, 'G1 X-100 F6000', 'print-move-x-100.png'), pos=(3,0))\r
170                 sizer.Add(PrintCommandButton(self, 'G1 X-10 F6000', 'print-move-x-10.png'), pos=(3,1))\r
171                 sizer.Add(PrintCommandButton(self, 'G1 X-1 F6000', 'print-move-x-1.png'), pos=(3,2))\r
172 \r
173                 sizer.Add(PrintCommandButton(self, 'G28 X0 Y0', 'print-move-home.png'), pos=(3,3))\r
174 \r
175                 sizer.Add(PrintCommandButton(self, 'G1 X1 F6000', 'print-move-x1.png'), pos=(3,4))\r
176                 sizer.Add(PrintCommandButton(self, 'G1 X10 F6000', 'print-move-x10.png'), pos=(3,5))\r
177                 sizer.Add(PrintCommandButton(self, 'G1 X100 F6000', 'print-move-x100.png'), pos=(3,6))\r
178 \r
179                 sizer.Add(PrintCommandButton(self, 'G1 Z10 F200', 'print-move-z10.png'), pos=(0,7))\r
180                 sizer.Add(PrintCommandButton(self, 'G1 Z1 F200', 'print-move-z1.png'), pos=(1,7))\r
181                 sizer.Add(PrintCommandButton(self, 'G1 Z0.1 F200', 'print-move-z0.1.png'), pos=(2,7))\r
182 \r
183                 sizer.Add(PrintCommandButton(self, 'G28 Z0', 'print-move-home.png'), pos=(3,7))\r
184 \r
185                 sizer.Add(PrintCommandButton(self, 'G1 Z-0.1 F200', 'print-move-z-0.1.png'), pos=(4,7))\r
186                 sizer.Add(PrintCommandButton(self, 'G1 Z-1 F200', 'print-move-z-1.png'), pos=(5,7))\r
187                 sizer.Add(PrintCommandButton(self, 'G1 Z-10 F200', 'print-move-z-10.png'), pos=(6,7))\r
188 \r
189                 nb.AddPage(self.directControlPanel, 'Jog')\r
190 \r
191                 self.speedPanel = wx.Panel(nb)\r
192                 sizer = wx.GridBagSizer(2, 2)\r
193                 self.speedPanel.SetSizer(sizer)\r
194 \r
195                 self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
196                 self.outerWallSpeedSelect.SetRange(5, 1000)\r
197                 self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
198                 self.innerWallSpeedSelect.SetRange(5, 1000)\r
199                 self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
200                 self.fillSpeedSelect.SetRange(5, 1000)\r
201                 self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS)\r
202                 self.supportSpeedSelect.SetRange(5, 1000)\r
203                 \r
204                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0,0))\r
205                 sizer.Add(self.outerWallSpeedSelect, pos=(0,1))\r
206                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0,2))\r
207                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1,0))\r
208                 sizer.Add(self.innerWallSpeedSelect, pos=(1,1))\r
209                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1,2))\r
210                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2,0))\r
211                 sizer.Add(self.fillSpeedSelect, pos=(2,1))\r
212                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2,2))\r
213                 sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3,0))\r
214                 sizer.Add(self.supportSpeedSelect, pos=(3,1))\r
215                 sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2))\r
216 \r
217                 nb.AddPage(self.speedPanel, 'Speed')\r
218                 \r
219                 self.termPanel = wx.Panel(nb)\r
220                 sizer = wx.GridBagSizer(2, 2)\r
221                 self.termPanel.SetSizer(sizer)\r
222                 \r
223                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)\r
224                 self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP)\r
225                 self.termLog.SetFont(f)\r
226                 self.termLog.SetEditable(0)\r
227                 self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER)\r
228                 self.termInput.SetFont(f)\r
229 \r
230                 sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND)\r
231                 sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND)\r
232                 sizer.AddGrowableCol(0)\r
233                 sizer.AddGrowableRow(0)\r
234 \r
235                 nb.AddPage(self.termPanel, 'Term')\r
236 \r
237                 self.sizer.AddGrowableRow(3)\r
238                 self.sizer.AddGrowableCol(0)\r
239                 \r
240                 self.Bind(wx.EVT_CLOSE, self.OnClose)\r
241                 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)\r
242                 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)\r
243                 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)\r
244                 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)\r
245                 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)\r
246                 \r
247                 self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)\r
248                 self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)\r
249 \r
250                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect)\r
251                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect)\r
252                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect)\r
253                 self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect)\r
254                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput)\r
255                 self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey)\r
256                 \r
257                 self.Layout()\r
258                 self.Fit()\r
259                 self.Centre()\r
260 \r
261                 self.UpdateButtonStates()\r
262                 self.UpdateProgress()\r
263         \r
264         def UpdateButtonStates(self):\r
265                 self.connectButton.Enable(not self.machineConnected)\r
266                 #self.loadButton.Enable(self.printIdx == None)\r
267                 self.printButton.Enable(self.machineConnected and self.gcodeList != None and self.printIdx == None)\r
268                 self.pauseButton.Enable(self.printIdx != None)\r
269                 self.cancelButton.Enable(self.printIdx != None)\r
270                 self.temperatureSelect.Enable(self.machineConnected)\r
271                 self.bedTemperatureSelect.Enable(self.machineConnected)\r
272                 self.directControlPanel.Enable(self.machineConnected)\r
273         \r
274         def UpdateProgress(self):\r
275                 status = ""\r
276                 if self.gcode == None:\r
277                         status += "Loading gcode...\n"\r
278                 else:\r
279                         status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)\r
280                         cost = self.gcode.calculateCost()\r
281                         if cost != False:\r
282                                 status += "Filament cost: %s\n" % (cost)\r
283                         status += "Print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))\r
284                 if self.printIdx == None:\r
285                         self.progress.SetValue(0)\r
286                         if self.gcodeList != None:\r
287                                 status += 'Line: -/%d\n' % (len(self.gcodeList))\r
288                 else:\r
289                         status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList))\r
290                         self.progress.SetValue(self.printIdx)\r
291                 if self.temp != None:\r
292                         status += 'Temp: %d\n' % (self.temp)\r
293                 if self.bedTemp != None and self.bedTemp > 0:\r
294                         status += 'Bed Temp: %d\n' % (self.bedTemp)\r
295                         self.bedTemperatureLabel.Show(True)\r
296                         self.bedTemperatureSelect.Show(True)\r
297                         self.temperaturePanel.Layout()\r
298                 self.statsText.SetLabel(status.strip())\r
299                 #self.Layout()\r
300         \r
301         def OnConnect(self, e):\r
302                 if self.machineCom != None:\r
303                         self.machineCom.close()\r
304                         self.thread.join()\r
305                 self.machineCom = machineCom.MachineCom()\r
306                 self.thread = threading.Thread(target=self.PrinterMonitor)\r
307                 self.thread.start()\r
308                 self.UpdateButtonStates()\r
309         \r
310         def OnLoad(self, e):\r
311                 pass\r
312         \r
313         def OnPrint(self, e):\r
314                 if not self.machineConnected:\r
315                         return\r
316                 if self.gcodeList == None:\r
317                         return\r
318                 if self.printIdx != None:\r
319                         return\r
320                 self.printIdx = 1\r
321                 self.sendLine(0)\r
322                 self.sendCnt = self.bufferLineCount\r
323                 self.UpdateButtonStates()\r
324         \r
325         def OnCancel(self, e):\r
326                 self.printIdx = None\r
327                 self.pause = False\r
328                 self.pauseButton.SetLabel('Pause')\r
329                 self.sendCommand("M84")\r
330                 self.UpdateButtonStates()\r
331         \r
332         def OnPause(self, e):\r
333                 if self.pause:\r
334                         self.pause = False\r
335                         self.sendLine(self.printIdx)\r
336                         self.printIdx += 1\r
337                         self.pauseButton.SetLabel('Pause')\r
338                 else:\r
339                         self.pause = True\r
340                         self.pauseButton.SetLabel('Resume')\r
341         \r
342         def OnClose(self, e):\r
343                 global printWindowHandle\r
344                 printWindowHandle = None\r
345                 if self.machineCom != None:\r
346                         self.machineCom.close()\r
347                         self.thread.join()\r
348                 self.Destroy()\r
349 \r
350         def OnTempChange(self, e):\r
351                 self.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue()))\r
352 \r
353         def OnBedTempChange(self, e):\r
354                 self.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))\r
355         \r
356         def OnSpeedChange(self, e):\r
357                 self.feedrateRatioOuterWall = self.outerWallSpeedSelect.GetValue() / 100.0\r
358                 self.feedrateRatioInnerWall = self.innerWallSpeedSelect.GetValue() / 100.0\r
359                 self.feedrateRatioFill = self.fillSpeedSelect.GetValue() / 100.0\r
360                 self.feedrateRatioSupport = self.supportSpeedSelect.GetValue() / 100.0\r
361         \r
362         def AddTermLog(self, line):\r
363                 self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))\r
364         \r
365         def OnTermEnterLine(self, e):\r
366                 line = self.termInput.GetValue()\r
367                 if line == '':\r
368                         return\r
369                 self.termLog.AppendText('>%s\n' % (line))\r
370                 self.sendCommand(line)\r
371                 self.termHistory.append(line)\r
372                 self.termHistoryIdx = len(self.termHistory)\r
373                 self.termInput.SetValue('')\r
374 \r
375         def OnTermKey(self, e):\r
376                 if len(self.termHistory) > 0:\r
377                         if e.GetKeyCode() == wx.WXK_UP:\r
378                                 self.termHistoryIdx = self.termHistoryIdx - 1\r
379                                 if self.termHistoryIdx < 0:\r
380                                         self.termHistoryIdx = len(self.termHistory) - 1\r
381                                 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])\r
382                         if e.GetKeyCode() == wx.WXK_DOWN:\r
383                                 self.termHistoryIdx = self.termHistoryIdx - 1\r
384                                 if self.termHistoryIdx >= len(self.termHistory):\r
385                                         self.termHistoryIdx = 0\r
386                                 self.termInput.SetValue(self.termHistory[self.termHistoryIdx])\r
387                 e.Skip()\r
388 \r
389         def LoadGCodeFile(self, filename):\r
390                 if self.printIdx != None:\r
391                         return\r
392                 #Send an initial M110 to reset the line counter to zero.\r
393                 lineType = 'CUSTOM'\r
394                 gcodeList = ["M110"]\r
395                 typeList = [lineType]\r
396                 for line in open(filename, 'r'):\r
397                         if line.startswith(';TYPE:'):\r
398                                 lineType = line[6:].strip()\r
399                         if ';' in line:\r
400                                 line = line[0:line.find(';')]\r
401                         line = line.strip()\r
402                         if len(line) > 0:\r
403                                 gcodeList.append(line)\r
404                                 typeList.append(lineType)\r
405                 gcode = gcodeInterpreter.gcode()\r
406                 gcode.loadList(gcodeList)\r
407                 print "Loaded: %s (%d)" % (filename, len(gcodeList))\r
408                 self.gcode = gcode\r
409                 self.gcodeList = gcodeList\r
410                 self.typeList = typeList\r
411                 \r
412                 wx.CallAfter(self.progress.SetRange, len(gcodeList))\r
413                 wx.CallAfter(self.UpdateButtonStates)\r
414                 wx.CallAfter(self.UpdateProgress)\r
415                 \r
416         def sendCommand(self, cmd):\r
417                 if self.machineConnected:\r
418                         if self.printIdx == None or self.pause:\r
419                                 self.machineCom.sendCommand(cmd)\r
420                         else:\r
421                                 self.sendList.append(cmd)\r
422 \r
423         def sendLine(self, lineNr):\r
424                 if lineNr >= len(self.gcodeList):\r
425                         return False\r
426                 line = self.gcodeList[lineNr]\r
427                 try:\r
428                         if line == 'M0' or line == 'M1':\r
429                                 self.OnPause(None)\r
430                                 line = 'M105'\r
431                         if ('M104' in line or 'M109' in line) and 'S' in line:\r
432                                 n = int(re.search('S([0-9]*)', line).group(1))\r
433                                 wx.CallAfter(self.temperatureSelect.SetValue, n)\r
434                         if ('M140' in line or 'M190' in line) and 'S' in line:\r
435                                 n = int(re.search('S([0-9]*)', line).group(1))\r
436                                 wx.CallAfter(self.bedTemperatureSelect.SetValue, n)\r
437                         if self.typeList[lineNr] == 'WALL-OUTER':\r
438                                 line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioOuterWall)), line)\r
439                         if self.typeList[lineNr] == 'WALL-INNER':\r
440                                 line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioInnerWall)), line)\r
441                         if self.typeList[lineNr] == 'FILL':\r
442                                 line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioFill)), line)\r
443                         if self.typeList[lineNr] == 'SUPPORT':\r
444                                 line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioSupport)), line)\r
445                 except:\r
446                         print "Unexpected error:", sys.exc_info()\r
447                 checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))\r
448                 self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum))\r
449                 return True\r
450 \r
451         def PrinterMonitor(self):\r
452                 while True:\r
453                         line = self.machineCom.readline()\r
454                         if line == None:\r
455                                 self.machineConnected = False\r
456                                 wx.CallAfter(self.UpdateButtonStates)\r
457                                 return\r
458                         if line == '':  #When we have a communication "timeout" and we're not sending gcode, then read the temperature.\r
459                                 if self.printIdx == None or self.pause:\r
460                                         self.machineCom.sendCommand("M105")\r
461                                 else:\r
462                                         wx.CallAfter(self.AddTermLog, '!!Comm timeout, forcing next line!!\n')\r
463                                         line = 'ok'\r
464                         if self.machineConnected:\r
465                                 while self.sendCnt > 0 and not self.pause:\r
466                                         self.sendLine(self.printIdx)\r
467                                         self.printIdx += 1\r
468                                         self.sendCnt -= 1\r
469                         if line.startswith("start"):\r
470                                 self.machineConnected = True\r
471                                 wx.CallAfter(self.UpdateButtonStates)\r
472                         elif 'T:' in line:\r
473                                 self.temp = float(re.search("[0-9\.]*", line.split('T:')[1]).group(0))\r
474                                 if 'B:' in line:\r
475                                         self.bedTemp = float(re.search("[0-9\.]*", line.split('B:')[1]).group(0))\r
476                                 self.temperatureGraph.addPoint(self.temp, self.temperatureSelect.GetValue(), self.bedTemp, self.bedTemperatureSelect.GetValue())\r
477                                 wx.CallAfter(self.UpdateProgress)\r
478                         elif line.strip() != 'ok':\r
479                                 wx.CallAfter(self.AddTermLog, line)\r
480                         if self.printIdx != None:\r
481                                 if line.startswith("ok"):\r
482                                         if len(self.sendList) > 0:\r
483                                                 self.machineCom.sendCommand(self.sendList.pop(0))\r
484                                         elif self.pause:\r
485                                                 self.sendCnt += 1\r
486                                         else:\r
487                                                 if self.sendLine(self.printIdx):\r
488                                                         self.printIdx += 1\r
489                                                 else:\r
490                                                         self.printIdx = None\r
491                                                         wx.CallAfter(self.UpdateButtonStates)\r
492                                                 wx.CallAfter(self.UpdateProgress)\r
493                                 elif "resend" in line.lower() or "rs" in line:\r
494                                         try:\r
495                                                 lineNr=int(line.replace("N:"," ").replace("N"," ").replace(":"," ").split()[-1])\r
496                                         except:\r
497                                                 if "rs" in line:\r
498                                                         lineNr=int(line.split()[1])\r
499                                         self.printIdx = lineNr\r
500                                         #we should actually resend the line here, but we also get an "ok" for each error from Marlin. And thus we'll resend on the OK.\r
501 \r
502 class temperatureGraph(wx.Panel):\r
503         def __init__(self, parent):\r
504                 super(temperatureGraph, self).__init__(parent)\r
505                 \r
506                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)\r
507                 self.Bind(wx.EVT_SIZE, self.OnSize)\r
508                 self.Bind(wx.EVT_PAINT, self.OnDraw)\r
509                 \r
510                 self.lastDraw = time.time() - 1.0\r
511                 self.points = []\r
512                 self.backBuffer = None\r
513                 self.addPoint(0,0,0,0)\r
514         \r
515         def OnEraseBackground(self, e):\r
516                 pass\r
517         \r
518         def OnSize(self, e):\r
519                 if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():\r
520                         self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())\r
521                         self.UpdateDrawing(True)\r
522         \r
523         def OnDraw(self, e):\r
524                 dc = wx.BufferedPaintDC(self, self.backBuffer)\r
525         \r
526         def UpdateDrawing(self, force = False):\r
527                 now = time.time()\r
528                 if not force and now - self.lastDraw < 1.0:\r
529                         return\r
530                 self.lastDraw = now\r
531                 dc = wx.MemoryDC()\r
532                 dc.SelectObject(self.backBuffer)\r
533                 dc.Clear()\r
534                 w, h = self.GetSizeTuple()\r
535                 x0 = 0\r
536                 t0 = 0\r
537                 bt0 = 0\r
538                 tSP0 = 0\r
539                 btSP0 = 0\r
540                 tempPen = wx.Pen('#FF4040')\r
541                 tempSPPen = wx.Pen('#FFA0A0')\r
542                 tempPenBG = wx.Pen('#FFD0D0')\r
543                 bedTempPen = wx.Pen('#4040FF')\r
544                 bedTempSPPen = wx.Pen('#A0A0FF')\r
545                 bedTempPenBG = wx.Pen('#D0D0FF')\r
546                 for temp, tempSP, bedTemp, bedTempSP, t in self.points:\r
547                         x1 = int(w - (now - t))\r
548                         for x in xrange(x0, x1 + 1):\r
549                                 t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0\r
550                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0\r
551                                 tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0\r
552                                 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0\r
553                                 dc.SetPen(tempPenBG)\r
554                                 dc.DrawLine(x, h, x, h - (t * h / 300))\r
555                                 dc.SetPen(bedTempPenBG)\r
556                                 dc.DrawLine(x, h, x, h - (bt * h / 300))\r
557                                 dc.SetPen(tempSPPen)\r
558                                 dc.DrawPoint(x, h - (tSP * h / 300))\r
559                                 dc.SetPen(bedTempSPPen)\r
560                                 dc.DrawPoint(x, h - (btSP * h / 300))\r
561                                 dc.SetPen(tempPen)\r
562                                 dc.DrawPoint(x, h - (t * h / 300))\r
563                                 dc.SetPen(bedTempPen)\r
564                                 dc.DrawPoint(x, h - (bt * h / 300))\r
565                         t0 = temp\r
566                         bt0 = bedTemp\r
567                         tSP0 = tempSP\r
568                         btSP0 = bedTempSP\r
569                         x0 = x1 + 1\r
570                 \r
571                 del dc\r
572                 self.Refresh(eraseBackground=False)\r
573                 self.Update()\r
574                 \r
575                 if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20:\r
576                         self.points.pop(0)\r
577 \r
578         def addPoint(self, temp, tempSP, bedTemp, bedTempSP):\r
579                 self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))\r
580                 wx.CallAfter(self.UpdateDrawing)\r
581 \r