chiark / gitweb /
printWindow: Add full screen mode for the temperature graph
[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
585                 self._fullscreenTemperature = None
586                 self._termHistory = []
587                 self._termHistoryIdx = 0
588
589                 self._mapImage = wx.Image(resources.getPathForImage('print-window-map.png'))
590                 self._colorCommandMap = {}
591
592                 # Move X
593                 self._addMovementCommand(0, 0, 255, self._moveX, 100)
594                 self._addMovementCommand(0, 0, 240, self._moveX, 10)
595                 self._addMovementCommand(0, 0, 220, self._moveX, 1)
596                 self._addMovementCommand(0, 0, 200, self._moveX, 0.1)
597                 self._addMovementCommand(0, 0, 180, self._moveX, -0.1)
598                 self._addMovementCommand(0, 0, 160, self._moveX, -1)
599                 self._addMovementCommand(0, 0, 140, self._moveX, -10)
600                 self._addMovementCommand(0, 0, 120, self._moveX, -100)
601
602                 # Move Y
603                 self._addMovementCommand(0, 255, 0, self._moveY, -100)
604                 self._addMovementCommand(0, 240, 0, self._moveY, -10)
605                 self._addMovementCommand(0, 220, 0, self._moveY, -1)
606                 self._addMovementCommand(0, 200, 0, self._moveY, -0.1)
607                 self._addMovementCommand(0, 180, 0, self._moveY, 0.1)
608                 self._addMovementCommand(0, 160, 0, self._moveY, 1)
609                 self._addMovementCommand(0, 140, 0, self._moveY, 10)
610                 self._addMovementCommand(0, 120, 0, self._moveY, 100)
611
612                 # Move Z
613                 self._addMovementCommand(255, 0, 0, self._moveZ, 10)
614                 self._addMovementCommand(220, 0, 0, self._moveZ, 1)
615                 self._addMovementCommand(200, 0, 0, self._moveZ, 0.1)
616                 self._addMovementCommand(180, 0, 0, self._moveZ, -0.1)
617                 self._addMovementCommand(160, 0, 0, self._moveZ, -1)
618                 self._addMovementCommand(140, 0, 0, self._moveZ, -10)
619
620                 # Extrude/Retract
621                 self._addMovementCommand(255, 80, 0, self._moveE, 10)
622                 self._addMovementCommand(255, 180, 0, self._moveE, -10)
623
624                 # Home
625                 self._addMovementCommand(255, 255, 0, self._homeXYZ, None)
626                 self._addMovementCommand(240, 255, 0, self._homeXYZ, "X")
627                 self._addMovementCommand(220, 255, 0, self._homeXYZ, "Y")
628                 self._addMovementCommand(200, 255, 0, self._homeXYZ, "Z")
629
630                 self.powerWarningText = wx.StaticText(parent=self.panel,
631                         id=-1,
632                         label=_("Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish."),
633                         style=wx.ALIGN_CENTER)
634                 self.powerWarningText.SetBackgroundColour('red')
635                 self.powerWarningText.SetForegroundColour('white')
636                 self.powerManagement = power.PowerManagement()
637                 self.powerWarningTimer = wx.Timer(self)
638                 self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer)
639                 self.OnPowerWarningChange(None)
640                 self.powerWarningTimer.Start(1000)
641
642                 self.connectButton = wx.Button(self.panel, -1, _("Connect"), size=(125, 30))
643                 self.printButton = wx.Button(self.panel, -1, _("Print"), size=(125, 30))
644                 self.cancelButton = wx.Button(self.panel, -1, _("Cancel"), size=(125, 30))
645                 self.errorLogButton = wx.Button(self.panel, -1, _("Error log"), size=(125, 30))
646                 self.motorsOffButton = wx.Button(self.panel, -1, _("Motors off"), size=(125, 30))
647                 self.movementBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
648                                 resources.getPathForImage('print-window.png'))), (0, 0))
649                 self.temperatureBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
650                                 resources.getPathForImage('print-window-temperature.png'))), (0, 0))
651                 self.temperatureField = TemperatureField(self.panel)
652                 self.temperatureBedBitmap = wx.StaticBitmap(self.panel, -1, wx.BitmapFromImage(wx.Image(
653                                 resources.getPathForImage('print-window-temperature-bed.png'))), (0, 0))
654                 self.temperatureBedField = TemperatureField(self.panel)
655                 self.temperatureGraph = TemperatureGraph(self.panel)
656                 self.progress = wx.Gauge(self.panel, -1, range=1000)
657
658                 f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
659                 self._termLog = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP)
660                 self._termLog.SetFont(f)
661                 self._termLog.SetEditable(0)
662                 self._termInput = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER)
663                 self._termInput.SetFont(f)
664
665                 self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self._termInput)
666                 self._termInput.Bind(wx.EVT_CHAR, self.OnTermKey)
667
668                 self.sizer.Add(self.powerWarningText, pos=(0, 0), span=(1, 6), flag=wx.EXPAND|wx.BOTTOM, border=5)
669                 self.sizer.Add(self.connectButton, pos=(1, 0), flag=wx.LEFT, border=2)
670                 self.sizer.Add(self.printButton, pos=(1, 1), flag=wx.LEFT, border=2)
671                 self.sizer.Add(self.cancelButton, pos=(1, 2), flag=wx.LEFT, border=2)
672                 self.sizer.Add(self.errorLogButton, pos=(1, 4), flag=wx.LEFT, border=2)
673                 self.sizer.Add(self.motorsOffButton, pos=(1, 5), flag=wx.LEFT|wx.RIGHT, border=2)
674                 self.sizer.Add(self.movementBitmap, pos=(2, 0), span=(2, 3))
675                 self.sizer.Add(self.temperatureGraph, pos=(4, 0), span=(4, 2), flag=wx.EXPAND)
676                 self.sizer.Add(self.temperatureBitmap, pos=(4, 2), span=(1, 1), flag=wx.EXPAND)
677                 self.sizer.Add(self.temperatureField, pos=(5, 2), span=(1, 1), flag=wx.EXPAND)
678                 self.sizer.Add(self.temperatureBedBitmap, pos=(6, 2), span=(1, 1), flag=wx.EXPAND)
679                 self.sizer.Add(self.temperatureBedField, pos=(7, 2), span=(1, 1), flag=wx.EXPAND)
680                 self.sizer.Add(self._termLog, pos=(2, 3), span=(5, 3), flag=wx.EXPAND)
681                 self.sizer.Add(self._termInput, pos=(7, 3), span=(1, 3), flag=wx.EXPAND)
682                 self.sizer.Add(self.progress, pos=(8, 0), span=(1, 6), flag=wx.EXPAND|wx.BOTTOM)
683
684                 self.Bind(wx.EVT_SIZE, self.OnSize)
685                 self.Bind(wx.EVT_CLOSE, self.OnClose)
686                 self.movementBitmap.Bind(wx.EVT_LEFT_DOWN, self.OnMovementClick)
687                 self.temperatureGraph.Bind(wx.EVT_LEFT_DOWN, self.OnTemperatureClick)
688                 self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
689                 self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint)
690                 self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
691                 self.errorLogButton.Bind(wx.EVT_BUTTON, self.OnErrorLog)
692                 self.motorsOffButton.Bind(wx.EVT_BUTTON, self.OnMotorsOff)
693
694                 self.Layout()
695                 self.Fit()
696                 self.Refresh()
697                 self.progress.SetMinSize(self.progress.GetSize())
698                 self._updateButtonStates()
699
700                 self._printerConnection.addCallback(self._doPrinterConnectionUpdate)
701
702                 if self._printerConnection.hasActiveConnection() and \
703                    not self._printerConnection.isActiveConnectionOpen():
704                         self._printerConnection.openActiveConnection()
705
706         def OnSize(self, e):
707                 # HACK ALERT: This is needed for some reason otherwise the window
708                 # will be bigger than it should be until a power warning change
709                 self.Layout()
710                 self.Fit()
711
712         def OnClose(self, e):
713                 if self._printerConnection.hasActiveConnection():
714                         if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
715                                 pass #TODO: Give warning that the close will kill the print.
716                         self._printerConnection.closeActiveConnection()
717                 self._printerConnection.removeCallback(self._doPrinterConnectionUpdate)
718                 #TODO: When multiple printer windows are open, closing one will enable sleeping again.
719                 preventComputerFromSleeping(self, False)
720                 self._printerConnection.cancelPrint()
721                 self.Destroy()
722
723         def OnPowerWarningChange(self, e):
724                 type = self.powerManagement.get_providing_power_source_type()
725                 if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown():
726                         self.powerWarningText.Hide()
727                         self.panel.Layout()
728                         self.Layout()
729                         self.Fit()
730                         self.Refresh()
731                 elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown():
732                         self.powerWarningText.Show()
733                         self.panel.Layout()
734                         self.Layout()
735                         self.Fit()
736                         self.Refresh()
737
738         def script_addTextButton(self, r_text, g_text, b_text, r_button, g_button, b_button, button_text, command, data):
739                 x_text, y_text, w_text, h_text = self._getColoredRect(r_text, g_text, b_text)
740                 if x_text < 0:
741                         return
742                 x_button, y_button, w_button, h_button = self._getColoredRect(r_button, g_button, b_button)
743                 if x_button < 0:
744                         return
745                 from wx.lib.intctrl import IntCtrl
746                 text = IntCtrl(self, -1)
747                 text.SetBounds(0, 300)
748                 text.SetPosition((x_text, y_text))
749                 text.SetSize((w_text, h_text))
750
751                 button = wx.Button(self, -1, _(button_text))
752                 button.SetPosition((x_button, y_button))
753                 button.SetSize((w_button, h_button))
754                 button.command = command
755                 button.data = data
756                 self._buttonList.append(button)
757                 self.Bind(wx.EVT_BUTTON, lambda e: command(data % text.GetValue()), button)
758
759         def OnConnect(self, e):
760                 self._printerConnection.openActiveConnection()
761
762         def OnPrint(self, e):
763                 if self._printerConnection.isPrinting() or self._printerConnection.isPaused():
764                         self._printerConnection.pause(not self._printerConnection.isPaused())
765                 else:
766                         self._printerConnection.startPrint()
767
768         def OnCancel(self, e):
769                 self._printerConnection.cancelPrint()
770
771         def OnErrorLog(self, e):
772                 LogWindow(self._printerConnection.getErrorLog())
773
774         def OnMotorsOff(self, e):
775                 self._printerConnection.sendCommand("M18")
776
777         def GetMapRGB(self, x, y):
778                 r = self._mapImage.GetRed(x, y)
779                 g = self._mapImage.GetGreen(x, y)
780                 b = self._mapImage.GetBlue(x, y)
781                 return (r, g, b)
782
783         def OnMovementClick(self, e):
784                 (r, g, b) = self.GetMapRGB(e.GetX(), e.GetY())
785                 if (r, g, b) in self._colorCommandMap:
786                         command = self._colorCommandMap[(r, g, b)]
787                         command[0](command[1])
788
789         def _addMovementCommand(self, r, g, b, command, step):
790                 self._colorCommandMap[(r, g, b)] = (command, step)
791
792         def _moveXYZE(self, motor, step, feedrate):
793                 if (not self._printerConnection.hasActiveConnection() or \
794                         self._printerConnection.isActiveConnectionOpen()) and \
795                         (not self._printerConnection.isPaused() and \
796                          not self._printerConnection.isPrinting()):
797                         self._printerConnection.sendCommand("G91")
798                         self._printerConnection.sendCommand("G1 %s%.1f F%d" % (motor, step, feedrate))
799                         self._printerConnection.sendCommand("G90")
800
801         def _moveX(self, step):
802                 self._moveXYZE("X", step, 2000)
803
804         def _moveY(self, step):
805                 self._moveXYZE("Y", step, 2000)
806
807         def _moveZ(self, step):
808                 self._moveXYZE("Z", step, 200)
809
810         def _moveE(self, step):
811                 self._moveXYZE("E", step, 120)
812
813         def _homeXYZ(self, direction):
814                 if not self._printerConnection.isPaused() and not self._printerConnection.isPrinting():
815                         if direction is None:
816                                 self._printerConnection.sendCommand("G28")
817                         else:
818                                 self._printerConnection.sendCommand("G28 %s0" % direction)
819
820         def _setHotendTemperature(self, value):
821                 self._printerConnection.sendCommand("M104 S%d" % value)
822
823         def _setBedTemperature(self, value):
824                 self._printerConnection.sendCommand("M140 S%d" % value)
825
826         def OnTemperatureClick(self, e):
827                 wx.CallAfter(self.ToggleFullScreenTemperature)
828
829         def ToggleFullScreenTemperature(self):
830                 sizer = self.GetSizer()
831                 if self._fullscreenTemperature:
832                         self._fullscreenTemperature.Show(False)
833                         sizer.Detach(self._fullscreenTemperature)
834                         self._fullscreenTemperature.Destroy()
835                         self._fullscreenTemperature = None
836                         self.panel.Show(True)
837                 else:
838                         self._fullscreenTemperature = self.temperatureGraph.Clone(self)
839                         self._fullscreenTemperature.Bind(wx.EVT_LEFT_DOWN, self.OnTemperatureClick)
840                         sizer.Add(self._fullscreenTemperature, 1, flag=wx.EXPAND)
841                         self.panel.Show(False)
842                 self.Layout()
843                 self.Refresh()
844
845         def OnTermEnterLine(self, e):
846                 if not self._printerConnection.isAbleToSendDirectCommand():
847                         return
848                 line = self._termInput.GetValue()
849                 if line == '':
850                         return
851                 self._addTermLog('> %s\n' % (line))
852                 self._printerConnection.sendCommand(line)
853                 self._termHistory.append(line)
854                 self._termHistoryIdx = len(self._termHistory)
855                 self._termInput.SetValue('')
856
857         def OnTermKey(self, e):
858                 if len(self._termHistory) > 0:
859                         if e.GetKeyCode() == wx.WXK_UP:
860                                 self._termHistoryIdx -= 1
861                                 if self._termHistoryIdx < 0:
862                                         self._termHistoryIdx = len(self._termHistory) - 1
863                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
864                         if e.GetKeyCode() == wx.WXK_DOWN:
865                                 self._termHistoryIdx -= 1
866                                 if self._termHistoryIdx >= len(self._termHistory):
867                                         self._termHistoryIdx = 0
868                                 self._termInput.SetValue(self._termHistory[self._termHistoryIdx])
869                 e.Skip()
870
871         def _addTermLog(self, line):
872                 if self._termLog is not None:
873                         if len(self._termLog.GetValue()) > 10000:
874                                 self._termLog.SetValue(self._termLog.GetValue()[-10000:])
875                         self._termLog.SetInsertionPointEnd()
876                         if type(line) != unicode:
877                                 line = unicode(line, 'utf-8', 'replace')
878                         self._termLog.AppendText(line.encode('utf-8', 'replace'))
879
880         def _updateButtonStates(self):
881                 self.connectButton.Show(self._printerConnection.hasActiveConnection())
882                 self.connectButton.Enable(not self._printerConnection.isActiveConnectionOpen() and \
883                                                                   not self._printerConnection.isActiveConnectionOpening())
884                 if not self._printerConnection.hasPause():
885                         if not self._printerConnection.hasActiveConnection() or \
886                            self._printerConnection.isActiveConnectionOpen():
887                                 self.printButton.Enable(not self._printerConnection.isPrinting() and \
888                                                                                 not self._printerConnection.isPaused())
889                         else:
890                                 self.printButton.Enable(False)
891                 else:
892                         if not self._printerConnection.hasActiveConnection() or \
893                            self._printerConnection.isActiveConnectionOpen():
894                                 if self._printerConnection.isPrinting():
895                                         self.printButton.SetLabel(_("Pause"))
896                                 else:
897                                         if self._printerConnection.isPaused():
898                                                 self.printButton.SetLabel(_("Resume"))
899                                         else:
900                                                 self.printButton.SetLabel(_("Print"))
901                                 self.printButton.Enable(True)
902                         else:
903                                 self.printButton.Enable(False)
904                 if not self._printerConnection.hasActiveConnection() or \
905                    self._printerConnection.isActiveConnectionOpen():
906                         self.cancelButton.Enable(self._printerConnection.isPrinting() or \
907                                                                          self._printerConnection.isPaused())
908                 else:
909                         self.cancelButton.Enable(False)
910                 self.errorLogButton.Show(self._printerConnection.isInErrorState())
911                 self._termInput.Enable(self._printerConnection.isAbleToSendDirectCommand())
912
913         def _doPrinterConnectionUpdate(self, connection, extraInfo = None):
914                 wx.CallAfter(self.__doPrinterConnectionUpdate, connection, extraInfo)
915                 temp = []
916                 for n in xrange(0, 4):
917                         t = connection.getTemperature(0)
918                         if t is not None:
919                                 temp.append(t)
920                         else:
921                                 break
922                 self.temperatureGraph.addPoint(temp, [0] * len(temp), connection.getBedTemperature(), 0)
923                 if self._fullscreenTemperature is not None:
924                         self._fullscreenTemperature.addPoint(temp, [0] * len(temp), connection.getBedTemperature(), 0)
925
926         def __doPrinterConnectionUpdate(self, connection, extraInfo):
927                 t = time.time()
928                 if self._lastUpdateTime + 0.5 > t and extraInfo is None:
929                         return
930                 self._lastUpdateTime = t
931
932                 if extraInfo is not None and len(extraInfo) > 0:
933                         self._addTermLog('< %s\n' % (extraInfo))
934
935                 self._updateButtonStates()
936                 isPrinting = connection.isPrinting() or connection.isPaused()
937                 if isPrinting:
938                         self.progress.SetValue(connection.getPrintProgress() * 1000)
939                 else:
940                         self.progress.SetValue(0)
941                 info = connection.getStatusString()
942                 info += '\n'
943                 if self._printerConnection.getTemperature(0) is not None:
944                         info += 'Temperature: %d' % (self._printerConnection.getTemperature(0))
945                 if self._printerConnection.getBedTemperature() > 0:
946                         info += ' Bed: %d' % (self._printerConnection.getBedTemperature())
947                 self.SetTitle(info.replace('\n', ', '))
948                 if isPrinting != self._isPrinting:
949                         self._isPrinting = isPrinting
950                         preventComputerFromSleeping(self, self._isPrinting)
951
952 class TemperatureField(wx.Panel):
953         def __init__(self, parent, callback):
954                 super(TemperatureField, self).__init__(parent)
955                 self.callback = callback
956                 
957                 self.SetBackgroundColour(wx.WHITE)
958                 self.sizer = wx.BoxSizer()
959                 self.SetSizer(self.sizer)
960                 self.sizer.SetOrientation(wx.HORIZONTAL)
961
962                 self.text = IntCtrl(self, -1)
963                 self.text.SetBounds(0, 300)
964                 self.sizer.Add(self.text, 1, flag=wx.EXPAND)
965
966                 self.button = wx.Button(self, -1, _("Set"))
967                 self.sizer.Add(self.button, 0)
968                 self.Bind(wx.EVT_BUTTON, lambda e: self.callback(self.text.GetValue()), self.button)
969
970
971 class TemperatureGraph(wx.Panel):
972         def __init__(self, parent):
973                 super(TemperatureGraph, self).__init__(parent)
974
975                 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
976                 self.Bind(wx.EVT_SIZE, self.OnSize)
977                 self.Bind(wx.EVT_PAINT, self.OnDraw)
978
979                 self._lastDraw = time.time() - 1.0
980                 self._points = []
981                 self._backBuffer = None
982                 self.addPoint([0]*16, [0]*16, 0, 0)
983
984         def Clone(self, parent):
985                 clone = TemperatureGraph(parent)
986                 clone._points = list(self._points)
987                 return clone
988
989         def OnEraseBackground(self, e):
990                 pass
991
992         def OnSize(self, e):
993                 if self._backBuffer is None or self.GetSize() != self._backBuffer.GetSize():
994                         self._backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
995                         self.UpdateDrawing(True)
996
997         def OnDraw(self, e):
998                 dc = wx.BufferedPaintDC(self, self._backBuffer)
999
1000         def UpdateDrawing(self, force=False):
1001                 now = time.time()
1002                 if (not force and now - self._lastDraw < 1.0) or self._backBuffer is None:
1003                         return
1004                 self._lastDraw = now
1005                 dc = wx.MemoryDC()
1006                 dc.SelectObject(self._backBuffer)
1007                 dc.Clear()
1008                 dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT))
1009                 w, h = self.GetSizeTuple()
1010                 bgLinePen = wx.Pen('#A0A0A0')
1011                 tempPen = wx.Pen('#FF4040')
1012                 tempSPPen = wx.Pen('#FFA0A0')
1013                 tempPenBG = wx.Pen('#FFD0D0')
1014                 bedTempPen = wx.Pen('#4040FF')
1015                 bedTempSPPen = wx.Pen('#A0A0FF')
1016                 bedTempPenBG = wx.Pen('#D0D0FF')
1017
1018                 #Draw the background up to the current temperatures.
1019                 x0 = 0
1020                 t0 = []
1021                 bt0 = 0
1022                 tSP0 = 0
1023                 btSP0 = 0
1024                 for temp, tempSP, bedTemp, bedTempSP, t in self._points:
1025                         x1 = int(w - (now - t))
1026                         for x in xrange(x0, x1 + 1):
1027                                 for n in xrange(0, min(len(t0), len(temp))):
1028                                         t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
1029                                         dc.SetPen(tempPenBG)
1030                                         dc.DrawLine(x, h, x, h - (t * h / 350))
1031                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
1032                                 dc.SetPen(bedTempPenBG)
1033                                 dc.DrawLine(x, h, x, h - (bt * h / 350))
1034                         t0 = temp
1035                         bt0 = bedTemp
1036                         tSP0 = tempSP
1037                         btSP0 = bedTempSP
1038                         x0 = x1 + 1
1039
1040                 #Draw the grid
1041                 for x in xrange(w, 0, -30):
1042                         dc.SetPen(bgLinePen)
1043                         dc.DrawLine(x, 0, x, h)
1044                 tmpNr = 0
1045                 for y in xrange(h - 1, 0, -h * 50 / 350):
1046                         dc.SetPen(bgLinePen)
1047                         dc.DrawLine(0, y, w, y)
1048                         dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight())
1049                         tmpNr += 50
1050                 dc.DrawLine(0, 0, w, 0)
1051                 dc.DrawLine(0, 0, 0, h)
1052
1053                 #Draw the main lines
1054                 x0 = 0
1055                 t0 = []
1056                 bt0 = 0
1057                 tSP0 = []
1058                 btSP0 = 0
1059                 for temp, tempSP, bedTemp, bedTempSP, t in self._points:
1060                         x1 = int(w - (now - t))
1061                         for x in xrange(x0, x1 + 1):
1062                                 for n in xrange(0, min(len(t0), len(temp))):
1063                                         t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
1064                                         tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP[n] - tSP0[n]) + tSP0[n]
1065                                         dc.SetPen(tempSPPen)
1066                                         dc.DrawPoint(x, h - (tSP * h / 350))
1067                                         dc.SetPen(tempPen)
1068                                         dc.DrawPoint(x, h - (t * h / 350))
1069                                 bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
1070                                 btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
1071                                 dc.SetPen(bedTempSPPen)
1072                                 dc.DrawPoint(x, h - (btSP * h / 350))
1073                                 dc.SetPen(bedTempPen)
1074                                 dc.DrawPoint(x, h - (bt * h / 350))
1075                         t0 = temp
1076                         bt0 = bedTemp
1077                         tSP0 = tempSP
1078                         btSP0 = bedTempSP
1079                         x0 = x1 + 1
1080
1081                 del dc
1082                 self.Refresh(eraseBackground=False)
1083                 self.Update()
1084
1085                 if len(self._points) > 0 and (time.time() - self._points[0][4]) > w + 20:
1086                         self._points.pop(0)
1087
1088         def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
1089                 if len(self._points) > 0 and time.time() - self._points[-1][4] < 0.5:
1090                         return
1091                 for n in xrange(0, len(temp)):
1092                         if temp[n] is None:
1093                                 temp[n] = 0
1094                 for n in xrange(0, len(tempSP)):
1095                         if tempSP[n] is None:
1096                                 tempSP[n] = 0
1097                 if bedTemp is None:
1098                         bedTemp = 0
1099                 if bedTempSP is None:
1100                         bedTempSP = 0
1101                 self._points.append((temp[:], tempSP[:], bedTemp, bedTempSP, time.time()))
1102                 wx.CallAfter(self.UpdateDrawing)
1103
1104 class LogWindow(wx.Frame):
1105         def __init__(self, logText):
1106                 super(LogWindow, self).__init__(None, title=_("Error log"))
1107                 self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY)
1108                 self.SetSize((500, 400))
1109                 self.Show(True)