chiark / gitweb /
Add experimental new advanced print window
[cura.git] / Cura / gui / printWindow.py
1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
2
3 import wx
4 import power
5 import time
6 import sys
7 import os
8 import ctypes
9 import subprocess
10 from Cura.util import resources
11
12 #TODO: This does not belong here!
13 if sys.platform.startswith('win'):
14         def preventComputerFromSleeping(frame, prevent):
15                 """
16                 Function used to prevent the computer from going into sleep mode.
17                 :param prevent: True = Prevent the system from going to sleep from this point on.
18                 :param prevent: False = No longer prevent the system from going to sleep.
19                 """
20                 ES_CONTINUOUS = 0x80000000
21                 ES_SYSTEM_REQUIRED = 0x00000001
22                 ES_AWAYMODE_REQUIRED = 0x00000040
23                 #SetThreadExecutionState returns 0 when failed, which is ignored. The function should be supported from windows XP and up.
24                 if prevent:
25                         # For Vista and up we use ES_AWAYMODE_REQUIRED to prevent a print from failing if the PC does go to sleep
26                         # As it's not supported on XP, we catch the error and fallback to using ES_SYSTEM_REQUIRED only
27                         if ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED) == 0:
28                                 ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED)
29                 else:
30                         ctypes.windll.kernel32.SetThreadExecutionState(ES_CONTINUOUS)
31
32 elif sys.platform.startswith('darwin'):
33         import objc
34         bundle = objc.initFrameworkWrapper("IOKit",
35         frameworkIdentifier="com.apple.iokit",
36         frameworkPath=objc.pathForFramework("/System/Library/Frameworks/IOKit.framework"),
37         globals=globals())
38         objc.loadBundleFunctions(bundle, globals(), [("IOPMAssertionCreateWithName", b"i@I@o^I")])
39         def preventComputerFromSleeping(frame, prevent):
40                 if prevent:
41                         success, preventComputerFromSleeping.assertionID = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, "Cura is printing", None)
42                         if success != kIOReturnSuccess:
43                                 preventComputerFromSleeping.assertionID = None
44                 else:
45                         if preventComputerFromSleeping.assertionID is not None:
46                                 IOPMAssertionRelease(preventComputerFromSleeping.assertionID)
47                                 preventComputerFromSleeping.assertionID = None
48 else:
49         def preventComputerFromSleeping(frame, prevent):
50                 if os.path.isfile("/usr/bin/xdg-screensaver"):
51                         try:
52                                 cmd = ['xdg-screensaver', 'suspend' if prevent else 'resume', str(frame.GetHandle())]
53                                 subprocess.call(cmd)
54                         except:
55                                 pass
56
57 class printWindowPlugin(wx.Frame):
58         def __init__(self, parent, printerConnection, filename):
59                 super(printWindowPlugin, self).__init__(parent, -1, style=wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.CAPTION|wx.SYSTEM_MENU|wx.FRAME_FLOAT_ON_PARENT|wx.MINIMIZE_BOX, title=_("Printing on %s") % (printerConnection.getName()))
60                 self._printerConnection = printerConnection
61                 self._basePath = os.path.dirname(filename)
62                 self._backgroundImage = None
63                 self._colorCommandMap = {}
64                 self._buttonList = []
65                 self._termLog = None
66                 self._termInput = None
67                 self._termHistory = []
68                 self._termHistoryIdx = 0
69                 self._progressBar = None
70                 self._tempGraph = None
71                 self._infoText = None
72                 self._lastUpdateTime = time.time()
73                 self._isPrinting = False
74
75                 variables = {
76                         'setImage': self.script_setImage,
77                         'addColorCommand': self.script_addColorCommand,
78                         'addTerminal': self.script_addTerminal,
79                         'addTemperatureGraph': self.script_addTemperatureGraph,
80                         'addProgressbar': self.script_addProgressbar,
81                         'addButton': self.script_addButton,
82                         'addSpinner': self.script_addSpinner,
83                         'addTextButton': self.script_addTextButton,
84
85                         'sendGCode': self.script_sendGCode,
86                         'sendMovementGCode': self.script_sendMovementGCode,
87                         'connect': self.script_connect,
88                         'startPrint': self.script_startPrint,
89                         'pausePrint': self.script_pausePrint,
90                         'cancelPrint': self.script_cancelPrint,
91                         'showErrorLog': self.script_showErrorLog,
92                 }
93                 execfile(filename, variables, variables)
94
95                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
96                 self.Bind(wx.EVT_PAINT, self.OnDraw)
97                 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftClick)
98                 self.Bind(wx.EVT_CLOSE, self.OnClose)
99
100                 self._updateButtonStates()
101
102                 self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
103
104                 if self._printerConnection.hasActiveConnection() and not self._printerConnection.isActiveConnectionOpen():
105                         self._printerConnection.openActiveConnection()
106
107         def script_setImage(self, guiImage, mapImage):
108                 self._backgroundImage = wx.BitmapFromImage(wx.Image(os.path.join(self._basePath, guiImage)))
109                 self._mapImage = wx.Image(os.path.join(self._basePath, mapImage))
110                 self.SetClientSize(self._mapImage.GetSize())
111
112         def script_addColorCommand(self, r, g, b, command, data = None):
113                 self._colorCommandMap[(r, g, b)] = (command, data)
114
115         def script_addTerminal(self, r, g, b):
116                 x, y, w, h = self._getColoredRect(r, g, b)
117                 if x < 0 or self._termLog is not None:
118                         return
119                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
120                 self._termLog = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
121                 self._termLog.SetFont(f)
122                 self._termLog.SetEditable(0)
123                 self._termInput = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
124                 self._termInput.SetFont(f)
125
126                 self._termLog.SetPosition((x, y))
127                 self._termLog.SetSize((w, h - self._termInput.GetSize().GetHeight()))
128                 self._termInput.SetPosition((x, y + h - self._termInput.GetSize().GetHeight()))
129                 self._termInput.SetSize((w, self._termInput.GetSize().GetHeight()))
130                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput)
131                 self._termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
132
133         def script_addTemperatureGraph(self, r, g, b):
134                 x, y, w, h = self._getColoredRect(r, g, b)
135                 if x < 0 or self._tempGraph is not None:
136                         return
137                 self._tempGraph = TemperatureGraph(self)
138
139                 self._tempGraph.SetPosition((x, y))
140                 self._tempGraph.SetSize((w, h))
141
142         def script_addProgressbar(self, r, g, b):
143                 x, y, w, h = self._getColoredRect(r, g, b)
144                 if x < 0:
145                         return
146                 self._progressBar = wx.Gauge(self, -1, range=1000)
147
148                 self._progressBar.SetPosition((x, y))
149                 self._progressBar.SetSize((w, h))
150
151         def script_addButton(self, r, g, b, text, command, data = None):
152                 x, y, w, h = self._getColoredRect(r, g, b)
153                 if x < 0:
154                         return
155                 button = wx.Button(self, -1, _(text))
156                 button.SetPosition((x, y))
157                 button.SetSize((w, h))
158                 button.command = command
159                 button.data = data
160                 self._buttonList.append(button)
161                 self.Bind(wx.EVT_BUTTON, lambda e: command(data), button)
162
163         def script_addSpinner(self, r, g, b, command, data):
164                 x, y, w, h = self._getColoredRect(r, g, b)
165                 if x < 0:
166                         return
167
168                 def run_command(spinner):
169                         value = spinner.GetValue()
170                         print "Value (%s) and (%s)" % (spinner.last_value, value)
171                         if spinner.last_value != '' and value != 0:
172                                 spinner.command(spinner.data % value)
173                                 spinner.last_value = value
174
175                 spinner = wx.SpinCtrl(self, -1, style=wx.TE_PROCESS_ENTER)
176                 spinner.SetRange(0, 300)
177                 spinner.SetPosition((x, y))
178                 spinner.SetSize((w, h))
179                 spinner.SetValue(0)
180                 spinner.command = command
181                 spinner.data = data
182                 spinner.last_value = ''
183                 self._buttonList.append(spinner)
184                 self.Bind(wx.EVT_SPINCTRL, lambda e: run_command(spinner), spinner)
185
186         def script_addTextButton(self, r_text, g_text, b_text, r_button, g_button, b_button, button_text, command, data):
187                 x_text, y_text, w_text, h_text = self._getColoredRect(r_text, g_text, b_text)
188                 if x_text < 0:
189                         return
190                 x_button, y_button, w_button, h_button = self._getColoredRect(r_button, g_button, b_button)
191                 if x_button < 0:
192                         return
193                 from wx.lib.intctrl import IntCtrl
194                 text = IntCtrl(self, -1)
195                 text.SetBounds(0, 300)
196                 text.SetPosition((x_text, y_text))
197                 text.SetSize((w_text, h_text))
198                 
199                 button = wx.Button(self, -1, _(button_text))
200                 button.SetPosition((x_button, y_button))
201                 button.SetSize((w_button, h_button))
202                 button.command = command
203                 button.data = data
204                 self._buttonList.append(button)
205                 self.Bind(wx.EVT_BUTTON, lambda e: command(data % text.GetValue()), button)
206
207         def _getColoredRect(self, r, g, b):
208                 for x in xrange(0, self._mapImage.GetWidth()):
209                         for y in xrange(0, self._mapImage.GetHeight()):
210                                 if self._mapImage.GetRed(x, y) == r and self._mapImage.GetGreen(x, y) == g and self._mapImage.GetBlue(x, y) == b:
211                                         w = 0
212                                         while x+w < self._mapImage.GetWidth() and self._mapImage.GetRed(x + w, y) == r and self._mapImage.GetGreen(x + w, y) == g and self._mapImage.GetBlue(x + w, y) == b:
213                                                 w += 1
214                                         h = 0
215                                         while y+h < self._mapImage.GetHeight() and self._mapImage.GetRed(x, y + h) == r and self._mapImage.GetGreen(x, y + h) == g and self._mapImage.GetBlue(x, y + h) == b:
216                                                 h += 1
217                                         return x, y, w, h
218                 print "Failed to find color: ", r, g, b
219                 return -1, -1, 1, 1
220
221         def script_sendGCode(self, data = None):
222                 for line in data.split(';'):
223                         line = line.strip()
224                         if len(line) > 0:
225                                 self._printerConnection.sendCommand(line)
226
227         def script_sendMovementGCode(self, data = None):
228                 if not self._printerConnection.isPaused() and not self._printerConnection.isPrinting():
229                         self.script_sendGCode(data)
230
231         def script_connect(self, data = None):
232                 self._printerConnection.openActiveConnection()
233
234         def script_startPrint(self, data = None):
235                 if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
236                         self._printerConnection.pause(not self._printerConnection.isPaused())
237                 else:
238                         self._printerConnection.startPrint()
239
240         def script_cancelPrint(self, e):
241                 self._printerConnection.cancelPrint()
242
243         def script_pausePrint(self, e):
244                 self._printerConnection.pause(not self._printerConnection.isPaused())
245
246         def script_showErrorLog(self, e):
247                 LogWindow(self._printerConnection.getErrorLog())
248
249         def OnEraseBackground(self, e):
250                 pass
251
252         def OnDraw(self, e):
253                 dc = wx.BufferedPaintDC(self, self._backgroundImage)
254
255         def OnLeftClick(self, e):
256                 r = self._mapImage.GetRed(e.GetX(), e.GetY())
257                 g = self._mapImage.GetGreen(e.GetX(), e.GetY())
258                 b = self._mapImage.GetBlue(e.GetX(), e.GetY())
259                 if (r, g, b) in self._colorCommandMap:
260                         command = self._colorCommandMap[(r, g, b)]
261                         command[0](command[1])
262
263         def OnClose(self, e):
264                 if self._printerConnection.hasActiveConnection():
265                         if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
266                                 pass #TODO: Give warning that the close will kill the print.
267                         self._printerConnection.closeActiveConnection()
268                 self._printerConnection.removeCallback(self._doPrinterConnectionUpdate)
269                 #TODO: When multiple printer windows are open, closing one will enable sleeping again.
270                 preventComputerFromSleeping(self, False)
271                 self._printerConnection.cancelPrint()
272                 self.Destroy()
273
274         def OnTermEnterLine(self, e):
275                 if not self._printerConnection.isAbleToSendDirectCommand():
276                         return
277                 line = self._termInput.GetValue()
278                 if line == '':
279                         return
280                 self._addTermLog('> %s\n' % (line))
281                 self._printerConnection.sendCommand(line)
282                 self._termHistory.append(line)
283                 self._termHistoryIdx = len(self._termHistory)
284                 self._termInput.SetValue('')
285
286         def OnTermKey(self, e):
287                 if len(self._termHistory) > 0:
288                         if e.GetKeyCode() == wx.WXK_UP:
289                                 self._termHistoryIdx -= 1
290                                 if self._termHistoryIdx < 0:
291                                         self._termHistoryIdx = len(self._termHistory) - 1
292                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
293                         if e.GetKeyCode() == wx.WXK_DOWN:
294                                 self._termHistoryIdx -= 1
295                                 if self._termHistoryIdx >= len(self._termHistory):
296                                         self._termHistoryIdx = 0
297                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
298                 e.Skip()
299
300         def _addTermLog(self, line):
301                 if self._termLog is not None:
302                         if len(self._termLog.GetValue()) > 10000:
303                                 self._termLog.SetValue(self._termLog.GetValue()[-10000:])
304                         self._termLog.SetInsertionPointEnd()
305                         if type(line) != unicode:
306                                 line = unicode(line, 'utf-8', 'replace')
307                         self._termLog.AppendText(line.encode('utf-8', 'replace'))
308
309         def _updateButtonStates(self):
310                 hasPauseButton = False
311                 for button in self._buttonList:
312                         if button.command == self.script_pausePrint:
313                                 hasPauseButton = True
314                                 break
315
316                 for button in self._buttonList:
317                         if button.command == self.script_connect:
318                                 button.Show(self._printerConnection.hasActiveConnection())
319                                 button.Enable(not self._printerConnection.isActiveConnectionOpen() and \
320                                                           not self._printerConnection.isActiveConnectionOpening())
321                         elif button.command == self.script_pausePrint:
322                                 button.Show(self._printerConnection.hasPause())
323                                 if not self._printerConnection.hasActiveConnection() or \
324                                    self._printerConnection.isActiveConnectionOpen():
325                                         button.Enable(self._printerConnection.isPrinting() or \
326                                                                   self._printerConnection.isPaused())
327                                         if self._printerConnection.isPaused():
328                                                 button.SetLabel(_("Resume"))
329                                         else:
330                                                 button.SetLabel(_("Pause"))
331                                 else:
332                                         button.Enable(False)
333                         elif button.command == self.script_startPrint:
334                                 if hasPauseButton or not self._printerConnection.hasPause():
335                                         if not self._printerConnection.hasActiveConnection() or \
336                                            self._printerConnection.isActiveConnectionOpen():
337                                                         button.Enable(not self._printerConnection.isPrinting() and \
338                                                                                   not self._printerConnection.isPaused())
339                                         else:
340                                                 button.Enable(False)
341                                 else:
342                                         if not self._printerConnection.hasActiveConnection() or \
343                                            self._printerConnection.isActiveConnectionOpen():
344                                                 if self._printerConnection.isPrinting():
345                                                         button.SetLabel(_("Pause"))
346                                                 else:
347                                                         if self._printerConnection.isPaused():
348                                                                 button.SetLabel(_("Resume"))
349                                                         else:
350                                                                 button.SetLabel(_("Print"))
351                                                 button.Enable(True)
352                                         else:
353                                                 button.Enable(False)
354                         elif button.command == self.script_cancelPrint:
355                                 if not self._printerConnection.hasActiveConnection() or \
356                                    self._printerConnection.isActiveConnectionOpen():
357                                         button.Enable(self._printerConnection.isPrinting() or \
358                                                                   self._printerConnection.isPaused())
359                                 else:
360                                         button.Enable(False)
361                         elif button.command == self.script_showErrorLog:
362                                 button.Show(self._printerConnection.isInErrorState())
363                 if self._termInput is not None:
364                         self._termInput.Enable(self._printerConnection.isAbleToSendDirectCommand())
365
366         def _doPrinterConnectionUpdate(self, connection, extraInfo = None):
367                 wx.CallAfter(self.__doPrinterConnectionUpdate, connection, extraInfo)
368                 if self._tempGraph is not None:
369                         temp = []
370                         for n in xrange(0, 4):
371                                 t = connection.getTemperature(0)
372                                 if t is not None:
373                                         temp.append(t)
374                                 else:
375                                         break
376                         self._tempGraph.addPoint(temp, [0] * len(temp), connection.getBedTemperature(), 0)
377
378         def __doPrinterConnectionUpdate(self, connection, extraInfo):
379                 t = time.time()
380                 if self._lastUpdateTime + 0.5 > t and extraInfo is None:
381                         return
382                 self._lastUpdateTime = t
383
384                 if extraInfo is not None and len(extraInfo) > 0:
385                         self._addTermLog('< %s\n' % (extraInfo))
386
387                 self._updateButtonStates()
388                 isPrinting = connection.isPrinting() or connection.isPaused()
389                 if self._progressBar is not None:
390                         if isPrinting:
391                                 self._progressBar.SetValue(connection.getPrintProgress() * 1000)
392                         else:
393                                 self._progressBar.SetValue(0)
394                 info = connection.getStatusString()
395                 info += '\n'
396                 if self._printerConnection.getTemperature(0) is not None:
397                         info += 'Temperature: %d' % (self._printerConnection.getTemperature(0))
398                 if self._printerConnection.getBedTemperature() > 0:
399                         info += ' Bed: %d' % (self._printerConnection.getBedTemperature())
400                 if self._infoText is not None:
401                         self._infoText.SetLabel(info)
402                 else:
403                         self.SetTitle(info.replace('\n', ', '))
404                 if isPrinting != self._isPrinting:
405                         self._isPrinting = isPrinting
406                         preventComputerFromSleeping(self, self._isPrinting)
407
408 class printWindowBasic(wx.Frame):
409         """
410         Printing window for USB printing, network printing, and any other type of printer connection we can think off.
411         This is only a basic window with minimal information.
412         """
413         def __init__(self, parent, printerConnection):
414                 super(printWindowBasic, self).__init__(parent, -1, style=wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.CAPTION|wx.SYSTEM_MENU|wx.FRAME_TOOL_WINDOW|wx.FRAME_FLOAT_ON_PARENT, title=_("Printing on %s") % (printerConnection.getName()))
415                 self._printerConnection = printerConnection
416                 self._lastUpdateTime = 0
417                 self._isPrinting = False
418
419                 self.SetSizer(wx.BoxSizer())
420                 self.panel = wx.Panel(self)
421                 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
422                 self.sizer = wx.GridBagSizer(2, 2)
423                 self.panel.SetSizer(self.sizer)
424
425                 self.powerWarningText = wx.StaticText(parent=self.panel,
426                         id=-1,
427                         label=_("Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish."),
428                         style=wx.ALIGN_CENTER)
429                 self.powerWarningText.SetBackgroundColour('red')
430                 self.powerWarningText.SetForegroundColour('white')
431                 self.powerManagement = power.PowerManagement()
432                 self.powerWarningTimer = wx.Timer(self)
433                 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
434                 self.OnPowerWarningChange(None)
435                 self.powerWarningTimer.Start(10000)
436
437                 self.statsText = wx.StaticText(self.panel, -1, _("InfoLine from printer connection\nInfoLine from dialog\nExtra line\nMore lines for layout\nMore lines for layout\nMore lines for layout"))
438
439                 self.connectButton = wx.Button(self.panel, -1, _("Connect"))
440                 #self.loadButton = wx.Button(self.panel, -1, 'Load')
441                 self.printButton = wx.Button(self.panel, -1, _("Print"))
442                 self.pauseButton = wx.Button(self.panel, -1, _("Pause"))
443                 self.cancelButton = wx.Button(self.panel, -1, _("Cancel print"))
444                 self.errorLogButton = wx.Button(self.panel, -1, _("Error log"))
445                 self.progress = wx.Gauge(self.panel, -1, range=1000)
446
447                 self.sizer.Add(self.powerWarningText, pos=(0, 0), span=(1, 5), flag=wx.EXPAND|wx.BOTTOM, border=5)
448                 self.sizer.Add(self.statsText, pos=(1, 0), span=(1, 5), flag=wx.LEFT, border=5)
449                 self.sizer.Add(self.connectButton, pos=(2, 0))
450                 #self.sizer.Add(self.loadButton, pos=(2,1))
451                 self.sizer.Add(self.printButton, pos=(2, 1))
452                 self.sizer.Add(self.pauseButton, pos=(2, 2))
453                 self.sizer.Add(self.cancelButton, pos=(2, 3))
454                 self.sizer.Add(self.errorLogButton, pos=(2, 4))
455                 self.sizer.Add(self.progress, pos=(3, 0), span=(1, 5), flag=wx.EXPAND)
456
457                 self.Bind(wx.EVT_CLOSE, self.OnClose)
458                 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
459                 #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad)
460                 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
461                 self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause)
462                 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
463                 self.errorLogButton.Bind(wx.EVT_BUTTON, self.OnErrorLog)
464
465                 self.Layout()
466                 self.Fit()
467                 self.Centre()
468
469                 self.progress.SetMinSize(self.progress.GetSize())
470                 self.statsText.SetLabel('\n\n\n\n\n\n')
471                 self._updateButtonStates()
472
473                 self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
474
475                 if self._printerConnection.hasActiveConnection() and not self._printerConnection.isActiveConnectionOpen():
476                         self._printerConnection.openActiveConnection()
477
478         def OnPowerWarningChange(self, e):
479                 type = self.powerManagement.get_providing_power_source_type()
480                 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
481                         self.powerWarningText.Hide()
482                         self.panel.Layout()
483                         self.Layout()
484                         self.Fit()
485                         self.Refresh()
486                 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
487                         self.powerWarningText.Show()
488                         self.panel.Layout()
489                         self.Layout()
490                         self.Fit()
491                         self.Refresh()
492
493         def OnClose(self, e):
494                 if self._printerConnection.hasActiveConnection():
495                         if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
496                                 pass #TODO: Give warning that the close will kill the print.
497                         self._printerConnection.closeActiveConnection()
498                 self._printerConnection.removeCallback(self._doPrinterConnectionUpdate)
499                 #TODO: When multiple printer windows are open, closing one will enable sleeping again.
500                 preventComputerFromSleeping(self, False)
501                 self.Destroy()
502
503         def OnConnect(self, e):
504                 self._printerConnection.openActiveConnection()
505
506         def OnLoad(self, e):
507                 pass
508
509         def OnPrint(self, e):
510                 self._printerConnection.startPrint()
511
512         def OnCancel(self, e):
513                 self._printerConnection.cancelPrint()
514
515         def OnPause(self, e):
516                 self._printerConnection.pause(not self._printerConnection.isPaused())
517
518         def OnErrorLog(self, e):
519                 LogWindow(self._printerConnection.getErrorLog())
520
521         def _doPrinterConnectionUpdate(self, connection, extraInfo = None):
522                 wx.CallAfter(self.__doPrinterConnectionUpdate, connection, extraInfo)
523                 #temp = [connection.getTemperature(0)]
524                 #self.temperatureGraph.addPoint(temp, [0], connection.getBedTemperature(), 0)
525
526         def __doPrinterConnectionUpdate(self, connection, extraInfo):
527                 now = time.time()
528                 if self._lastUpdateTime + 0.5 > now and extraInfo is None:
529                         return
530                 self._lastUpdateTime = now
531
532                 if extraInfo is not None and len(extraInfo) > 0:
533                         self._addTermLog('< %s\n' % (extraInfo))
534
535                 self._updateButtonStates()
536                 onGoingPrint = connection.isPrinting() or connection.isPaused()
537                 if onGoingPrint:
538                         self.progress.SetValue(connection.getPrintProgress() * 1000)
539                 else:
540                         self.progress.SetValue(0)
541                 info = connection.getStatusString()
542                 info += '\n'
543                 if self._printerConnection.getTemperature(0) is not None:
544                         info += 'Temperature: %d' % (self._printerConnection.getTemperature(0))
545                 if self._printerConnection.getBedTemperature() > 0:
546                         info += ' Bed: %d' % (self._printerConnection.getBedTemperature())
547                 info += '\n\n'
548                 self.statsText.SetLabel(info)
549                 if onGoingPrint != self._isPrinting:
550                         self._isPrinting = onGoingPrint
551                         preventComputerFromSleeping(self, self._isPrinting)
552
553         def _addTermLog(self, msg):
554                 pass
555
556         def _updateButtonStates(self):
557                 self.connectButton.Show(self._printerConnection.hasActiveConnection())
558                 self.connectButton.Enable(not self._printerConnection.isActiveConnectionOpen() and not self._printerConnection.isActiveConnectionOpening())
559                 self.pauseButton.Show(self._printerConnection.hasPause())
560                 if not self._printerConnection.hasActiveConnection() or self._printerConnection.isActiveConnectionOpen():
561                         self.printButton.Enable(not self._printerConnection.isPrinting() and \
562                                                                         not self._printerConnection.isPaused())
563                         self.pauseButton.Enable(self._printerConnection.isPrinting())
564                         self.cancelButton.Enable(self._printerConnection.isPrinting())
565                 else:
566                         self.printButton.Enable(False)
567                         self.pauseButton.Enable(False)
568                         self.cancelButton.Enable(False)
569                 self.errorLogButton.Show(self._printerConnection.isInErrorState())
570
571 class printWindowAdvanced(wx.Frame):
572         def __init__(self, parent, printerConnection):
573                 super(printWindowAdvanced, self).__init__(parent, -1, style=wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.CAPTION|wx.SYSTEM_MENU|wx.FRAME_FLOAT_ON_PARENT|wx.MINIMIZE_BOX, title=_("Printing on %s") % (printerConnection.getName()))
574                 self._printerConnection = printerConnection
575                 self._lastUpdateTime = time.time()
576                 self._isPrinting = False
577
578                 self.SetSizer(wx.BoxSizer())
579                 self.panel = wx.Panel(self)
580                 self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND)
581                 self.sizer = wx.GridBagSizer(2, 2)
582                 self.panel.SetSizer(self.sizer)
583                 self.panel.SetBackgroundColour(wx.WHITE)
584                 self._termHistory = []
585                 self._termHistoryIdx = 0
586
587                 self._mapImage = wx.Image(resources.getPathForImage('print-window-map.png'))
588                 self._colorCommandMap = {}
589
590                 # Move X
591                 self._addMovementCommand(0, 0, 255, self._moveX, 100)
592                 self._addMovementCommand(0, 0, 240, self._moveX, 10)
593                 self._addMovementCommand(0, 0, 220, self._moveX, 1)
594                 self._addMovementCommand(0, 0, 200, self._moveX, 0.1)
595                 self._addMovementCommand(0, 0, 180, self._moveX, -0.1)
596                 self._addMovementCommand(0, 0, 160, self._moveX, -1)
597                 self._addMovementCommand(0, 0, 140, self._moveX, -10)
598                 self._addMovementCommand(0, 0, 120, self._moveX, -100)
599
600                 # Move Y
601                 self._addMovementCommand(0, 255, 0, self._moveY, -100)
602                 self._addMovementCommand(0, 240, 0, self._moveY, -10)
603                 self._addMovementCommand(0, 220, 0, self._moveY, -1)
604                 self._addMovementCommand(0, 200, 0, self._moveY, -0.1)
605                 self._addMovementCommand(0, 180, 0, self._moveY, 0.1)
606                 self._addMovementCommand(0, 160, 0, self._moveY, 1)
607                 self._addMovementCommand(0, 140, 0, self._moveY, 10)
608                 self._addMovementCommand(0, 120, 0, self._moveY, 100)
609
610                 # Move Z
611                 self._addMovementCommand(255, 0, 0, self._moveZ, -10)
612                 self._addMovementCommand(220, 0, 0, self._moveZ, -1)
613                 self._addMovementCommand(200, 0, 0, self._moveZ, -0.1)
614                 self._addMovementCommand(180, 0, 0, self._moveZ, 0.1)
615                 self._addMovementCommand(160, 0, 0, self._moveZ, 1)
616                 self._addMovementCommand(140, 0, 0, self._moveZ, 10)
617
618                 # Extrude/Retract
619                 self._addMovementCommand(255, 80, 0, self._moveE, 10)
620                 self._addMovementCommand(255, 180, 0, self._moveE, -10)
621
622                 # Home
623                 self._addMovementCommand(255, 255, 0, self._homeXYZ, None)
624                 self._addMovementCommand(240, 255, 0, self._homeXYZ, "X")
625                 self._addMovementCommand(220, 255, 0, self._homeXYZ, "Y")
626                 self._addMovementCommand(200, 255, 0, self._homeXYZ, "Z")
627
628                 self.powerWarningText = wx.StaticText(parent=self.panel,
629                         id=-1,
630                         label=_("Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish."),
631                         style=wx.ALIGN_CENTER)
632                 self.powerWarningText.SetBackgroundColour('red')
633                 self.powerWarningText.SetForegroundColour('white')
634                 self.powerManagement = power.PowerManagement()
635                 self.powerWarningTimer = wx.Timer(self)
636                 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
637                 self.OnPowerWarningChange(None)
638                 self.powerWarningTimer.Start(1000)
639
640                 self.connectButton = wx.Button(self.panel, -1, _("Connect"), size=(125, 30))
641                 self.printButton = wx.Button(self.panel, -1, _("Print"), size=(125, 30))
642                 self.cancelButton = wx.Button(self.panel, -1, _("Cancel"), size=(125, 30))
643                 self.errorLogButton = wx.Button(self.panel, -1, _("Error log"), size=(125, 30))
644                 self.motorsOffButton = wx.Button(self.panel, -1, _("Motors off"), size=(125, 30))
645                 self.movementBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
646                                 resources.getPathForImage('print-window.png'))), (0, 0))
647                 self.temperatureBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
648                                 resources.getPathForImage('print-window-temperature.png'))), (0, 0))
649                 self.temperatureField = TemperatureField(self.panel)
650                 self.temperatureBedBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
651                                 resources.getPathForImage('print-window-temperature-bed.png'))), (0, 0))
652                 self.temperatureBedField = TemperatureField(self.panel)
653                 self.temperatureGraph = TemperatureGraph(self.panel)
654                 self.progress = wx.Gauge(self.panel, -1, range=1000)
655
656                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
657                 self._termLog = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
658                 self._termLog.SetFont(f)
659                 self._termLog.SetEditable(0)
660                 self._termInput = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER)
661                 self._termInput.SetFont(f)
662
663                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput)
664                 self._termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
665
666                 self.sizer.Add(self.powerWarningText, pos=(0, 0), span=(1, 6), flag=wx.EXPAND|wx.BOTTOM, border=5)
667                 self.sizer.Add(self.connectButton, pos=(1, 0), flag=wx.LEFT, border=2)
668                 self.sizer.Add(self.printButton, pos=(1, 1), flag=wx.LEFT, border=2)
669                 self.sizer.Add(self.cancelButton, pos=(1, 2), flag=wx.LEFT, border=2)
670                 self.sizer.Add(self.errorLogButton, pos=(1, 4), flag=wx.LEFT, border=2)
671                 self.sizer.Add(self.motorsOffButton, pos=(1, 5), flag=wx.LEFT|wx.RIGHT, border=2)
672                 self.sizer.Add(self.movementBitmap, pos=(2, 0), span=(2, 3))
673                 self.sizer.Add(self.temperatureGraph, pos=(4, 0), span=(4, 2), flag=wx.EXPAND)
674                 self.sizer.Add(self.temperatureBitmap, pos=(4, 2), span=(1, 1), flag=wx.EXPAND)
675                 self.sizer.Add(self.temperatureField, pos=(5, 2), span=(1, 1), flag=wx.EXPAND)
676                 self.sizer.Add(self.temperatureBedBitmap, pos=(6, 2), span=(1, 1), flag=wx.EXPAND)
677                 self.sizer.Add(self.temperatureBedField, pos=(7, 2), span=(1, 1), flag=wx.EXPAND)
678                 self.sizer.Add(self._termLog, pos=(2, 3), span=(5, 3), flag=wx.EXPAND)
679                 self.sizer.Add(self._termInput, pos=(7, 3), span=(1, 3), flag=wx.EXPAND)
680                 self.sizer.Add(self.progress, pos=(8, 0), span=(1, 6), flag=wx.EXPAND|wx.BOTTOM)
681
682                 self.Bind(wx.EVT_SIZE, self.OnSize)
683                 self.Bind(wx.EVT_CLOSE, self.OnClose)
684                 self.movementBitmap.Bind(wx.EVT_LEFT_DOWN, self.OnLeftClick)
685                 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
686                 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
687                 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
688                 self.errorLogButton.Bind(wx.EVT_BUTTON, self.OnErrorLog)
689                 self.motorsOffButton.Bind(wx.EVT_BUTTON, self.OnMotorsOff)
690
691                 self.Layout()
692                 self.Fit()
693                 self.Refresh()
694                 self.progress.SetMinSize(self.progress.GetSize())
695                 self._updateButtonStates()
696
697                 self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
698
699                 if self._printerConnection.hasActiveConnection() and \
700                    not self._printerConnection.isActiveConnectionOpen():
701                         self._printerConnection.openActiveConnection()
702
703         def OnSize(self, e):
704                 # HACK ALERT: This is needed for some reason otherwise the window
705                 # will be bigger than it should be until a power warning change
706                 self.Layout()
707                 self.Fit()
708
709         def OnClose(self, e):
710                 if self._printerConnection.hasActiveConnection():
711                         if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
712                                 pass #TODO: Give warning that the close will kill the print.
713                         self._printerConnection.closeActiveConnection()
714                 self._printerConnection.removeCallback(self._doPrinterConnectionUpdate)
715                 #TODO: When multiple printer windows are open, closing one will enable sleeping again.
716                 preventComputerFromSleeping(self, False)
717                 self._printerConnection.cancelPrint()
718                 self.Destroy()
719
720         def OnPowerWarningChange(self, e):
721                 type = self.powerManagement.get_providing_power_source_type()
722                 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
723                         self.powerWarningText.Hide()
724                         self.panel.Layout()
725                         self.Layout()
726                         self.Fit()
727                         self.Refresh()
728                 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
729                         self.powerWarningText.Show()
730                         self.panel.Layout()
731                         self.Layout()
732                         self.Fit()
733                         self.Refresh()
734
735         def script_addTextButton(self, r_text, g_text, b_text, r_button, g_button, b_button, button_text, command, data):
736                 x_text, y_text, w_text, h_text = self._getColoredRect(r_text, g_text, b_text)
737                 if x_text < 0:
738                         return
739                 x_button, y_button, w_button, h_button = self._getColoredRect(r_button, g_button, b_button)
740                 if x_button < 0:
741                         return
742                 from wx.lib.intctrl import IntCtrl
743                 text = IntCtrl(self, -1)
744                 text.SetBounds(0, 300)
745                 text.SetPosition((x_text, y_text))
746                 text.SetSize((w_text, h_text))
747
748                 button = wx.Button(self, -1, _(button_text))
749                 button.SetPosition((x_button, y_button))
750                 button.SetSize((w_button, h_button))
751                 button.command = command
752                 button.data = data
753                 self._buttonList.append(button)
754                 self.Bind(wx.EVT_BUTTON, lambda e: command(data % text.GetValue()), button)
755
756         def OnConnect(self, e):
757                 self._printerConnection.openActiveConnection()
758
759         def OnPrint(self, e):
760                 if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
761                         self._printerConnection.pause(not self._printerConnection.isPaused())
762                 else:
763                         self._printerConnection.startPrint()
764
765         def OnCancel(self, e):
766                 self._printerConnection.cancelPrint()
767
768         def OnErrorLog(self, e):
769                 LogWindow(self._printerConnection.getErrorLog())
770
771         def OnMotorsOff(self, e):
772                 self._printerConnection.sendCommand("M18")
773
774         def GetMapRGB(self, x, y):
775                 r = self._mapImage.GetRed(x, y)
776                 g = self._mapImage.GetGreen(x, y)
777                 b = self._mapImage.GetBlue(x, y)
778                 return (r, g, b)
779
780         def OnLeftClick(self, e):
781                 (r, g, b) = self.GetMapRGB(e.GetX(), e.GetY())
782                 if (r, g, b) in self._colorCommandMap:
783                         command = self._colorCommandMap[(r, g, b)]
784                         command[0](command[1])
785
786         def _addMovementCommand(self, r, g, b, command, step):
787                 self._colorCommandMap[(r, g, b)] = (command, step)
788
789         def _moveXYZE(self, step, feedrate):
790                 if (not self._printerConnection.hasActiveConnection() or \
791                         self._printerConnection.isActiveConnectionOpen()) and \
792                         (not self._printerConnection.isPaused() and \
793                          not self._printerConnection.isPrinting()):
794                         self._printerConnection.sendCommand("G91")
795                         self._printerConnection.sendCommand("G1 X%d F%d" % (step, feedrate))
796                         self._printerConnection.sendCommand("G90")
797
798         def _moveX(self, step):
799                 self._moveXYZE(step, 2000)
800
801         def _moveY(self, step):
802                 self._moveXYZE(step, 2000)
803
804         def _moveZ(self, step):
805                 self._moveXYZE(step, 200)
806
807         def _moveE(self, step):
808                 self._moveXYZE(step, 120)
809
810         def _homeXYZ(self, direction):
811                 if not self._printerConnection.isPaused() and not self._printerConnection.isPrinting():
812                         if direction is None:
813                                 self._printerConnection.sendCommand("G28")
814                         else:
815                                 self._printerConnection.sendCommand("G28 %s0" % direction)
816
817         def OnTermEnterLine(self, e):
818                 if not self._printerConnection.isAbleToSendDirectCommand():
819                         return
820                 line = self._termInput.GetValue()
821                 if line == '':
822                         return
823                 self._addTermLog('> %s\n' % (line))
824                 self._printerConnection.sendCommand(line)
825                 self._termHistory.append(line)
826                 self._termHistoryIdx = len(self._termHistory)
827                 self._termInput.SetValue('')
828
829         def OnTermKey(self, e):
830                 if len(self._termHistory) > 0:
831                         if e.GetKeyCode() == wx.WXK_UP:
832                                 self._termHistoryIdx -= 1
833                                 if self._termHistoryIdx < 0:
834                                         self._termHistoryIdx = len(self._termHistory) - 1
835                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
836                         if e.GetKeyCode() == wx.WXK_DOWN:
837                                 self._termHistoryIdx -= 1
838                                 if self._termHistoryIdx >= len(self._termHistory):
839                                         self._termHistoryIdx = 0
840                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
841                 e.Skip()
842
843         def _addTermLog(self, line):
844                 if self._termLog is not None:
845                         if len(self._termLog.GetValue()) > 10000:
846                                 self._termLog.SetValue(self._termLog.GetValue()[-10000:])
847                         self._termLog.SetInsertionPointEnd()
848                         if type(line) != unicode:
849                                 line = unicode(line, 'utf-8', 'replace')
850                         self._termLog.AppendText(line.encode('utf-8', 'replace'))
851
852         def _updateButtonStates(self):
853                 self.connectButton.Show(self._printerConnection.hasActiveConnection())
854                 self.connectButton.Enable(not self._printerConnection.isActiveConnectionOpen() and \
855                                                                   not self._printerConnection.isActiveConnectionOpening())
856                 if not self._printerConnection.hasPause():
857                         if not self._printerConnection.hasActiveConnection() or \
858                            self._printerConnection.isActiveConnectionOpen():
859                                 self.printButton.Enable(not self._printerConnection.isPrinting() and \
860                                                                                 not self._printerConnection.isPaused())
861                         else:
862                                 self.printButton.Enable(False)
863                 else:
864                         if not self._printerConnection.hasActiveConnection() or \
865                            self._printerConnection.isActiveConnectionOpen():
866                                 if self._printerConnection.isPrinting():
867                                         self.printButton.SetLabel(_("Pause"))
868                                 else:
869                                         if self._printerConnection.isPaused():
870                                                 self.printButton.SetLabel(_("Resume"))
871                                         else:
872                                                 self.printButton.SetLabel(_("Print"))
873                                 self.printButton.Enable(True)
874                         else:
875                                 self.printButton.Enable(False)
876                 if not self._printerConnection.hasActiveConnection() or \
877                    self._printerConnection.isActiveConnectionOpen():
878                         self.cancelButton.Enable(self._printerConnection.isPrinting() or \
879                                                                          self._printerConnection.isPaused())
880                 else:
881                         self.cancelButton.Enable(False)
882                 self.errorLogButton.Show(self._printerConnection.isInErrorState())
883                 self._termInput.Enable(self._printerConnection.isAbleToSendDirectCommand())
884
885         def _doPrinterConnectionUpdate(self, connection, extraInfo = None):
886                 wx.CallAfter(self.__doPrinterConnectionUpdate, connection, extraInfo)
887                 if self.temperatureGraph is not None:
888                         temp = []
889                         for n in xrange(0, 4):
890                                 t = connection.getTemperature(0)
891                                 if t is not None:
892                                         temp.append(t)
893                                 else:
894                                         break
895                         self.temperatureGraph.addPoint(temp, [0] * len(temp), connection.getBedTemperature(), 0)
896
897         def __doPrinterConnectionUpdate(self, connection, extraInfo):
898                 t = time.time()
899                 if self._lastUpdateTime + 0.5 > t and extraInfo is None:
900                         return
901                 self._lastUpdateTime = t
902
903                 if extraInfo is not None and len(extraInfo) > 0:
904                         self._addTermLog('< %s\n' % (extraInfo))
905
906                 self._updateButtonStates()
907                 isPrinting = connection.isPrinting() or connection.isPaused()
908                 if isPrinting:
909                         self.progress.SetValue(connection.getPrintProgress() * 1000)
910                 else:
911                         self.progress.SetValue(0)
912                 info = connection.getStatusString()
913                 info += '\n'
914                 if self._printerConnection.getTemperature(0) is not None:
915                         info += 'Temperature: %d' % (self._printerConnection.getTemperature(0))
916                 if self._printerConnection.getBedTemperature() > 0:
917                         info += ' Bed: %d' % (self._printerConnection.getBedTemperature())
918                 self.SetTitle(info.replace('\n', ', '))
919                 if isPrinting != self._isPrinting:
920                         self._isPrinting = isPrinting
921                         preventComputerFromSleeping(self, self._isPrinting)
922
923 class TemperatureField(wx.Panel):
924         def __init__(self, parent, callback):
925                 super(TemperatureField, self).__init__(parent)
926                 self.callback = callback
927                 
928                 self.SetBackgroundColour(wx.WHITE)
929                 self.sizer = wx.BoxSizer()
930                 self.SetSizer(self.sizer)
931                 self.sizer.SetOrientation(wx.HORIZONTAL)
932
933                 self.text = IntCtrl(self, -1)
934                 self.text.SetBounds(0, 300)
935                 self.sizer.Add(self.text, 1, flag=wx.EXPAND)
936
937                 self.button = wx.Button(self, -1, _("Set"))
938                 self.sizer.Add(self.button, 0)
939                 self.Bind(wx.EVT_BUTTON, lambda e: self.callback(self.text.GetValue()), self.button)
940
941
942 class TemperatureGraph(wx.Panel):
943         def __init__(self, parent):
944                 super(TemperatureGraph, self).__init__(parent)
945
946                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
947                 self.Bind(wx.EVT_SIZE, self.OnSize)
948                 self.Bind(wx.EVT_PAINT, self.OnDraw)
949
950                 self._lastDraw = time.time() - 1.0
951                 self._points = []
952                 self._backBuffer = None
953                 self.addPoint([0]*16, [0]*16, 0, 0)
954
955         def OnEraseBackground(self, e):
956                 pass
957
958         def OnSize(self, e):
959                 if self._backBuffer is None or self.GetSize() != self._backBuffer.GetSize():
960                         self._backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
961                         self.UpdateDrawing(True)
962
963         def OnDraw(self, e):
964                 dc = wx.BufferedPaintDC(self, self._backBuffer)
965
966         def UpdateDrawing(self, force=False):
967                 now = time.time()
968                 if (not force and now - self._lastDraw < 1.0) or self._backBuffer is None:
969                         return
970                 self._lastDraw = now
971                 dc = wx.MemoryDC()
972                 dc.SelectObject(self._backBuffer)
973                 dc.Clear()
974                 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
975                 w, h = self.GetSizeTuple()
976                 bgLinePen = wx.Pen('#A0A0A0')
977                 tempPen = wx.Pen('#FF4040')
978                 tempSPPen = wx.Pen('#FFA0A0')
979                 tempPenBG = wx.Pen('#FFD0D0')
980                 bedTempPen = wx.Pen('#4040FF')
981                 bedTempSPPen = wx.Pen('#A0A0FF')
982                 bedTempPenBG = wx.Pen('#D0D0FF')
983
984                 #Draw the background up to the current temperatures.
985                 x0 = 0
986                 t0 = []
987                 bt0 = 0
988                 tSP0 = 0
989                 btSP0 = 0
990                 for temp, tempSP, bedTemp, bedTempSP, t in self._points:
991                         x1 = int(w - (now - t))
992                         for x in xrange(x0, x1 + 1):
993                                 for n in xrange(0, min(len(t0), len(temp))):
994                                         t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
995                                         dc.SetPen(tempPenBG)
996                                         dc.DrawLine(x, h, x, h - (t * h / 350))
997                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
998                                 dc.SetPen(bedTempPenBG)
999                                 dc.DrawLine(x, h, x, h - (bt * h / 350))
1000                         t0 = temp
1001                         bt0 = bedTemp
1002                         tSP0 = tempSP
1003                         btSP0 = bedTempSP
1004                         x0 = x1 + 1
1005
1006                 #Draw the grid
1007                 for x in xrange(w, 0, -30):
1008                         dc.SetPen(bgLinePen)
1009                         dc.DrawLine(x, 0, x, h)
1010                 tmpNr = 0
1011                 for y in xrange(h - 1, 0, -h * 50 / 350):
1012                         dc.SetPen(bgLinePen)
1013                         dc.DrawLine(0, y, w, y)
1014                         dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
1015                         tmpNr += 50
1016                 dc.DrawLine(0, 0, w, 0)
1017                 dc.DrawLine(0, 0, 0, h)
1018
1019                 #Draw the main lines
1020                 x0 = 0
1021                 t0 = []
1022                 bt0 = 0
1023                 tSP0 = []
1024                 btSP0 = 0
1025                 for temp, tempSP, bedTemp, bedTempSP, t in self._points:
1026                         x1 = int(w - (now - t))
1027                         for x in xrange(x0, x1 + 1):
1028                                 for n in xrange(0, min(len(t0), len(temp))):
1029                                         t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
1030                                         tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP[n] - tSP0[n]) + tSP0[n]
1031                                         dc.SetPen(tempSPPen)
1032                                         dc.DrawPoint(x, h - (tSP * h / 350))
1033                                         dc.SetPen(tempPen)
1034                                         dc.DrawPoint(x, h - (t * h / 350))
1035                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
1036                                 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
1037                                 dc.SetPen(bedTempSPPen)
1038                                 dc.DrawPoint(x, h - (btSP * h / 350))
1039                                 dc.SetPen(bedTempPen)
1040                                 dc.DrawPoint(x, h - (bt * h / 350))
1041                         t0 = temp
1042                         bt0 = bedTemp
1043                         tSP0 = tempSP
1044                         btSP0 = bedTempSP
1045                         x0 = x1 + 1
1046
1047                 del dc
1048                 self.Refresh(eraseBackground=False)
1049                 self.Update()
1050
1051                 if len(self._points) > 0 and (time.time() - self._points[0][4]) > w + 20:
1052                         self._points.pop(0)
1053
1054         def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
1055                 if len(self._points) > 0 and time.time() - self._points[-1][4] < 0.5:
1056                         return
1057                 for n in xrange(0, len(temp)):
1058                         if temp[n] is None:
1059                                 temp[n] = 0
1060                 for n in xrange(0, len(tempSP)):
1061                         if tempSP[n] is None:
1062                                 tempSP[n] = 0
1063                 if bedTemp is None:
1064                         bedTemp = 0
1065                 if bedTempSP is None:
1066                         bedTempSP = 0
1067                 self._points.append((temp[:], tempSP[:], bedTemp, bedTempSP, time.time()))
1068                 wx.CallAfter(self.UpdateDrawing)
1069
1070 class LogWindow(wx.Frame):
1071         def __init__(self, logText):
1072                 super(LogWindow, self).__init__(None, title=_("Error log"))
1073                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
1074                 self.SetSize((500, 400))
1075                 self.Show(True)