chiark / gitweb /
Change how the engine is interfaced from the python code. Put the GCode viewer in...
[cura.git] / Cura / gui / printWindow.py
index 3a57bb45833d2b6f7085de9c0ccf2120c29bcf46..1a0e2cbe116c63a3b67bb6a8ac49c121a8a23609 100644 (file)
@@ -1,5 +1,5 @@
-# coding=utf-8
 from __future__ import absolute_import
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import threading
 import re
@@ -7,6 +7,9 @@ import subprocess
 import sys
 import time
 import platform
+import os
+import power
+import datetime
 
 import wx
 from wx.lib import buttons
@@ -15,64 +18,98 @@ from Cura.gui.util import webcam
 from Cura.gui.util import taskbar
 from Cura.util import machineCom
 from Cura.util import gcodeInterpreter
-from Cura.util import power
-from Cura.util.resources import getPathForImage
-
-printWindowMonitorHandle = None
-
-def printFile(filename):
-       global printWindowMonitorHandle
-       if printWindowMonitorHandle == None:
-               printWindowMonitorHandle = printProcessMonitor()
-       printWindowMonitorHandle.loadFile(filename)
-
-
-def startPrintInterface(filename):
-       #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
-       # It needs to run in a seperate process, as any running python code blocks the GCode sender pyton code (http://wiki.python.org/moin/GlobalInterpreterLock).
-       app = wx.App(False)
-       printWindowHandle = printWindow()
-       printWindowHandle.Show(True)
-       printWindowHandle.Raise()
-       printWindowHandle.OnConnect(None)
-       t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
-       t.daemon = True
-       t.start()
-       app.MainLoop()
-
+from Cura.util import resources
+from Cura.util import profile
 
+#The printProcessMonitor is used from the main GUI python process. This monitors the printing python process.
+# This class also handles starting of the 2nd process for printing and all communications with it.
 class printProcessMonitor():
-       def __init__(self):
+       def __init__(self, callback = None):
                self.handle = None
-
-       def loadFile(self, filename):
-               if self.handle == None:
-                       cmdList = [sys.executable, sys.argv[0], '-r', filename]
+               self._state = 'CLOSED'
+               self._z = 0.0
+               self._callback = callback
+               self._id = -1
+
+       def loadFile(self, filename, id):
+               if self.handle is None:
+                       if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
+                               cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
+                       else:
+                               cmdList = [sys.executable, '-m', 'Cura.cura']
+                       cmdList.append('-r')
+                       cmdList.append(filename)
                        if platform.system() == "Darwin":
                                if platform.machine() == 'i386':
                                        cmdList.insert(0, 'arch')
                                        cmdList.insert(1, '-i386')
-                       self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                               stderr=subprocess.PIPE)
+                       #Save the preferences before starting the print window so we use the proper machine settings.
+                       profile.savePreferences(profile.getPreferencePath())
+                       self.handle = subprocess.Popen(cmdList, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                        self.thread = threading.Thread(target=self.Monitor)
                        self.thread.start()
                else:
-                       self.handle.stdin.write(filename + '\n')
+                       self.handle.stdin.write('LOAD:%s\n' % filename)
+               self._id = id
 
        def Monitor(self):
                p = self.handle
                line = p.stdout.readline()
-               while(len(line) > 0):
-                       #print line.rstrip()
+               while len(line) > 0:
+                       line = line.rstrip()
+                       try:
+                               if line.startswith('Z:'):
+                                       self._z = float(line[2:])
+                                       self._callCallback()
+                               elif line.startswith('STATE:'):
+                                       self._state = line[6:]
+                                       self._callCallback()
+                               else:
+                                       print '>' + line.rstrip()
+                       except:
+                               print sys.exc_info()
                        line = p.stdout.readline()
+               line = p.stderr.readline()
+               while len(line) > 0:
+                       print '>>' + line.rstrip()
+                       line = p.stderr.readline()
                p.communicate()
                self.handle = None
                self.thread = None
 
+       def getID(self):
+               return self._id
+
+       def getZ(self):
+               return self._z
+
+       def getState(self):
+               return self._state
+
+       def _callCallback(self):
+               if self._callback is not None:
+                       self._callback()
+
+def startPrintInterface(filename):
+       #startPrintInterface is called from the main script when we want the printer interface to run in a separate process.
+       # It needs to run in a separate process, as any running python code blocks the GCode sender python code (http://wiki.python.org/moin/GlobalInterpreterLock).
+
+       sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+
+       app = wx.App(False)
+       resources.setupLocalization(profile.getPreference('language'))
+       printWindowHandle = printWindow()
+       printWindowHandle.Show(True)
+       printWindowHandle.Raise()
+       printWindowHandle.OnConnect(None)
+       t = threading.Thread(target=printWindowHandle.LoadGCodeFile, args=(filename,))
+       t.daemon = True
+       t.start()
+       app.MainLoop()
 
 class PrintCommandButton(buttons.GenBitmapButton):
        def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)):
-               self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename))
+               self.bitmap = wx.Bitmap(resources.getPathForImage(bitmapFilename))
                super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
 
                self.commandList = commandList
@@ -84,7 +121,7 @@ class PrintCommandButton(buttons.GenBitmapButton):
                self.Bind(wx.EVT_BUTTON, self.OnClick)
 
        def OnClick(self, e):
-               if self.parent.machineCom == None or self.parent.machineCom.isPrinting():
+               if self.parent.machineCom is None or self.parent.machineCom.isPrinting():
                        return;
                for cmd in self.commandList:
                        self.parent.machineCom.sendCommand(cmd)
@@ -95,7 +132,8 @@ class printWindow(wx.Frame):
        "Main user interface window"
 
        def __init__(self):
-               super(printWindow, self).__init__(None, -1, title='Printing')
+               super(printWindow, self).__init__(None, -1, title=_("Printing"))
+               t = time.time()
                self.machineCom = None
                self.gcode = None
                self.gcodeList = None
@@ -113,10 +151,6 @@ class printWindow(wx.Frame):
                self.termHistoryIdx = 0
 
                self.cam = None
-               if webcam.hasWebcamSupport():
-                       self.cam = webcam.webcam()
-                       if not self.cam.hasCamera():
-                               self.cam = None
 
                self.SetSizer(wx.BoxSizer())
                self.panel = wx.Panel(self)
@@ -124,12 +158,12 @@ class printWindow(wx.Frame):
                self.sizer = wx.GridBagSizer(2, 2)
                self.panel.SetSizer(self.sizer)
 
-               sb = wx.StaticBox(self.panel, label="Statistics")
+               sb = wx.StaticBox(self.panel, label=_("Statistics"))
                boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
 
                self.powerWarningText = wx.StaticText(parent=self.panel,
                        id=-1,
-                       label="Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish.",
+                       label=_("Your computer is running on battery power.\nConnect your computer to AC power or your print might not finish."),
                        style=wx.ALIGN_CENTER)
                self.powerWarningText.SetBackgroundColour('red')
                self.powerWarningText.SetForegroundColour('white')
@@ -140,18 +174,17 @@ class printWindow(wx.Frame):
                self.OnPowerWarningChange(None)
                self.powerWarningTimer.Start(10000)
 
-               self.statsText = wx.StaticText(self.panel, -1,
-                       "Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
+               self.statsText = wx.StaticText(self.panel, -1, _("Filament: ####.##m #.##g\nEstimated print time: #####:##\nMachine state:\nDetecting baudrateXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"))
                boxsizer.Add(self.statsText, flag=wx.LEFT, border=5)
 
                self.sizer.Add(boxsizer, pos=(0, 0), span=(7, 1), flag=wx.EXPAND)
 
-               self.connectButton = wx.Button(self.panel, -1, 'Connect')
+               self.connectButton = wx.Button(self.panel, -1, _("Connect"))
                #self.loadButton = wx.Button(self.panel, -1, 'Load')
-               self.printButton = wx.Button(self.panel, -1, 'Print')
-               self.pauseButton = wx.Button(self.panel, -1, 'Pause')
-               self.cancelButton = wx.Button(self.panel, -1, 'Cancel print')
-               self.machineLogButton = wx.Button(self.panel, -1, 'Error log')
+               self.printButton = wx.Button(self.panel, -1, _("Print"))
+               self.pauseButton = wx.Button(self.panel, -1, _("Pause"))
+               self.cancelButton = wx.Button(self.panel, -1, _("Cancel print"))
+               self.machineLogButton = wx.Button(self.panel, -1, _("Error log"))
                self.progress = wx.Gauge(self.panel, -1)
 
                self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
@@ -163,6 +196,7 @@ class printWindow(wx.Frame):
                self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND)
 
                nb = wx.Notebook(self.panel)
+               self.tabs = nb
                self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
 
                self.temperaturePanel = wx.Panel(nb)
@@ -171,22 +205,23 @@ class printWindow(wx.Frame):
 
                self.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
                self.temperatureSelect.SetRange(0, 400)
-               self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, "BedTemp:")
-               self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21),
-                       style=wx.SP_ARROW_KEYS)
+               self.temperatureHeatUp = wx.Button(self.temperaturePanel, -1, str(int(profile.getProfileSettingFloat('print_temperature'))) + 'C')
+               self.bedTemperatureLabel = wx.StaticText(self.temperaturePanel, -1, _("BedTemp:"))
+               self.bedTemperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
                self.bedTemperatureSelect.SetRange(0, 400)
                self.bedTemperatureLabel.Show(False)
                self.bedTemperatureSelect.Show(False)
 
                self.temperatureGraph = temperatureGraph(self.temperaturePanel)
 
-               sizer.Add(wx.StaticText(self.temperaturePanel, -1, "Temp:"), pos=(0, 0))
+               sizer.Add(wx.StaticText(self.temperaturePanel, -1, _("Temp:")), pos=(0, 0))
                sizer.Add(self.temperatureSelect, pos=(0, 1))
+               sizer.Add(self.temperatureHeatUp, pos=(0, 2))
                sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
                sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
-               sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
+               sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 3), flag=wx.EXPAND)
                sizer.AddGrowableRow(2)
-               sizer.AddGrowableCol(1)
+               sizer.AddGrowableCol(2)
 
                nb.AddPage(self.temperaturePanel, 'Temp')
 
@@ -227,7 +262,7 @@ class printWindow(wx.Frame):
                sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E-2 F120'], 'retract.png', size=(60, 20)), pos=(2, 10),
                        span=(1, 3), flag=wx.EXPAND)
 
-               nb.AddPage(self.directControlPanel, 'Jog')
+               nb.AddPage(self.directControlPanel, _("Jog"))
 
                self.speedPanel = wx.Panel(nb)
                sizer = wx.GridBagSizer(2, 2)
@@ -242,20 +277,20 @@ class printWindow(wx.Frame):
                self.supportSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
                self.supportSpeedSelect.SetRange(5, 1000)
 
-               sizer.Add(wx.StaticText(self.speedPanel, -1, "Outer wall:"), pos=(0, 0))
+               sizer.Add(wx.StaticText(self.speedPanel, -1, _("Outer wall:")), pos=(0, 0))
                sizer.Add(self.outerWallSpeedSelect, pos=(0, 1))
                sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(0, 2))
-               sizer.Add(wx.StaticText(self.speedPanel, -1, "Inner wall:"), pos=(1, 0))
+               sizer.Add(wx.StaticText(self.speedPanel, -1, _("Inner wall:")), pos=(1, 0))
                sizer.Add(self.innerWallSpeedSelect, pos=(1, 1))
                sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(1, 2))
-               sizer.Add(wx.StaticText(self.speedPanel, -1, "Fill:"), pos=(2, 0))
+               sizer.Add(wx.StaticText(self.speedPanel, -1, _("Fill:")), pos=(2, 0))
                sizer.Add(self.fillSpeedSelect, pos=(2, 1))
                sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(2, 2))
-               sizer.Add(wx.StaticText(self.speedPanel, -1, "Support:"), pos=(3, 0))
+               sizer.Add(wx.StaticText(self.speedPanel, -1, _("Support:")), pos=(3, 0))
                sizer.Add(self.supportSpeedSelect, pos=(3, 1))
                sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2))
 
-               nb.AddPage(self.speedPanel, 'Speed')
+               nb.AddPage(self.speedPanel, _("Speed"))
 
                self.termPanel = wx.Panel(nb)
                sizer = wx.GridBagSizer(2, 2)
@@ -273,36 +308,7 @@ class printWindow(wx.Frame):
                sizer.AddGrowableCol(0)
                sizer.AddGrowableRow(0)
 
-               nb.AddPage(self.termPanel, 'Term')
-
-               if self.cam != None:
-                       self.camPage = wx.Panel(nb)
-                       sizer = wx.GridBagSizer(2, 2)
-                       self.camPage.SetSizer(sizer)
-
-                       self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelaps movie recording')
-                       sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       pages = self.cam.propertyPages()
-                       self.cam.buttons = [self.timelapsEnable]
-                       for page in pages:
-                               button = wx.Button(self.camPage, -1, page)
-                               button.index = pages.index(page)
-                               sizer.Add(button, pos=(1, pages.index(page)))
-                               button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
-                               self.cam.buttons.append(button)
-
-                       self.campreviewEnable = wx.CheckBox(self.camPage, -1, 'Show preview')
-                       sizer.Add(self.campreviewEnable, pos=(2, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       self.camPreview = wx.Panel(self.camPage)
-                       sizer.Add(self.camPreview, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
-
-                       nb.AddPage(self.camPage, 'Camera')
-                       self.camPreview.timer = wx.Timer(self)
-                       self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
-                       self.camPreview.timer.Start(500)
-                       self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
+               nb.AddPage(self.termPanel, _("Term"))
 
                self.sizer.AddGrowableRow(6)
                self.sizer.AddGrowableCol(3)
@@ -315,6 +321,7 @@ class printWindow(wx.Frame):
                self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
                self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
 
+               self.Bind(wx.EVT_BUTTON, lambda e: (self.temperatureSelect.SetValue(int(profile.getProfileSettingFloat('print_temperature'))), self.machineCom.sendCommand("M104 S%d" % (int(profile.getProfileSettingFloat('print_temperature'))))), self.temperatureHeatUp)
                self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
                self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
 
@@ -333,12 +340,61 @@ class printWindow(wx.Frame):
 
                self.UpdateButtonStates()
 
-       #self.UpdateProgress()
+               #self.UpdateProgress()
+               self._thread = threading.Thread(target=self._stdinMonitor)
+               self._thread.daemon = True
+               self._thread.start()
+
+               if webcam.hasWebcamSupport():
+                       #Need to call the camera class on the GUI thread, or else it won't work. Shame as it hangs the GUI for about 2 seconds.
+                       wx.CallAfter(self._webcamCheck)
+
+       def _stdinMonitor(self):
+               while True:
+                       line = sys.stdin.readline().rstrip()
+                       if line.startswith('LOAD:'):
+                               if not self.LoadGCodeFile(line[5:]):
+                                       print 'LOADFAILED\n'
+
+       def _webcamCheck(self):
+               self.cam = webcam.webcam()
+               if self.cam.hasCamera():
+                       self.camPage = wx.Panel(self.tabs)
+                       sizer = wx.GridBagSizer(2, 2)
+                       self.camPage.SetSizer(sizer)
+
+                       self.timelapsEnable = wx.CheckBox(self.camPage, -1, _("Enable timelapse movie recording"))
+                       self.timelapsSavePath = wx.TextCtrl(self.camPage, -1, os.path.expanduser('~/timelaps_' + datetime.datetime.now().strftime('%Y-%m-%d_%H-%M') + '.mpg'))
+                       sizer.Add(self.timelapsEnable, pos=(0, 0), span=(1, 2), flag=wx.EXPAND)
+                       sizer.Add(self.timelapsSavePath, pos=(1, 0), span=(1, 2), flag=wx.EXPAND)
+
+                       pages = self.cam.propertyPages()
+                       self.cam.buttons = [self.timelapsEnable, self.timelapsSavePath]
+                       for page in pages:
+                               button = wx.Button(self.camPage, -1, page)
+                               button.index = pages.index(page)
+                               sizer.Add(button, pos=(2, pages.index(page)))
+                               button.Bind(wx.EVT_BUTTON, self.OnPropertyPageButton)
+                               self.cam.buttons.append(button)
+
+                       self.campreviewEnable = wx.CheckBox(self.camPage, -1, _("Show preview"))
+                       sizer.Add(self.campreviewEnable, pos=(3, 0), span=(1, 2), flag=wx.EXPAND)
+
+                       self.camPreview = wx.Panel(self.camPage)
+                       sizer.Add(self.camPreview, pos=(4, 0), span=(1, 2), flag=wx.EXPAND)
+
+                       self.tabs.AddPage(self.camPage, _("Camera"))
+                       self.camPreview.timer = wx.Timer(self)
+                       self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPreview.timer)
+                       self.camPreview.timer.Start(500)
+                       self.camPreview.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
+               else:
+                       self.cam = None
 
        def OnCameraTimer(self, e):
                if not self.campreviewEnable.GetValue():
                        return
-               if self.machineCom != None and self.machineCom.isPrinting():
+               if self.machineCom is not None and self.machineCom.isPrinting():
                        return
                self.cam.takeNewImage()
                self.camPreview.Refresh()
@@ -350,7 +406,7 @@ class printWindow(wx.Frame):
                        rect = self.GetUpdateRegion().GetBox()
                        dc.SetClippingRect(rect)
                dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID))
-               if self.cam.getLastImage() != None:
+               if self.cam.getLastImage() is not None:
                        self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight()))
                        self.camPage.Fit()
                        dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
@@ -361,42 +417,42 @@ class printWindow(wx.Frame):
                self.cam.openPropertyPage(e.GetEventObject().index)
 
        def UpdateButtonStates(self):
-               self.connectButton.Enable(self.machineCom == None or self.machineCom.isClosedOrError())
+               self.connectButton.Enable(self.machineCom is None or self.machineCom.isClosedOrError())
                #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
-               self.printButton.Enable(self.machineCom != None and self.machineCom.isOperational() and not (
+               self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
+               self.machineCom.isPrinting() or self.machineCom.isPaused()))
+               self.temperatureHeatUp.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
                self.machineCom.isPrinting() or self.machineCom.isPaused()))
                self.pauseButton.Enable(
-                       self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
-               if self.machineCom != None and self.machineCom.isPaused():
-                       self.pauseButton.SetLabel('Resume')
+                       self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
+               if self.machineCom is not None and self.machineCom.isPaused():
+                       self.pauseButton.SetLabel(_("Resume"))
                else:
-                       self.pauseButton.SetLabel('Pause')
+                       self.pauseButton.SetLabel(_("Pause"))
                self.cancelButton.Enable(
-                       self.machineCom != None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
-               self.temperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
-               self.bedTemperatureSelect.Enable(self.machineCom != None and self.machineCom.isOperational())
+                       self.machineCom is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
+               self.temperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
+               self.bedTemperatureSelect.Enable(self.machineCom is not None and self.machineCom.isOperational())
                self.directControlPanel.Enable(
-                       self.machineCom != None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
-               self.machineLogButton.Show(self.machineCom != None and self.machineCom.isClosedOrError())
-               if self.cam != None:
+                       self.machineCom is not None and self.machineCom.isOperational() and not self.machineCom.isPrinting())
+               self.machineLogButton.Show(self.machineCom is not None and self.machineCom.isClosedOrError())
+               if self.cam is not None:
                        for button in self.cam.buttons:
-                               button.Enable(self.machineCom == None or not self.machineCom.isPrinting())
+                               button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
 
        def UpdateProgress(self):
                status = ""
-               if self.gcode == None:
-                       status += "Loading gcode...\n"
+               if self.gcode is None:
+                       status += _("Loading gcode...\n")
                else:
-                       status += "Filament: %.2fm %.2fg\n" % (
-                       self.gcode.extrusionAmount / 1000, self.gcode.calculateWeight() * 1000)
+                       status += _("Filament: %(amount).2fm %(weight).2fg\n") % {'amount': self.gcode.extrusionAmount / 1000, 'weight': self.gcode.calculateWeight() * 1000}
                        cost = self.gcode.calculateCost()
-                       if cost != False:
-                               status += "Filament cost: %s\n" % (cost)
-                       status += "Estimated print time: %02d:%02d\n" % (
-                       int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
-               if self.machineCom == None or not self.machineCom.isPrinting():
+                       if cost is not None:
+                               status += _("Filament cost: %s\n") % (cost)
+                       #status += "Estimated print time: %02d:%02d\n" % (int(self.gcode.totalMoveTimeMinute / 60), int(self.gcode.totalMoveTimeMinute % 60))
+               if self.machineCom is None or not self.machineCom.isPrinting():
                        self.progress.SetValue(0)
-                       if self.gcodeList != None:
+                       if self.gcodeList is not None:
                                status += 'Line: -/%d\n' % (len(self.gcodeList))
                else:
                        printTime = self.machineCom.getPrintTime() / 60
@@ -406,15 +462,15 @@ class printWindow(wx.Frame):
                        if self.currentZ > 0:
                                status += 'Height: %0.1f\n' % (self.currentZ)
                        status += 'Print time: %02d:%02d\n' % (int(printTime / 60), int(printTime % 60))
-                       if printTimeLeft == None:
+                       if printTimeLeft is None:
                                status += 'Print time left: Unknown\n'
                        else:
                                status += 'Print time left: %02d:%02d\n' % (int(printTimeLeft / 60), int(printTimeLeft % 60))
                        self.progress.SetValue(self.machineCom.getPrintPos())
                        taskbar.setProgress(self, self.machineCom.getPrintPos(), len(self.gcodeList))
-               if self.machineCom != None:
+               if self.machineCom is not None:
                        if self.machineCom.getTemp() > 0:
-                               status += 'Temp: %d\n' % (self.machineCom.getTemp())
+                               status += 'Temp: %s\n' % (' ,'.join(map(str, self.machineCom.getTemp())))
                        if self.machineCom.getBedTemp() > 0:
                                status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
                                self.bedTemperatureLabel.Show(True)
@@ -425,7 +481,7 @@ class printWindow(wx.Frame):
                self.statsText.SetLabel(status.strip())
 
        def OnConnect(self, e):
-               if self.machineCom != None:
+               if self.machineCom is not None:
                        self.machineCom.close()
                self.machineCom = machineCom.MachineCom(callbackObject=self)
                self.UpdateButtonStates()
@@ -435,15 +491,15 @@ class printWindow(wx.Frame):
                pass
 
        def OnPrint(self, e):
-               if self.machineCom == None or not self.machineCom.isOperational():
+               if self.machineCom is None or not self.machineCom.isOperational():
                        return
-               if self.gcodeList == None:
+               if self.gcodeList is None:
                        return
                if self.machineCom.isPrinting():
                        return
                self.currentZ = -1
-               if self.cam != None and self.timelapsEnable.GetValue():
-                       self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
+               if self.cam is not None and self.timelapsEnable.GetValue():
+                       self.cam.startTimelapse(self.timelapsSavePath.GetValue())
                self.machineCom.printGCode(self.gcodeList)
                self.UpdateButtonStates()
 
@@ -451,6 +507,7 @@ class printWindow(wx.Frame):
                self.pauseButton.SetLabel('Pause')
                self.machineCom.cancelPrint()
                self.machineCom.sendCommand("M84")
+               self.machineCom.sendCommand("M104 S0")
                self.UpdateButtonStates()
 
        def OnPause(self, e):
@@ -465,7 +522,7 @@ class printWindow(wx.Frame):
        def OnClose(self, e):
                global printWindowHandle
                printWindowHandle = None
-               if self.machineCom != None:
+               if self.machineCom is not None:
                        self.machineCom.close()
                self.Destroy()
 
@@ -476,7 +533,7 @@ class printWindow(wx.Frame):
                self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue()))
 
        def OnSpeedChange(self, e):
-               if self.machineCom == None:
+               if self.machineCom is None:
                        return
                self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0)
                self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0)
@@ -484,9 +541,12 @@ class printWindow(wx.Frame):
                self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
 
        def AddTermLog(self, line):
+               if len(self.termLog.GetValue()) > 10000:
+                       self.termLog.SetValue(self.termLog.GetValue()[-10000:])
+               self.termLog.SetInsertionPointEnd()
                self.termLog.AppendText(unicode(line, 'utf-8', 'replace'))
-               l = len(self.termLog.GetValue())
-               self.termLog.SetCaret(wx.Caret(self.termLog, (l, l)))
+               #l = self.termLog.GetLastPosition()     # if needed (windows? mac?)
+               #self.termLog.ShowPosition(l)
 
        def OnTermEnterLine(self, e):
                line = self.termInput.GetValue()
@@ -501,12 +561,12 @@ class printWindow(wx.Frame):
        def OnTermKey(self, e):
                if len(self.termHistory) > 0:
                        if e.GetKeyCode() == wx.WXK_UP:
-                               self.termHistoryIdx = self.termHistoryIdx - 1
+                               self.termHistoryIdx -= 1
                                if self.termHistoryIdx < 0:
                                        self.termHistoryIdx = len(self.termHistory) - 1
                                self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
                        if e.GetKeyCode() == wx.WXK_DOWN:
-                               self.termHistoryIdx = self.termHistoryIdx - 1
+                               self.termHistoryIdx -= 1
                                if self.termHistoryIdx >= len(self.termHistory):
                                        self.termHistoryIdx = 0
                                self.termInput.SetValue(self.termHistory[self.termHistoryIdx])
@@ -524,8 +584,8 @@ class printWindow(wx.Frame):
                        self.Layout()
 
        def LoadGCodeFile(self, filename):
-               if self.machineCom != None and self.machineCom.isPrinting():
-                       return
+               if self.machineCom is not None and self.machineCom.isPrinting():
+                       return False
                #Send an initial M110 to reset the line counter to zero.
                prevLineType = lineType = 'CUSTOM'
                gcodeList = ["M110"]
@@ -542,7 +602,7 @@ class printWindow(wx.Frame):
                                        gcodeList.append(line)
                                prevLineType = lineType
                gcode = gcodeInterpreter.gcode()
-               gcode.loadList(gcodeList)
+               gcode.load(gcodeList)
                #print "Loaded: %s (%d)" % (filename, len(gcodeList))
                self.filename = filename
                self.gcode = gcode
@@ -551,7 +611,7 @@ class printWindow(wx.Frame):
                wx.CallAfter(self.progress.SetRange, len(gcodeList))
                wx.CallAfter(self.UpdateButtonStates)
                wx.CallAfter(self.UpdateProgress)
-               wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename))
+               return True
 
        def sendLine(self, lineNr):
                if lineNr >= len(self.gcodeList):
@@ -576,22 +636,30 @@ class printWindow(wx.Frame):
 
        def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
                self.temperatureGraph.addPoint(temp, targetTemp, bedTemp, bedTargetTemp)
-#              ToFix, this causes problems with setting the temperature with the keyboard
-#              if self.temperatureSelect.GetValue() != targetTemp:
-#                      wx.CallAfter(self.temperatureSelect.SetValue, targetTemp)
-#              if self.bedTemperatureSelect.GetValue() != bedTargetTemp:
-#                      wx.CallAfter(self.bedTemperatureSelect.SetValue, bedTargetTemp)
+               wx.CallAfter(self._mcTempUpdate, temp, bedTemp, targetTemp, bedTargetTemp)
+
+       def _mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
+               if self.temperatureSelect.GetValue() != targetTemp[0] and wx.Window.FindFocus() != self.temperatureSelect:
+                       self.temperatureSelect.SetValue(targetTemp[0])
+               if self.bedTemperatureSelect.GetValue() != bedTargetTemp and wx.Window.FindFocus() != self.bedTemperatureSelect:
+                       self.bedTemperatureSelect.SetValue(bedTargetTemp)
 
        def mcStateChange(self, state):
-               if self.machineCom != None:
-                       if state == self.machineCom.STATE_OPERATIONAL and self.cam != None:
-                               self.cam.endTimelaps()
+               if self.machineCom is not None:
+                       if state == self.machineCom.STATE_OPERATIONAL and self.cam is not None:
+                               self.cam.endTimelapse()
                        if state == self.machineCom.STATE_OPERATIONAL:
                                taskbar.setBusy(self, False)
                        if self.machineCom.isClosedOrError():
                                taskbar.setBusy(self, False)
                        if self.machineCom.isPaused():
                                taskbar.setPause(self, True)
+                       if self.machineCom.isClosedOrError():
+                               print 'STATE:CLOSED'
+                       elif self.machineCom.isPrinting():
+                               print 'STATE:PRINTING'
+                       else:
+                               print 'STATE:IDLE'
                wx.CallAfter(self.UpdateButtonStates)
                wx.CallAfter(self.UpdateProgress)
 
@@ -603,7 +671,8 @@ class printWindow(wx.Frame):
 
        def mcZChange(self, newZ):
                self.currentZ = newZ
-               if self.cam != None:
+               print 'Z:%f' % newZ
+               if self.cam is not None:
                        wx.CallAfter(self.cam.takeNewImage)
                        wx.CallAfter(self.camPreview.Refresh)
 
@@ -619,14 +688,14 @@ class temperatureGraph(wx.Panel):
                self.lastDraw = time.time() - 1.0
                self.points = []
                self.backBuffer = None
-               self.addPoint(0, 0, 0, 0)
+               self.addPoint([0]*16, [0]*16, 0, 0)
                self.SetMinSize((320, 200))
 
        def OnEraseBackground(self, e):
                pass
 
        def OnSize(self, e):
-               if self.backBuffer == None or self.GetSize() != self.backBuffer.GetSize():
+               if self.backBuffer is None or self.GetSize() != self.backBuffer.GetSize():
                        self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
                        self.UpdateDrawing(True)
 
@@ -653,17 +722,18 @@ class temperatureGraph(wx.Panel):
 
                #Draw the background up to the current temperatures.
                x0 = 0
-               t0 = 0
+               t0 = [0] * len(self.points[0][0])
                bt0 = 0
                tSP0 = 0
                btSP0 = 0
                for temp, tempSP, bedTemp, bedTempSP, t in self.points:
                        x1 = int(w - (now - t))
                        for x in xrange(x0, x1 + 1):
-                               t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
+                               for n in xrange(0, len(temp)):
+                                       t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
+                                       dc.SetPen(tempPenBG)
+                                       dc.DrawLine(x, h, x, h - (t * h / 300))
                                bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
-                               dc.SetPen(tempPenBG)
-                               dc.DrawLine(x, h, x, h - (t * h / 300))
                                dc.SetPen(bedTempPenBG)
                                dc.DrawLine(x, h, x, h - (bt * h / 300))
                        t0 = temp
@@ -687,23 +757,24 @@ class temperatureGraph(wx.Panel):
 
                #Draw the main lines
                x0 = 0
-               t0 = 0
+               t0 = [0] * len(self.points[0][0])
                bt0 = 0
-               tSP0 = 0
+               tSP0 = [0] * len(self.points[0][0])
                btSP0 = 0
                for temp, tempSP, bedTemp, bedTempSP, t in self.points:
                        x1 = int(w - (now - t))
                        for x in xrange(x0, x1 + 1):
-                               t = float(x - x0) / float(x1 - x0 + 1) * (temp - t0) + t0
+                               for n in xrange(0, len(temp)):
+                                       t = float(x - x0) / float(x1 - x0 + 1) * (temp[n] - t0[n]) + t0[n]
+                                       tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP[n] - tSP0[n]) + tSP0[n]
+                                       dc.SetPen(tempSPPen)
+                                       dc.DrawPoint(x, h - (tSP * h / 300))
+                                       dc.SetPen(tempPen)
+                                       dc.DrawPoint(x, h - (t * h / 300))
                                bt = float(x - x0) / float(x1 - x0 + 1) * (bedTemp - bt0) + bt0
-                               tSP = float(x - x0) / float(x1 - x0 + 1) * (tempSP - tSP0) + tSP0
                                btSP = float(x - x0) / float(x1 - x0 + 1) * (bedTempSP - btSP0) + btSP0
-                               dc.SetPen(tempSPPen)
-                               dc.DrawPoint(x, h - (tSP * h / 300))
                                dc.SetPen(bedTempSPPen)
                                dc.DrawPoint(x, h - (btSP * h / 300))
-                               dc.SetPen(tempPen)
-                               dc.DrawPoint(x, h - (t * h / 300))
                                dc.SetPen(bedTempPen)
                                dc.DrawPoint(x, h - (bt * h / 300))
                        t0 = temp
@@ -720,11 +791,11 @@ class temperatureGraph(wx.Panel):
                        self.points.pop(0)
 
        def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
-               if bedTemp == None:
+               if bedTemp is None:
                        bedTemp = 0
-               if bedTempSP == None:
+               if bedTempSP is None:
                        bedTempSP = 0
-               self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
+               self.points.append((temp[:], tempSP[:], bedTemp, bedTempSP, time.time()))
                wx.CallAfter(self.UpdateDrawing)