chiark / gitweb /
Change how the engine is interfaced from the python code. Put the GCode viewer in...
[cura.git] / Cura / gui / printWindow.py
index 1d7f315eed0f82c79eae03b2d350eec53ec5fdec..1a0e2cbe116c63a3b67bb6a8ac49c121a8a23609 100644 (file)
@@ -1,5 +1,5 @@
-# coding=utf-8
 from __future__ import absolute_import
 from __future__ import absolute_import
+__copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
 
 import threading
 import re
 
 import threading
 import re
@@ -9,6 +9,7 @@ import time
 import platform
 import os
 import power
 import platform
 import os
 import power
+import datetime
 
 import wx
 from wx.lib import buttons
 
 import wx
 from wx.lib import buttons
@@ -17,38 +18,23 @@ from Cura.gui.util import webcam
 from Cura.gui.util import taskbar
 from Cura.util import machineCom
 from Cura.util import gcodeInterpreter
 from Cura.gui.util import taskbar
 from Cura.util import machineCom
 from Cura.util import gcodeInterpreter
-from Cura.util.resources import getPathForImage
-
-printWindowMonitorHandle = None
-
-def printFile(filename):
-       global printWindowMonitorHandle
-       if printWindowMonitorHandle is 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():
 class printProcessMonitor():
-       def __init__(self):
+       def __init__(self, callback = None):
                self.handle = None
                self.handle = None
+               self._state = 'CLOSED'
+               self._z = 0.0
+               self._callback = callback
+               self._id = -1
 
 
-       def loadFile(self, filename):
+       def loadFile(self, filename, id):
                if self.handle is None:
                        if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
                if self.handle is None:
                        if platform.system() == "Darwin" and hasattr(sys, 'frozen'):
-                               cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')] 
+                               cmdList = [os.path.join(os.path.dirname(sys.executable), 'Cura')]
                        else:
                                cmdList = [sys.executable, '-m', 'Cura.cura']
                        cmdList.append('-r')
                        else:
                                cmdList = [sys.executable, '-m', 'Cura.cura']
                        cmdList.append('-r')
@@ -57,31 +43,73 @@ class printProcessMonitor():
                                if platform.machine() == 'i386':
                                        cmdList.insert(0, 'arch')
                                        cmdList.insert(1, '-i386')
                                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.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:
 
        def Monitor(self):
                p = self.handle
                line = p.stdout.readline()
                while len(line) > 0:
-                       print line.rstrip()
+                       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:
                        line = p.stdout.readline()
                line = p.stderr.readline()
                while len(line) > 0:
-                       print line.rstrip()
+                       print '>>' + line.rstrip()
                        line = p.stderr.readline()
                p.communicate()
                self.handle = None
                self.thread = None
 
                        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)):
 
 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
                super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size)
 
                self.commandList = commandList
@@ -104,7 +132,8 @@ class printWindow(wx.Frame):
        "Main user interface window"
 
        def __init__(self):
        "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
                self.machineCom = None
                self.gcode = None
                self.gcodeList = None
@@ -122,10 +151,6 @@ class printWindow(wx.Frame):
                self.termHistoryIdx = 0
 
                self.cam = None
                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)
 
                self.SetSizer(wx.BoxSizer())
                self.panel = wx.Panel(self)
@@ -133,12 +158,12 @@ class printWindow(wx.Frame):
                self.sizer = wx.GridBagSizer(2, 2)
                self.panel.SetSizer(self.sizer)
 
                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,
                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')
                        style=wx.ALIGN_CENTER)
                self.powerWarningText.SetBackgroundColour('red')
                self.powerWarningText.SetForegroundColour('white')
@@ -149,18 +174,17 @@ class printWindow(wx.Frame):
                self.OnPowerWarningChange(None)
                self.powerWarningTimer.Start(10000)
 
                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)
 
                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.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)
                self.progress = wx.Gauge(self.panel, -1)
 
                self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND)
@@ -172,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.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)
                self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND)
 
                self.temperaturePanel = wx.Panel(nb)
@@ -180,19 +205,18 @@ 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.temperatureSelect = wx.SpinCtrl(self.temperaturePanel, -1, '0', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS)
                self.temperatureSelect.SetRange(0, 400)
-               self.temperatureHeatUpPLA = wx.Button(self.temperaturePanel, -1, '210C')
-               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)
 
                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.temperatureSelect, pos=(0, 1))
-               sizer.Add(self.temperatureHeatUpPLA, pos=(0, 2))
+               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, 3), flag=wx.EXPAND)
                sizer.Add(self.bedTemperatureLabel, pos=(1, 0))
                sizer.Add(self.bedTemperatureSelect, pos=(1, 1))
                sizer.Add(self.temperatureGraph, pos=(2, 0), span=(1, 3), flag=wx.EXPAND)
@@ -238,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)
 
                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)
 
                self.speedPanel = wx.Panel(nb)
                sizer = wx.GridBagSizer(2, 2)
@@ -253,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)
 
                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(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(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(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))
 
                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)
 
                self.termPanel = wx.Panel(nb)
                sizer = wx.GridBagSizer(2, 2)
@@ -284,36 +308,7 @@ class printWindow(wx.Frame):
                sizer.AddGrowableCol(0)
                sizer.AddGrowableRow(0)
 
                sizer.AddGrowableCol(0)
                sizer.AddGrowableRow(0)
 
-               nb.AddPage(self.termPanel, 'Term')
-
-               if self.cam is not None:
-                       self.camPage = wx.Panel(nb)
-                       sizer = wx.GridBagSizer(2, 2)
-                       self.camPage.SetSizer(sizer)
-
-                       self.timelapsEnable = wx.CheckBox(self.camPage, -1, 'Enable timelapse 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)
 
                self.sizer.AddGrowableRow(6)
                self.sizer.AddGrowableCol(3)
@@ -326,7 +321,7 @@ class printWindow(wx.Frame):
                self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)
                self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog)
 
                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(210), self.machineCom.sendCommand("M104 S210")), self.temperatureHeatUpPLA)
+               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)
 
                self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect)
                self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect)
 
@@ -345,7 +340,56 @@ class printWindow(wx.Frame):
 
                self.UpdateButtonStates()
 
 
                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():
 
        def OnCameraTimer(self, e):
                if not self.campreviewEnable.GetValue():
@@ -377,14 +421,14 @@ class printWindow(wx.Frame):
                #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
                self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
                self.machineCom.isPrinting() or self.machineCom.isPaused()))
                #self.loadButton.Enable(self.machineCom == None or not (self.machineCom.isPrinting() or self.machineCom.isPaused()))
                self.printButton.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
                self.machineCom.isPrinting() or self.machineCom.isPaused()))
-               self.temperatureHeatUpPLA.Enable(self.machineCom is not None and self.machineCom.isOperational() and not (
+               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 is not None and (self.machineCom.isPrinting() or self.machineCom.isPaused()))
                if self.machineCom is not None and self.machineCom.isPaused():
                self.machineCom.isPrinting() or self.machineCom.isPaused()))
                self.pauseButton.Enable(
                        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')
+                       self.pauseButton.SetLabel(_("Resume"))
                else:
                else:
-                       self.pauseButton.SetLabel('Pause')
+                       self.pauseButton.SetLabel(_("Pause"))
                self.cancelButton.Enable(
                        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.cancelButton.Enable(
                        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())
@@ -392,22 +436,20 @@ class printWindow(wx.Frame):
                self.directControlPanel.Enable(
                        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())
                self.directControlPanel.Enable(
                        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 != None:
+               if self.cam is not None:
                        for button in self.cam.buttons:
                                button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
 
        def UpdateProgress(self):
                status = ""
                        for button in self.cam.buttons:
                                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:
                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 is not None:
                        cost = self.gcode.calculateCost()
                        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))
+                               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 is not None:
                if self.machineCom is None or not self.machineCom.isPrinting():
                        self.progress.SetValue(0)
                        if self.gcodeList is not None:
@@ -420,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 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))
                                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:
                        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)
                        if self.machineCom.getBedTemp() > 0:
                                status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp())
                                self.bedTemperatureLabel.Show(True)
@@ -457,7 +499,7 @@ class printWindow(wx.Frame):
                        return
                self.currentZ = -1
                if self.cam is not None and self.timelapsEnable.GetValue():
                        return
                self.currentZ = -1
                if self.cam is not None and self.timelapsEnable.GetValue():
-                       self.cam.startTimelapse(self.filename[: self.filename.rfind('.')] + ".mpg")
+                       self.cam.startTimelapse(self.timelapsSavePath.GetValue())
                self.machineCom.printGCode(self.gcodeList)
                self.UpdateButtonStates()
 
                self.machineCom.printGCode(self.gcodeList)
                self.UpdateButtonStates()
 
@@ -499,9 +541,12 @@ class printWindow(wx.Frame):
                self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0)
 
        def AddTermLog(self, line):
                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'))
                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()
 
        def OnTermEnterLine(self, e):
                line = self.termInput.GetValue()
@@ -540,7 +585,7 @@ class printWindow(wx.Frame):
 
        def LoadGCodeFile(self, filename):
                if self.machineCom is not None and self.machineCom.isPrinting():
 
        def LoadGCodeFile(self, filename):
                if self.machineCom is not None and self.machineCom.isPrinting():
-                       return
+                       return False
                #Send an initial M110 to reset the line counter to zero.
                prevLineType = lineType = 'CUSTOM'
                gcodeList = ["M110"]
                #Send an initial M110 to reset the line counter to zero.
                prevLineType = lineType = 'CUSTOM'
                gcodeList = ["M110"]
@@ -557,7 +602,7 @@ class printWindow(wx.Frame):
                                        gcodeList.append(line)
                                prevLineType = lineType
                gcode = gcodeInterpreter.gcode()
                                        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
                #print "Loaded: %s (%d)" % (filename, len(gcodeList))
                self.filename = filename
                self.gcode = gcode
@@ -566,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.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):
 
        def sendLine(self, lineNr):
                if lineNr >= len(self.gcodeList):
@@ -594,8 +639,8 @@ class printWindow(wx.Frame):
                wx.CallAfter(self._mcTempUpdate, temp, bedTemp, targetTemp, bedTargetTemp)
 
        def _mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
                wx.CallAfter(self._mcTempUpdate, temp, bedTemp, targetTemp, bedTargetTemp)
 
        def _mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
-               if self.temperatureSelect.GetValue() != targetTemp and wx.Window.FindFocus() != self.temperatureSelect:
-                       self.temperatureSelect.SetValue(targetTemp)
+               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)
 
                if self.bedTemperatureSelect.GetValue() != bedTargetTemp and wx.Window.FindFocus() != self.bedTemperatureSelect:
                        self.bedTemperatureSelect.SetValue(bedTargetTemp)
 
@@ -609,6 +654,12 @@ class printWindow(wx.Frame):
                                taskbar.setBusy(self, False)
                        if self.machineCom.isPaused():
                                taskbar.setPause(self, True)
                                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)
 
                wx.CallAfter(self.UpdateButtonStates)
                wx.CallAfter(self.UpdateProgress)
 
@@ -620,6 +671,7 @@ class printWindow(wx.Frame):
 
        def mcZChange(self, newZ):
                self.currentZ = newZ
 
        def mcZChange(self, newZ):
                self.currentZ = newZ
+               print 'Z:%f' % newZ
                if self.cam is not None:
                        wx.CallAfter(self.cam.takeNewImage)
                        wx.CallAfter(self.camPreview.Refresh)
                if self.cam is not None:
                        wx.CallAfter(self.cam.takeNewImage)
                        wx.CallAfter(self.camPreview.Refresh)
@@ -636,14 +688,14 @@ class temperatureGraph(wx.Panel):
                self.lastDraw = time.time() - 1.0
                self.points = []
                self.backBuffer = None
                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):
                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)
 
                        self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple())
                        self.UpdateDrawing(True)
 
@@ -670,17 +722,18 @@ class temperatureGraph(wx.Panel):
 
                #Draw the background up to the current temperatures.
                x0 = 0
 
                #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):
                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
                                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
                                dc.SetPen(bedTempPenBG)
                                dc.DrawLine(x, h, x, h - (bt * h / 300))
                        t0 = temp
@@ -704,23 +757,24 @@ class temperatureGraph(wx.Panel):
 
                #Draw the main lines
                x0 = 0
 
                #Draw the main lines
                x0 = 0
-               t0 = 0
+               t0 = [0] * len(self.points[0][0])
                bt0 = 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):
                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
                                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
                                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(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
                                dc.SetPen(bedTempPen)
                                dc.DrawPoint(x, h - (bt * h / 300))
                        t0 = temp
@@ -737,11 +791,11 @@ class temperatureGraph(wx.Panel):
                        self.points.pop(0)
 
        def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
                        self.points.pop(0)
 
        def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
-               if bedTemp == None:
+               if bedTemp is None:
                        bedTemp = 0
                        bedTemp = 0
-               if bedTempSP == None:
+               if bedTempSP is None:
                        bedTempSP = 0
                        bedTempSP = 0
-               self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
+               self.points.append((temp[:], tempSP[:], bedTemp, bedTempSP, time.time()))
                wx.CallAfter(self.UpdateDrawing)
 
 
                wx.CallAfter(self.UpdateDrawing)