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