chiark / gitweb /
Change how the engine is interfaced from the python code. Put the GCode viewer in...
[cura.git] / Cura / gui / printWindow.py
index c8b6124e6cbaec114475c2f7a94d5f8af1d65d31..1a0e2cbe116c63a3b67bb6a8ac49c121a8a23609 100644 (file)
@@ -18,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')
@@ -58,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
@@ -105,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
@@ -123,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)
@@ -134,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')
@@ -150,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)
@@ -173,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)
@@ -181,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)
@@ -239,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)
@@ -254,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)
@@ -285,38 +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')
-                       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)
-
-                       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)
@@ -329,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)
 
@@ -348,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():
@@ -380,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())
@@ -395,20 +436,19 @@ 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 = ""
                if self.gcode is None:
                        for button in self.cam.buttons:
                                button.Enable(self.machineCom is None or not self.machineCom.isPrinting())
 
        def UpdateProgress(self):
                status = ""
                if self.gcode is None:
-                       status += "Loading gcode...\n"
+                       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 += _("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)
                        #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)
@@ -428,7 +468,7 @@ class printWindow(wx.Frame):
                                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: %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: %s\n' % (' ,'.join(map(str, self.machineCom.getTemp())))
                        if self.machineCom.getBedTemp() > 0:
                        if self.machineCom.getTemp() > 0:
                                status += 'Temp: %s\n' % (' ,'.join(map(str, self.machineCom.getTemp())))
                        if self.machineCom.getBedTemp() > 0:
@@ -459,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.timelapsSavePath)
+                       self.cam.startTimelapse(self.timelapsSavePath.GetValue())
                self.machineCom.printGCode(self.gcodeList)
                self.UpdateButtonStates()
 
                self.machineCom.printGCode(self.gcodeList)
                self.UpdateButtonStates()
 
@@ -501,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()
@@ -542,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"]
@@ -559,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
@@ -568,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):
@@ -611,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)
 
@@ -622,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)