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