diff --git a/.gitignore b/.gitignore index 2bd3ae3..35c9d5f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ *.pyc *.zip *.exe -osx64-Cura-* +darwin-Cura-* win32-Cura-* linux-Cura-* Printrun @@ -18,3 +18,4 @@ printrun.bat cura.bat object-mirror.png object.png +*darwin.dmg diff --git a/scripts/osx64/Cura.app/Contents/Resources/Cura.icns b/Cura/Cura.icns similarity index 100% rename from scripts/osx64/Cura.app/Contents/Resources/Cura.icns rename to Cura/Cura.icns diff --git a/Cura/cura.py b/Cura/cura.py index 35fb8f6..44eca43 100644 --- a/Cura/cura.py +++ b/Cura/cura.py @@ -9,12 +9,13 @@ The slicing code is the same as Skeinforge. But the UI has been revamped to be.. """ from __future__ import absolute_import -import __init__ import sys -import platform +import warnings from optparse import OptionParser +import wx._core + from util import profile __author__ = 'Daid' @@ -36,6 +37,7 @@ Reece.Arnott Wade Xsainnz Zach Hoeken +Ilya Kulakov (kulakov.ilya@gmail.com) Organizations: Ultimaker @@ -43,45 +45,66 @@ Art of Illusion """ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + +class CuraApp(wx.App): + def MacOpenFile(self, path): + try: + pass + except Exception as e: + warnings.warn("File at {p} cannot be read: {e}".format(p=path, e=str(e))) + def main(): parser = OptionParser(usage="usage: %prog [options] .stl") - parser.add_option("-i", "--ini", action="store", type="string", dest="profileini", help="Load settings from a profile ini file") - parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner", help="Open the project planner") - parser.add_option("-F", "--flat", action="store_true", dest="openflatslicer", help="Open the 2D SVG slicer (unfinished)") - parser.add_option("-r", "--print", action="store", type="string", dest="printfile", help="Open the printing interface, instead of the normal cura interface.") - parser.add_option("-p", "--profile", action="store", type="string", dest="profile", help="Internal option, do not use!") - parser.add_option("-s", "--slice", action="store_true", dest="slice", help="Slice the given files instead of opening them in Cura") + parser.add_option("-i", "--ini", action="store", type="string", dest="profileini", + help="Load settings from a profile ini file") + parser.add_option("-P", "--project", action="store_true", dest="openprojectplanner", + help="Open the project planner") + parser.add_option("-F", "--flat", action="store_true", dest="openflatslicer", + help="Open the 2D SVG slicer (unfinished)") + parser.add_option("-r", "--print", action="store", type="string", dest="printfile", + help="Open the printing interface, instead of the normal cura interface.") + parser.add_option("-p", "--profile", action="store", type="string", dest="profile", + help="Internal option, do not use!") + parser.add_option("-s", "--slice", action="store_true", dest="slice", + help="Slice the given files instead of opening them in Cura") (options, args) = parser.parse_args() - if options.profile != None: + + if options.profile is not None: profile.loadGlobalProfileFromString(options.profile) - if options.profileini != None: + if options.profileini is not None: profile.loadGlobalProfile(options.profileini) - if options.openprojectplanner != None: + + if options.openprojectplanner is not None: from gui import projectPlanner projectPlanner.main() - return - if options.openflatslicer != None: + elif options.openflatslicer is not None: from gui import flatSlicerWindow flatSlicerWindow.main() - return - if options.printfile != None: + elif options.printfile is not None: from gui import printWindow printWindow.startPrintInterface(options.printfile) - return - - if options.slice != None: + elif options.slice is not None: from util import sliceRun sliceRun.runSlice(args) else: if len(args) > 0: profile.putPreference('lastFile', ';'.join(args)) - from gui import splashScreen - splashScreen.showSplash(mainWindowRunCallback) -def mainWindowRunCallback(splash): - from gui import mainWindow - mainWindow.main(splash) + from gui import splashScreen + + def mainWindowRunCallback(splash): + from gui import mainWindow + if splash is not None: + splash.Show(False) + mainWindow.main() + + app = CuraApp(False) + # Apple discurage usage of splash screens on a mac. + if sys.platform.startswith('darwin'): + mainWindowRunCallback(None) + else: + splashScreen.splashScreen(mainWindowRunCallback) + app.MainLoop() if __name__ == '__main__': main() - diff --git a/Cura/gui/configWizard.py b/Cura/gui/configWizard.py index 08ca7c7..54d734a 100644 --- a/Cura/gui/configWizard.py +++ b/Cura/gui/configWizard.py @@ -1,7 +1,11 @@ +# coding=utf-8 from __future__ import absolute_import -import __init__ -import wx, os, platform, types, webbrowser, threading, time, re +import webbrowser +import threading +import time + +import wx import wx.wizard from gui import firmwareInstall @@ -9,30 +13,36 @@ from gui import toolbarUtil from gui import printWindow from util import machineCom from util import profile +from util.resources import getPathForImage class InfoBox(wx.Panel): def __init__(self, parent): super(InfoBox, self).__init__(parent) self.SetBackgroundColour('#FFFF80') - + self.sizer = wx.GridBagSizer(5, 5) self.SetSizer(self.sizer) - - self.attentionBitmap = toolbarUtil.getBitmapImage('attention.png') - self.errorBitmap = toolbarUtil.getBitmapImage('error.png') - self.readyBitmap = toolbarUtil.getBitmapImage('ready.png') - self.busyBitmap = [toolbarUtil.getBitmapImage('busy-0.png'), toolbarUtil.getBitmapImage('busy-1.png'), toolbarUtil.getBitmapImage('busy-2.png'), toolbarUtil.getBitmapImage('busy-3.png')] - + + self.attentionBitmap = wx.Bitmap(getPathForImage('attention.png')) + self.errorBitmap = wx.Bitmap(getPathForImage('error.png')) + self.readyBitmap = wx.Bitmap(getPathForImage('ready.png')) + self.busyBitmap = [ + wx.Bitmap(getPathForImage('busy-0.png')), + wx.Bitmap(getPathForImage('busy-1.png')), + wx.Bitmap(getPathForImage('busy-2.png')), + wx.Bitmap(getPathForImage('busy-3.png')) + ] + self.bitmap = wx.StaticBitmap(self, -1, wx.EmptyBitmapRGBA(24, 24, red=255, green=255, blue=255, alpha=1)) self.text = wx.StaticText(self, -1, '') self.extraInfoButton = wx.Button(self, -1, 'i', style=wx.BU_EXACTFIT) - self.sizer.Add(self.bitmap, pos=(0,0), flag=wx.ALL, border=5) - self.sizer.Add(self.text, pos=(0,1), flag=wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL, border=5) + self.sizer.Add(self.bitmap, pos=(0, 0), flag=wx.ALL, border=5) + self.sizer.Add(self.text, pos=(0, 1), flag=wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, border=5) self.sizer.Add(self.extraInfoButton, pos=(0,2), flag=wx.ALL|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5) self.sizer.AddGrowableCol(1) - + self.extraInfoButton.Show(False) - + self.extraInfoUrl = '' self.busyState = None self.timer = wx.Timer(self) @@ -54,18 +64,21 @@ class InfoBox(wx.Panel): self.Layout() self.SetErrorIndicator() self.Refresh() - + def SetAttention(self, info): self.SetBackgroundColour('#FFFF80') self.text.SetLabel(info) self.extraInfoButton.Show(False) self.SetAttentionIndicator() self.Refresh() - + def SetBusyIndicator(self): self.busyState = 0 self.bitmap.SetBitmap(self.busyBitmap[self.busyState]) - + + def doExtraInfo(self, e): + webbrowser.open(self.extraInfoUrl) + def doBusyUpdate(self, e): if self.busyState == None: return @@ -73,22 +86,20 @@ class InfoBox(wx.Panel): if self.busyState >= len(self.busyBitmap): self.busyState = 0 self.bitmap.SetBitmap(self.busyBitmap[self.busyState]) - - def doExtraInfo(self, e): - webbrowser.open(self.extraInfoUrl) - + def SetReadyIndicator(self): self.busyState = None self.bitmap.SetBitmap(self.readyBitmap) - + def SetErrorIndicator(self): self.busyState = None self.bitmap.SetBitmap(self.errorBitmap) - + def SetAttentionIndicator(self): self.busyState = None self.bitmap.SetBitmap(self.attentionBitmap) + class InfoPage(wx.wizard.WizardPageSimple): def __init__(self, parent, title): wx.wizard.WizardPageSimple.__init__(self, parent) @@ -99,52 +110,52 @@ class InfoPage(wx.wizard.WizardPageSimple): title = wx.StaticText(self, -1, title) title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) - sizer.Add(title, pos=(0, 0), span=(1,2), flag=wx.ALIGN_CENTRE|wx.ALL) - sizer.Add(wx.StaticLine(self, -1), pos=(1,0), span=(1,2), flag=wx.EXPAND|wx.ALL) + sizer.Add(title, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTRE | wx.ALL) + sizer.Add(wx.StaticLine(self, -1), pos=(1, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL) sizer.AddGrowableCol(1) - + self.rowNr = 2 - - def AddText(self,info): + + def AddText(self, info): text = wx.StaticText(self, -1, info) - self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT) + self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT) self.rowNr += 1 return text - + def AddSeperator(self): - self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1,2), flag=wx.EXPAND|wx.ALL) + self.GetSizer().Add(wx.StaticLine(self, -1), pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL) self.rowNr += 1 - + def AddHiddenSeperator(self): self.AddText('') def AddInfoBox(self): infoBox = InfoBox(self) - self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT|wx.EXPAND) + self.GetSizer().Add(infoBox, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT | wx.EXPAND) self.rowNr += 1 return infoBox - - def AddRadioButton(self, label, style = 0): + + def AddRadioButton(self, label, style=0): radio = wx.RadioButton(self, -1, label, style=style) - self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1,2), flag=wx.EXPAND|wx.ALL) + self.GetSizer().Add(radio, pos=(self.rowNr, 0), span=(1, 2), flag=wx.EXPAND | wx.ALL) self.rowNr += 1 return radio - def AddCheckbox(self, label, checked = False): + def AddCheckbox(self, label, checked=False): check = wx.CheckBox(self, -1) text = wx.StaticText(self, -1, label) check.SetValue(checked) - self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT|wx.RIGHT) - self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1,2), flag=wx.ALL) + self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT) + self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 2), flag=wx.ALL) self.rowNr += 1 return check - + def AddButton(self, label): button = wx.Button(self, -1, label) - self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT) + self.GetSizer().Add(button, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT) self.rowNr += 1 return button - + def AddDualButton(self, label1, label2): button1 = wx.Button(self, -1, label1) self.GetSizer().Add(button1, pos=(self.rowNr, 0), flag=wx.RIGHT) @@ -152,49 +163,50 @@ class InfoPage(wx.wizard.WizardPageSimple): self.GetSizer().Add(button2, pos=(self.rowNr, 1)) self.rowNr += 1 return button1, button2 - + def AddTextCtrl(self, value): ret = wx.TextCtrl(self, -1, value) - self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT) + self.GetSizer().Add(ret, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT) self.rowNr += 1 return ret def AddLabelTextCtrl(self, info, value): text = wx.StaticText(self, -1, info) ret = wx.TextCtrl(self, -1, value) - self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT) - self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1,1), flag=wx.LEFT) + self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT) + self.GetSizer().Add(ret, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT) self.rowNr += 1 return ret - + def AddTextCtrlButton(self, value, buttonText): text = wx.TextCtrl(self, -1, value) button = wx.Button(self, -1, buttonText) - self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT) - self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1,1), flag=wx.LEFT) + self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT) + self.GetSizer().Add(button, pos=(self.rowNr, 1), span=(1, 1), flag=wx.LEFT) self.rowNr += 1 return text, button def AddBitmap(self, bitmap): bitmap = wx.StaticBitmap(self, -1, bitmap) - self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1,2), flag=wx.LEFT|wx.RIGHT) + self.GetSizer().Add(bitmap, pos=(self.rowNr, 0), span=(1, 2), flag=wx.LEFT | wx.RIGHT) self.rowNr += 1 return bitmap def AddCheckmark(self, label, bitmap): check = wx.StaticBitmap(self, -1, bitmap) text = wx.StaticText(self, -1, label) - self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1,1), flag=wx.LEFT|wx.RIGHT) - self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1,1), flag=wx.ALL) + self.GetSizer().Add(text, pos=(self.rowNr, 0), span=(1, 1), flag=wx.LEFT | wx.RIGHT) + self.GetSizer().Add(check, pos=(self.rowNr, 1), span=(1, 1), flag=wx.ALL) self.rowNr += 1 return check - + def AllowNext(self): return True - + def StoreData(self): pass + class FirstInfoPage(InfoPage): def __init__(self, parent): super(FirstInfoPage, self).__init__(parent, "First time run wizard") @@ -204,13 +216,16 @@ class FirstInfoPage(InfoPage): self.AddText('* Configure Cura for your machine') self.AddText('* Upgrade your firmware') self.AddText('* Check if your machine is working safely') + #self.AddText('* Calibrate your machine') #self.AddText('* Do your first print') + class RepRapInfoPage(InfoPage): def __init__(self, parent): super(RepRapInfoPage, self).__init__(parent, "RepRap information") - self.AddText('RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.') + self.AddText( + 'RepRap machines are vastly different, and there is no\ndefault configuration in Cura for any of them.') self.AddText('If you like a default profile for your machine added,\nthen make an issue on github.') self.AddSeperator() self.AddText('You will have to manually install Marlin or Sprinter firmware.') @@ -229,6 +244,7 @@ class RepRapInfoPage(InfoPage): profile.putProfileSetting('wall_thickness', float(profile.getProfileSettingFloat('nozzle_size')) * 2) profile.putPreference('has_heated_bed', str(self.heatedBed.GetValue())) + class MachineSelectPage(InfoPage): def __init__(self, parent): super(MachineSelectPage, self).__init__(parent, "Select your machine") @@ -239,13 +255,13 @@ class MachineSelectPage(InfoPage): self.UltimakerRadio.Bind(wx.EVT_RADIOBUTTON, self.OnUltimakerSelect) self.OtherRadio = self.AddRadioButton("Other (Ex: RepRap)") self.OtherRadio.Bind(wx.EVT_RADIOBUTTON, self.OnOtherSelect) - + def OnUltimakerSelect(self, e): wx.wizard.WizardPageSimple.Chain(self, self.GetParent().ultimakerFirmwareUpgradePage) - + def OnOtherSelect(self, e): wx.wizard.WizardPageSimple.Chain(self, self.GetParent().repRapInfoPage) - + def StoreData(self): if self.UltimakerRadio.GetValue(): profile.putPreference('machine_width', '205') @@ -262,6 +278,7 @@ class MachineSelectPage(InfoPage): profile.putProfileSetting('nozzle_size', '0.5') profile.putProfileSetting('wall_thickness', float(profile.getProfileSetting('nozzle_size')) * 2) + class SelectParts(InfoPage): def __init__(self, parent): super(SelectParts, self).__init__(parent, "Select upgraded parts you have") @@ -283,14 +300,18 @@ class SelectParts(InfoPage): if getPreference('ultimaker_extruder_upgrade') == 'True': putProfileSetting('retraction_enable', 'True') + class FirmwareUpgradePage(InfoPage): def __init__(self, parent): super(FirmwareUpgradePage, self).__init__(parent, "Upgrade Ultimaker Firmware") - self.AddText('Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.') + self.AddText( + 'Firmware is the piece of software running directly on your 3D printer.\nThis firmware controls the step motors, regulates the temperature\nand ultimately makes your printer work.') self.AddHiddenSeperator() - self.AddText('The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.') + self.AddText( + 'The firmware shipping with new Ultimakers works, but upgrades\nhave been made to make better prints, and make calibration easier.') self.AddHiddenSeperator() - self.AddText('Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.') + self.AddText( + 'Cura requires these new features and thus\nyour firmware will most likely need to be upgraded.\nYou will get the chance to do so now.') upgradeButton, skipUpgradeButton = self.AddDualButton('Upgrade to Marlin firmware', 'Skip upgrade') upgradeButton.Bind(wx.EVT_BUTTON, self.OnUpgradeClick) skipUpgradeButton.Bind(wx.EVT_BUTTON, self.OnSkipClick) @@ -300,36 +321,38 @@ class FirmwareUpgradePage(InfoPage): self.AddText('* Have other changes in the firmware') button = self.AddButton('Goto this page for a custom firmware') button.Bind(wx.EVT_BUTTON, self.OnUrlClick) - + def AllowNext(self): return False - + def OnUpgradeClick(self, e): if firmwareInstall.InstallFirmware(): self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() - + def OnSkipClick(self, e): self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() - + def OnUrlClick(self, e): webbrowser.open('http://daid.mine.nu/~daid/marlin_build/') + class UltimakerCheckupPage(InfoPage): def __init__(self, parent): super(UltimakerCheckupPage, self).__init__(parent, "Ultimaker Checkup") - self.checkBitmap = toolbarUtil.getBitmapImage('checkmark.png') - self.crossBitmap = toolbarUtil.getBitmapImage('cross.png') - self.unknownBitmap = toolbarUtil.getBitmapImage('question.png') - self.endStopNoneBitmap = toolbarUtil.getBitmapImage('endstop_none.png') - self.endStopXMinBitmap = toolbarUtil.getBitmapImage('endstop_xmin.png') - self.endStopXMaxBitmap = toolbarUtil.getBitmapImage('endstop_xmax.png') - self.endStopYMinBitmap = toolbarUtil.getBitmapImage('endstop_ymin.png') - self.endStopYMaxBitmap = toolbarUtil.getBitmapImage('endstop_ymax.png') - self.endStopZMinBitmap = toolbarUtil.getBitmapImage('endstop_zmin.png') - self.endStopZMaxBitmap = toolbarUtil.getBitmapImage('endstop_zmax.png') + self.checkBitmap = wx.Bitmap(getPathForImage('checkmark.png')) + self.crossBitmap = wx.Bitmap(getPathForImage('cross.png')) + self.unknownBitmap = wx.Bitmap(getPathForImage('question.png')) + self.endStopNoneBitmap = wx.Bitmap(getPathForImage('endstop_none.png')) + self.endStopXMinBitmap = wx.Bitmap(getPathForImage('endstop_xmin.png')) + self.endStopXMaxBitmap = wx.Bitmap(getPathForImage('endstop_xmax.png')) + self.endStopYMinBitmap = wx.Bitmap(getPathForImage('endstop_ymin.png')) + self.endStopYMaxBitmap = wx.Bitmap(getPathForImage('endstop_ymax.png')) + self.endStopZMinBitmap = wx.Bitmap(getPathForImage('endstop_zmin.png')) + self.endStopZMaxBitmap = wx.Bitmap(getPathForImage('endstop_zmax.png')) - self.AddText('It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional.') + self.AddText( + 'It is a good idea to do a few sanity checks now on your Ultimaker.\nYou can skip these if you know your machine is functional.') b1, b2 = self.AddDualButton('Run checks', 'Skip checks') b1.Bind(wx.EVT_BUTTON, self.OnCheckClick) b2.Bind(wx.EVT_BUTTON, self.OnSkipClick) @@ -352,21 +375,21 @@ class UltimakerCheckupPage(InfoPage): self.yMaxStop = False self.zMinStop = False self.zMaxStop = False - + self.Bind(wx.EVT_BUTTON, self.OnErrorLog, self.errorLogButton) def __del__(self): if self.comm != None: self.comm.close() - + def AllowNext(self): self.endstopBitmap.Show(False) return False - + def OnSkipClick(self, e): self.GetParent().FindWindowById(wx.ID_FORWARD).Enable() - - def OnCheckClick(self, e = None): + + def OnCheckClick(self, e=None): self.errorLogButton.Show(False) if self.comm != None: self.comm.close() @@ -381,7 +404,7 @@ class UltimakerCheckupPage(InfoPage): self.stopState.SetBitmap(self.unknownBitmap) self.checkupState = 0 self.comm = machineCom.MachineCom(callbackObject=self) - + def OnErrorLog(self, e): printWindow.LogWindow('\n'.join(self.comm.getLog())) @@ -447,7 +470,7 @@ class UltimakerCheckupPage(InfoPage): wx.CallAfter(self.Layout) else: wx.CallAfter(self.machineState.SetLabel, 'Communication State: %s' % (self.comm.getStateString())) - + def mcMessage(self, message): if self.checkupState >= 3 and self.checkupState < 10 and 'x_min' in message: for data in message.split(' '): @@ -466,7 +489,7 @@ class UltimakerCheckupPage(InfoPage): if tag == 'z_max': self.zMaxStop = (value == 'H') self.comm.sendCommand('M119') - + if self.checkupState == 3: if not self.xMinStop and not self.xMaxStop and not self.yMinStop and not self.yMaxStop and not self.zMinStop and not self.zMaxStop: self.checkupState = 4 @@ -509,14 +532,15 @@ class UltimakerCheckupPage(InfoPage): def mcProgress(self, lineNr): pass - + def mcZChange(self, newZ): pass + class UltimakerCalibrationPage(InfoPage): def __init__(self, parent): super(UltimakerCalibrationPage, self).__init__(parent, "Ultimaker Calibration") - + self.AddText("Your Ultimaker requires some calibration.") self.AddText("This calibration is needed for a proper extrusion amount.") self.AddSeperator() @@ -528,19 +552,21 @@ class UltimakerCalibrationPage(InfoPage): self.AddSeperator() self.AddText("First we need the diameter of your filament:") self.filamentDiameter = self.AddTextCtrl(profile.getProfileSetting('filament_diameter')) - self.AddText("If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.") + self.AddText( + "If you do not own digital Calipers that can measure\nat least 2 digits then use 2.89mm.\nWhich is the average diameter of most filament.") self.AddText("Note: This value can be changed later at any time.") def StoreData(self): profile.putProfileSetting('filament_diameter', self.filamentDiameter.GetValue()) + class UltimakerCalibrateStepsPerEPage(InfoPage): def __init__(self, parent): super(UltimakerCalibrateStepsPerEPage, self).__init__(parent, "Ultimaker Calibration") if profile.getPreference('steps_per_e') == '0': profile.putPreference('steps_per_e', '865.888') - + self.AddText("Calibrating the Steps Per E requires some manual actions.") self.AddText("First remove any filament from your machine.") self.AddText("Next put in your filament so the tip is aligned with the\ntop of the extruder drive.") @@ -552,20 +578,21 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): self.stepsPerEInput = self.AddTextCtrl(profile.getPreference('steps_per_e')) self.AddText("You can repeat these steps to get better calibration.") self.AddSeperator() - self.AddText("If you still have filament in your printer which needs\nheat to remove, press the heat up button below:") + self.AddText( + "If you still have filament in your printer which needs\nheat to remove, press the heat up button below:") self.heatButton = self.AddButton("Heatup for filament removal") - + self.saveLengthButton.Bind(wx.EVT_BUTTON, self.OnSaveLengthClick) self.extrudeButton.Bind(wx.EVT_BUTTON, self.OnExtrudeClick) self.heatButton.Bind(wx.EVT_BUTTON, self.OnHeatClick) - + def OnSaveLengthClick(self, e): currentEValue = float(self.stepsPerEInput.GetValue()) realExtrudeLength = float(self.lengthInput.GetValue()) newEValue = currentEValue * 100 / realExtrudeLength self.stepsPerEInput.SetValue(str(newEValue)) self.lengthInput.SetValue("100") - + def OnExtrudeClick(self, e): threading.Thread(target=self.OnExtrudeRun).start() @@ -575,7 +602,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): currentEValue = float(self.stepsPerEInput.GetValue()) self.comm = machineCom.MachineCom() if not self.comm.isOpen(): - wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION) + wx.MessageBox( + "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", + 'Printer error', wx.OK | wx.ICON_INFORMATION) self.heatButton.Enable(True) self.extrudeButton.Enable(True) return @@ -585,9 +614,9 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): return if 'start' in line: break - #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. + #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. time.sleep(3) - + self.sendGCommand('M302') #Disable cold extrusion protection self.sendGCommand("M92 E%f" % (currentEValue)) self.sendGCommand("G92 E0") @@ -599,13 +628,15 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): def OnHeatClick(self, e): threading.Thread(target=self.OnHeatRun).start() - + def OnHeatRun(self): self.heatButton.Enable(False) self.extrudeButton.Enable(False) self.comm = machineCom.MachineCom() if not self.comm.isOpen(): - wx.MessageBox("Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", 'Printer error', wx.OK | wx.ICON_INFORMATION) + wx.MessageBox( + "Error: Failed to open serial port to machine\nIf this keeps happening, try disconnecting and reconnecting the USB cable", + 'Printer error', wx.OK | wx.ICON_INFORMATION) self.heatButton.Enable(True) self.extrudeButton.Enable(True) return @@ -617,17 +648,19 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): return if 'start' in line: break - #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. + #Wait 3 seconds for the SD card init to timeout if we have SD in our firmware but there is no SD card found. time.sleep(3) - + self.sendGCommand('M104 S200') #Set the temperature to 200C, should be enough to get PLA and ABS out. - wx.MessageBox('Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)', 'Machine heatup', wx.OK | wx.ICON_INFORMATION) + wx.MessageBox( + 'Wait till you can remove the filament from the machine, and press OK.\n(Temperature is set to 200C)', + 'Machine heatup', wx.OK | wx.ICON_INFORMATION) self.sendGCommand('M104 S0') time.sleep(1) self.comm.close() self.heatButton.Enable(True) self.extrudeButton.Enable(True) - + def sendGCommand(self, cmd): self.comm.sendCommand(cmd) #Disable cold extrusion protection while True: @@ -636,14 +669,15 @@ class UltimakerCalibrateStepsPerEPage(InfoPage): return if line.startswith('ok'): break - + def StoreData(self): profile.putPreference('steps_per_e', self.stepsPerEInput.GetValue()) + class configWizard(wx.wizard.Wizard): def __init__(self): super(configWizard, self).__init__(None, -1, "Configuration Wizard") - + self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.OnPageChanged) self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) @@ -662,10 +696,10 @@ class configWizard(wx.wizard.Wizard): wx.wizard.WizardPageSimple.Chain(self.ultimakerFirmwareUpgradePage, self.ultimakerCheckupPage) #wx.wizard.WizardPageSimple.Chain(self.ultimakerCheckupPage, self.ultimakerCalibrationPage) #wx.wizard.WizardPageSimple.Chain(self.ultimakerCalibrationPage, self.ultimakerCalibrateStepsPerEPage) - + self.FitToPage(self.firstInfoPage) self.GetPageAreaSizer().Add(self.firstInfoPage) - + self.RunWizard(self.firstInfoPage) self.Destroy() @@ -674,7 +708,7 @@ class configWizard(wx.wizard.Wizard): def OnPageChanged(self, e): if e.GetPage().AllowNext(): - self.FindWindowById(wx.ID_FORWARD).Enable() + self.FindWindowById(wx.ID_FORWARD).Enable() else: - self.FindWindowById(wx.ID_FORWARD).Disable() - self.FindWindowById(wx.ID_BACKWARD).Disable() + self.FindWindowById(wx.ID_FORWARD).Disable() + self.FindWindowById(wx.ID_BACKWARD).Disable() diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index f20e78d..d435194 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -25,7 +25,7 @@ from util import version from util import sliceRun from util import meshLoader -def main(splash): +def main(): #app = wx.App(False) if profile.getPreference('machine_type') == 'unknown': if platform.system() == "Darwin": @@ -39,7 +39,6 @@ def main(splash): for filename in glob.glob(os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'example', '*.*'))): shutil.copy(filename, os.path.join(os.path.dirname(exampleFile), os.path.basename(filename))) profile.putPreference('lastFile', exampleFile) - splash.Show(False) configWizard.configWizard() if profile.getPreference('startMode') == 'Simple': simpleMode.simpleModeWindow() @@ -53,12 +52,12 @@ class mainWindow(configBase.configWindowBase): super(mainWindow, self).__init__(title='Cura - ' + version.getVersion()) extruderCount = int(profile.getPreference('extruder_amount')) - + wx.EVT_CLOSE(self, self.OnClose) #self.SetIcon(icon.getMainIcon()) - + self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.supportedExtensions())) - + menubar = wx.MenuBar() fileMenu = wx.Menu() i = fileMenu.Append(-1, 'Load model file...\tCTRL+L') @@ -97,7 +96,7 @@ class mainWindow(configBase.configWindowBase): # i = toolsMenu.Append(-1, 'Open SVG (2D) slicer...') # self.Bind(wx.EVT_MENU, self.OnSVGSlicerOpen, i) menubar.Append(toolsMenu, 'Tools') - + expertMenu = wx.Menu() i = expertMenu.Append(-1, 'Open expert settings...') self.Bind(wx.EVT_MENU, self.OnExpertOpen, i) @@ -111,7 +110,7 @@ class mainWindow(configBase.configWindowBase): i = expertMenu.Append(-1, 'ReRun first run wizard...') self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i) menubar.Append(expertMenu, 'Expert') - + helpMenu = wx.Menu() i = helpMenu.Append(-1, 'Online documentation...') self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i) @@ -119,7 +118,7 @@ class mainWindow(configBase.configWindowBase): self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i) menubar.Append(helpMenu, 'Help') self.SetMenuBar(menubar) - + if profile.getPreference('lastFile') != '': self.filelist = profile.getPreference('lastFile').split(';') self.SetTitle('Cura - %s - %s' % (version.getVersion(), self.filelist[-1])) @@ -132,9 +131,9 @@ class mainWindow(configBase.configWindowBase): #Main tabs nb = wx.Notebook(self) - + (left, right) = self.CreateConfigTab(nb, 'Print config') - + configBase.TitleRow(left, "Quality") c = configBase.SettingRow(left, "Layer height (mm)", 'layer_height', '0.2', 'Layer height in millimeters.\n0.2 is a good value for quick prints.\n0.1 gives high quality prints.') validators.validFloat(c, 0.0001) @@ -143,19 +142,19 @@ class mainWindow(configBase.configWindowBase): validators.validFloat(c, 0.0001) validators.wallThicknessValidator(c) c = configBase.SettingRow(left, "Enable retraction", 'retraction_enable', False, 'Retract the filament when the nozzle is moving over a none-printed area. Details about the retraction can be configured in the advanced tab.') - + configBase.TitleRow(left, "Fill") c = configBase.SettingRow(left, "Bottom/Top thickness (mm)", 'solid_layer_thickness', '0.6', 'This controls the thickness of the bottom and top layers, the amount of solid layers put down is calculated by the layer thickness and this value.\nHaving this value a multiply of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part.') validators.validFloat(c, 0.0) c = configBase.SettingRow(left, "Fill Density (%)", 'fill_density', '20', 'This controls how densily filled the insides of your print will be. For a solid part use 100%, for an empty part use 0%. A value around 20% is usually enough') validators.validFloat(c, 0.0, 100.0) - + configBase.TitleRow(right, "Speed && Temperature") c = configBase.SettingRow(right, "Print speed (mm/s)", 'print_speed', '50', 'Speed at which printing happens. A well adjusted Ultimaker can reach 150mm/s, but for good quality prints you want to print slower. Printing speed depends on a lot of factors. So you will be experimenting with optimal settings for this.') validators.validFloat(c, 1.0) validators.warningAbove(c, 150.0, "It is highly unlikely that your machine can achieve a printing speed above 150mm/s") validators.printSpeedValidator(c) - + #configBase.TitleRow(right, "Temperature") c = configBase.SettingRow(right, "Printing temperature", 'print_temperature', '0', 'Temperature used for printing. Set at 0 to pre-heat yourself') validators.validFloat(c, 0.0, 340.0) @@ -163,7 +162,7 @@ class mainWindow(configBase.configWindowBase): if profile.getPreference('has_heated_bed') == 'True': c = configBase.SettingRow(right, "Bed temperature", 'print_bed_temperature', '0', 'Temperature used for the heated printer bed. Set at 0 to pre-heat yourself') validators.validFloat(c, 0.0, 340.0) - + configBase.TitleRow(right, "Support structure") c = configBase.SettingRow(right, "Support type", 'support', ['None', 'Exterior Only', 'Everywhere'], 'Type of support structure build.\n"Exterior only" is the most commonly used support setting.\n\nNone does not do any support.\nExterior only only creates support where the support structure will touch the build platform.\nEverywhere creates support even on the insides of the model.') c = configBase.SettingRow(right, "Add raft", 'enable_raft', False, 'A raft is a few layers of lines below the bottom of the object. It prevents warping. Full raft settings can be found in the expert settings.\nFor PLA this is usually not required. But if you print with ABS it is almost required.') @@ -176,9 +175,9 @@ class mainWindow(configBase.configWindowBase): validators.warningAbove(c, 3.5, "Are you sure your filament is that thick? Normal filament is around 3mm or 1.75mm.") c = configBase.SettingRow(right, "Packing Density", 'filament_density', '1.00', 'Packing density of your filament. This should be 1.00 for PLA and 0.85 for ABS') validators.validFloat(c, 0.5, 1.5) - + (left, right) = self.CreateConfigTab(nb, 'Advanced config') - + configBase.TitleRow(left, "Machine size") c = configBase.SettingRow(left, "Nozzle size (mm)", 'nozzle_size', '0.4', 'The nozzle size is very important, this is used to calculate the line width of the infill, and used to calculate the amount of outside wall lines and thickness for the wall thickness you entered in the print settings.') validators.validFloat(c, 0.1, 10.0) @@ -275,7 +274,7 @@ class mainWindow(configBase.configWindowBase): self.updateProfileToControls() self.SetBackgroundColour(nb.GetBackgroundColour()) - + self.Fit() if wx.Display().GetClientArea().GetWidth() < self.GetSize().GetWidth(): f = self.GetSize().GetWidth() - wx.Display().GetClientArea().GetWidth() @@ -285,7 +284,7 @@ class mainWindow(configBase.configWindowBase): self.SetMinSize(self.GetSize()) self.Centre() self.Show(True) - + def OnLoadProfile(self, e): dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg.SetWildcard("ini files (*.ini)|*.ini") @@ -311,7 +310,7 @@ class mainWindow(configBase.configWindowBase): else: wx.MessageBox('No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer.', 'Profile load error', wx.OK | wx.ICON_INFORMATION) dlg.Destroy() - + def OnSaveProfile(self, e): dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE) dlg.SetWildcard("ini files (*.ini)|*.ini") @@ -319,7 +318,7 @@ class mainWindow(configBase.configWindowBase): profileFile = dlg.GetPath() profile.saveGlobalProfile(profileFile) dlg.Destroy() - + def OnResetProfile(self, e): dlg = wx.MessageDialog(self, 'This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?', 'Profile reset', wx.YES_NO | wx.ICON_QUESTION) result = dlg.ShowModal() == wx.ID_YES @@ -327,22 +326,22 @@ class mainWindow(configBase.configWindowBase): if result: profile.resetGlobalProfile() self.updateProfileToControls() - + def OnBatchRun(self, e): br = batchRun.batchRunWindow(self) br.Centre() br.Show(True) - + def OnPreferences(self, e): prefDialog = preferencesDialog.preferencesDialog(self) prefDialog.Centre() prefDialog.Show(True) - + def OnSimpleSwitch(self, e): profile.putPreference('startMode', 'Simple') simpleMode.simpleModeWindow() self.Close() - + def OnDefaultMarlinFirmware(self, e): firmwareInstall.InstallFirmware() @@ -382,7 +381,7 @@ class mainWindow(configBase.configWindowBase): if filelist[-1] == False: return self._loadModels(filelist) - + def _loadModels(self, filelist): self.filelist = filelist self.SetTitle(filelist[-1] + ' - Cura - ' + version.getVersion()) @@ -395,7 +394,7 @@ class mainWindow(configBase.configWindowBase): def OnLoadModel(self, e): self._showModelLoadDialog(1) - + def OnLoadModel2(self, e): self._showModelLoadDialog(2) @@ -404,7 +403,7 @@ class mainWindow(configBase.configWindowBase): def OnLoadModel4(self, e): self._showModelLoadDialog(4) - + def OnSlice(self, e): if len(self.filelist) < 1: wx.MessageBox('You need to load a file before you can prepare it.', 'Print error', wx.OK | wx.ICON_INFORMATION) @@ -418,7 +417,7 @@ class mainWindow(configBase.configWindowBase): if newSize.GetWidth() < wx.GetDisplaySize()[0]: self.SetSize(newSize) self.progressPanelList.append(spp) - + def OnPrint(self, e): if len(self.filelist) < 1: wx.MessageBox('You need to load a file and prepare it before you can print.', 'Print error', wx.OK | wx.ICON_INFORMATION) @@ -432,7 +431,7 @@ class mainWindow(configBase.configWindowBase): ecw = expertConfig.expertConfigWindow() ecw.Centre() ecw.Show(True) - + def OnProjectPlanner(self, e): pp = projectPlanner.projectPlanner() pp.Centre() @@ -461,7 +460,7 @@ class mainWindow(configBase.configWindowBase): def OnQuit(self, e): self.Close() - + def OnClose(self, e): profile.saveGlobalProfile(profile.getDefaultProfilePath()) self.Destroy() diff --git a/Cura/gui/opengl.py b/Cura/gui/opengl.py index 9a8db81..8e327a7 100644 --- a/Cura/gui/opengl.py +++ b/Cura/gui/opengl.py @@ -1,14 +1,20 @@ -import math, time, os +# coding=utf-8 +from __future__ import absolute_import + +import math from util import meshLoader from util import util3d from util import profile +from util.resources import getPathForMesh try: import OpenGL + OpenGL.ERROR_CHECKING = False from OpenGL.GLU import * from OpenGL.GL import * + hasOpenGLlibs = True except: print "Failed to find PyOpenGL: http://pyopengl.sourceforge.net/" @@ -19,8 +25,8 @@ def InitGL(window, view3D, zoom): glMatrixMode(GL_MODELVIEW) glLoadIdentity() size = window.GetSize() - glViewport(0,0, size.GetWidth(), size.GetHeight()) - + glViewport(0, 0, size.GetWidth(), size.GetHeight()) + glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0]) glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0]) @@ -53,23 +59,23 @@ def DrawMachine(machineSize): if profile.getPreference('machine_type') == 'ultimaker': glPushMatrix() glEnable(GL_LIGHTING) - glTranslate(100,200,-5) - glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8,0.8,0.8]) - glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5,0.5,0.5]) + glTranslate(100, 200, -5) + glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8]) + glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5]) glEnable(GL_BLEND) glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR) - + global platformMesh if platformMesh == None: - platformMesh = meshLoader.loadMesh(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", 'ultimaker_platform.stl'))) + platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl')) platformMesh.setRotateMirror(0, False, False, False, False, False) - + DrawMesh(platformMesh) glPopMatrix() glDisable(GL_LIGHTING) if False: - glColor3f(0.7,0.7,0.7) + glColor3f(0.7, 0.7, 0.7) glLineWidth(2) glBegin(GL_LINES) for i in xrange(0, int(machineSize.x), 10): @@ -79,13 +85,13 @@ def DrawMachine(machineSize): glVertex3f(0, i, 0) glVertex3f(machineSize.x, i, 0) glEnd() - + glEnable(GL_LINE_SMOOTH) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glColor3f(0.0,0.0,0.0) + glColor3f(0.0, 0.0, 0.0) glLineWidth(4) glBegin(GL_LINE_LOOP) glVertex3f(0, 0, 0) @@ -93,7 +99,7 @@ def DrawMachine(machineSize): glVertex3f(machineSize.x, machineSize.y, 0) glVertex3f(0, machineSize.y, 0) glEnd() - + glLineWidth(2) glBegin(GL_LINE_LOOP) glVertex3f(0, 0, machineSize.z) @@ -115,47 +121,47 @@ def DrawMachine(machineSize): glDisable(GL_CULL_FACE) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glColor4ub(5,171,231,127) + glColor4ub(5, 171, 231, 127) glBegin(GL_QUADS) for x in xrange(0, int(machineSize.x), 20): for y in xrange(0, int(machineSize.y), 20): glVertex3f(x, y, -0.01) - glVertex3f(min(x+10, machineSize.x), y, -0.01) - glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) - glVertex3f(x, min(y+10, machineSize.y), -0.01) + glVertex3f(min(x + 10, machineSize.x), y, -0.01) + glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01) + glVertex3f(x, min(y + 10, machineSize.y), -0.01) for x in xrange(10, int(machineSize.x), 20): for y in xrange(10, int(machineSize.y), 20): glVertex3f(x, y, -0.01) - glVertex3f(min(x+10, machineSize.x), y, -0.01) - glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) - glVertex3f(x, min(y+10, machineSize.y), -0.01) + glVertex3f(min(x + 10, machineSize.x), y, -0.01) + glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01) + glVertex3f(x, min(y + 10, machineSize.y), -0.01) glEnd() - glColor4ub(5*8/10,171*8/10,231*8/10,128) + glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128) glBegin(GL_QUADS) for x in xrange(10, int(machineSize.x), 20): for y in xrange(0, int(machineSize.y), 20): glVertex3f(x, y, -0.01) - glVertex3f(min(x+10, machineSize.x), y, -0.01) - glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) - glVertex3f(x, min(y+10, machineSize.y), -0.01) + glVertex3f(min(x + 10, machineSize.x), y, -0.01) + glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01) + glVertex3f(x, min(y + 10, machineSize.y), -0.01) for x in xrange(0, int(machineSize.x), 20): for y in xrange(10, int(machineSize.y), 20): glVertex3f(x, y, -0.01) - glVertex3f(min(x+10, machineSize.x), y, -0.01) - glVertex3f(min(x+10, machineSize.x), min(y+10, machineSize.y), -0.01) - glVertex3f(x, min(y+10, machineSize.y), -0.01) + glVertex3f(min(x + 10, machineSize.x), y, -0.01) + glVertex3f(min(x + 10, machineSize.x), min(y + 10, machineSize.y), -0.01) + glVertex3f(x, min(y + 10, machineSize.y), -0.01) glEnd() glEnable(GL_CULL_FACE) - glColor4ub(5,171,231,64) + glColor4ub(5, 171, 231, 64) glBegin(GL_QUADS) glVertex3f(0, 0, machineSize.z) glVertex3f(0, machineSize.y, machineSize.z) glVertex3f(machineSize.x, machineSize.y, machineSize.z) glVertex3f(machineSize.x, 0, machineSize.z) glEnd() - - glColor4ub(5,171,231,96) + + glColor4ub(5, 171, 231, 96) glBegin(GL_QUADS) glVertex3f(0, 0, 0) glVertex3f(0, 0, machineSize.z) @@ -168,7 +174,7 @@ def DrawMachine(machineSize): glVertex3f(machineSize.x, machineSize.y, machineSize.z) glEnd() - glColor4ub(5,171,231,128) + glColor4ub(5, 171, 231, 128) glBegin(GL_QUADS) glVertex3f(0, 0, machineSize.z) glVertex3f(0, 0, 0) @@ -182,72 +188,73 @@ def DrawMachine(machineSize): glEnd() glDisable(GL_BLEND) - + glPushMatrix() - glTranslate(5,5,2) + glTranslate(5, 5, 2) glLineWidth(2) - glColor3f(0.5,0,0) + glColor3f(0.5, 0, 0) glBegin(GL_LINES) - glVertex3f(0,0,0) - glVertex3f(20,0,0) + glVertex3f(0, 0, 0) + glVertex3f(20, 0, 0) glEnd() - glColor3f(0,0.5,0) + glColor3f(0, 0.5, 0) glBegin(GL_LINES) - glVertex3f(0,0,0) - glVertex3f(0,20,0) + glVertex3f(0, 0, 0) + glVertex3f(0, 20, 0) glEnd() - glColor3f(0,0,0.5) + glColor3f(0, 0, 0.5) glBegin(GL_LINES) - glVertex3f(0,0,0) - glVertex3f(0,0,20) + glVertex3f(0, 0, 0) + glVertex3f(0, 0, 20) glEnd() glDisable(GL_DEPTH_TEST) #X - glColor3f(1,0,0) + glColor3f(1, 0, 0) glPushMatrix() - glTranslate(23,0,0) + glTranslate(23, 0, 0) noZ = ResetMatrixRotationAndScale() glBegin(GL_LINES) - glVertex3f(-0.8,1,0) - glVertex3f(0.8,-1,0) - glVertex3f(0.8,1,0) - glVertex3f(-0.8,-1,0) + glVertex3f(-0.8, 1, 0) + glVertex3f(0.8, -1, 0) + glVertex3f(0.8, 1, 0) + glVertex3f(-0.8, -1, 0) glEnd() glPopMatrix() #Y - glColor3f(0,1,0) + glColor3f(0, 1, 0) glPushMatrix() - glTranslate(0,23,0) + glTranslate(0, 23, 0) ResetMatrixRotationAndScale() glBegin(GL_LINES) - glVertex3f(-0.8, 1,0) - glVertex3f( 0.0, 0,0) - glVertex3f( 0.8, 1,0) - glVertex3f(-0.8,-1,0) + glVertex3f(-0.8, 1, 0) + glVertex3f(0.0, 0, 0) + glVertex3f(0.8, 1, 0) + glVertex3f(-0.8, -1, 0) glEnd() glPopMatrix() #Z if not noZ: - glColor3f(0,0,1) + glColor3f(0, 0, 1) glPushMatrix() - glTranslate(0,0,23) + glTranslate(0, 0, 23) ResetMatrixRotationAndScale() glBegin(GL_LINES) - glVertex3f(-0.8, 1,0) - glVertex3f( 0.8, 1,0) - glVertex3f( 0.8, 1,0) - glVertex3f(-0.8,-1,0) - glVertex3f(-0.8,-1,0) - glVertex3f( 0.8,-1,0) + glVertex3f(-0.8, 1, 0) + glVertex3f(0.8, 1, 0) + glVertex3f(0.8, 1, 0) + glVertex3f(-0.8, -1, 0) + glVertex3f(-0.8, -1, 0) + glVertex3f(0.8, -1, 0) glEnd() glPopMatrix() glPopMatrix() glEnable(GL_DEPTH_TEST) - + + def ResetMatrixRotationAndScale(): matrix = glGetFloatv(GL_MODELVIEW_MATRIX) noZ = False @@ -263,7 +270,7 @@ def ResetMatrixRotationAndScale(): matrix[0][2] = 0.0 matrix[1][2] = 0.0 matrix[2][2] = 1.0 - + if matrix[3][2] != 0.0: matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100) matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100) @@ -274,10 +281,11 @@ def ResetMatrixRotationAndScale(): matrix[2][2] = scale2D matrix[3][2] = -100 noZ = True - + glLoadMatrixf(matrix) return noZ + def DrawBox(vMin, vMax): glBegin(GL_LINE_LOOP) glVertex3f(vMin[0], vMin[1], vMin[2]) @@ -303,6 +311,7 @@ def DrawBox(vMin, vMax): glVertex3f(vMin[0], vMax[1], vMax[2]) glEnd() + def DrawMeshOutline(mesh): glEnable(GL_CULL_FACE) glEnableClientState(GL_VERTEX_ARRAY); @@ -314,9 +323,10 @@ def DrawMeshOutline(mesh): glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount) glPolygonMode(GL_BACK, GL_FILL) glCullFace(GL_BACK) - + glDisableClientState(GL_VERTEX_ARRAY) + def DrawMesh(mesh): glEnable(GL_CULL_FACE) glEnableClientState(GL_VERTEX_ARRAY); @@ -325,79 +335,81 @@ def DrawMesh(mesh): glNormalPointer(GL_FLOAT, 0, mesh.normal) #Odd, drawing in batchs is a LOT faster then drawing it all at once. - batchSize = 999 #Warning, batchSize needs to be dividable by 3 + batchSize = 999 #Warning, batchSize needs to be dividable by 3 extraStartPos = int(mesh.vertexCount / batchSize) * batchSize extraCount = mesh.vertexCount - extraStartPos - + glCullFace(GL_BACK) for i in xrange(0, int(mesh.vertexCount / batchSize)): - glDrawArrays(GL_TRIANGLES, i*batchSize, batchSize) + glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize) glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount) - + glCullFace(GL_FRONT) glNormalPointer(GL_FLOAT, 0, mesh.invNormal) for i in xrange(0, int(mesh.vertexCount / batchSize)): - glDrawArrays(GL_TRIANGLES, i*batchSize, batchSize) + glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize) extraStartPos = int(mesh.vertexCount / batchSize) * batchSize extraCount = mesh.vertexCount - extraStartPos glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount) glCullFace(GL_BACK) - + glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_NORMAL_ARRAY); + def DrawMeshSteep(mesh, angle): cosAngle = math.sin(angle / 180.0 * math.pi) glDisable(GL_LIGHTING) glDepthFunc(GL_EQUAL) for i in xrange(0, int(mesh.vertexCount), 3): if mesh.normal[i][2] < -0.999999: - if mesh.vertexes[i+0][2] > 0.01: - glColor3f(0.5,0,0) + if mesh.vertexes[i + 0][2] > 0.01: + glColor3f(0.5, 0, 0) glBegin(GL_TRIANGLES) - glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) - glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) - glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) + glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2]) + glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2]) + glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2]) glEnd() elif mesh.normal[i][2] < -cosAngle: - glColor3f(-mesh.normal[i][2],0,0) + glColor3f(-mesh.normal[i][2], 0, 0) glBegin(GL_TRIANGLES) - glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) - glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) - glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) + glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2]) + glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2]) + glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2]) glEnd() elif mesh.normal[i][2] > 0.999999: - if mesh.vertexes[i+0][2] > 0.01: - glColor3f(0.5,0,0) + if mesh.vertexes[i + 0][2] > 0.01: + glColor3f(0.5, 0, 0) glBegin(GL_TRIANGLES) - glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) - glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) - glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) + glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2]) + glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2]) + glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2]) glEnd() elif mesh.normal[i][2] > cosAngle: - glColor3f(mesh.normal[i][2],0,0) + glColor3f(mesh.normal[i][2], 0, 0) glBegin(GL_TRIANGLES) - glVertex3f(mesh.vertexes[i+0][0], mesh.vertexes[i+0][1], mesh.vertexes[i+0][2]) - glVertex3f(mesh.vertexes[i+2][0], mesh.vertexes[i+2][1], mesh.vertexes[i+2][2]) - glVertex3f(mesh.vertexes[i+1][0], mesh.vertexes[i+1][1], mesh.vertexes[i+1][2]) + glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2]) + glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2]) + glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2]) glEnd() glDepthFunc(GL_LESS) + def DrawGCodeLayer(layer): filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2 filamentArea = math.pi * filamentRadius * filamentRadius lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10 - + fillCycle = 0 - fillColorCycle = [[0.5,0.5,0.0],[0.0,0.5,0.5],[0.5,0.0,0.5]] - moveColor = [0,0,1] - retractColor = [1,0,0.5] - supportColor = [0,1,1] - extrudeColor = [1,0,0] - innerWallColor = [0,1,0] - skirtColor = [0,0.5,0.5] + fillColorCycle = [[0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]] + moveColor = [0, 0, 1] + retractColor = [1, 0, 0.5] + supportColor = [0, 1, 1] + extrudeColor = [1, 0, 0] + innerWallColor = [0, 1, 0] + skirtColor = [0, 0.5, 0.5] prevPathWasRetract = False - + glDisable(GL_CULL_FACE) for path in layer: if path.type == 'move': @@ -420,13 +432,13 @@ def DrawGCodeLayer(layer): else: c = extrudeColor if path.type == 'retract': - c = [0,1,1] + c = [0, 1, 1] if path.type == 'extrude': drawLength = 0.0 prevNormal = None - for i in xrange(0, len(path.list)-1): + for i in xrange(0, len(path.list) - 1): v0 = path.list[i] - v1 = path.list[i+1] + v1 = path.list[i + 1] # Calculate line width from ePerDistance (needs layer thickness and filament diameter) dist = (v0 - v1).vsize() @@ -435,7 +447,7 @@ def DrawGCodeLayer(layer): lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply drawLength += (v0 - v1).vsize() - normal = (v0 - v1).cross(util3d.Vector3(0,0,1)) + normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1)) normal.normalize() vv2 = v0 + normal * lineWidth @@ -461,13 +473,13 @@ def DrawGCodeLayer(layer): glVertex3f(vv4.x, vv4.y, vv4.z - zOffset) glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset) glVertex3f(v0.x, v0.y, v0.z - zOffset) - + glVertex3f(vv0.x, vv0.y, vv0.z - zOffset) glVertex3f(vv5.x, vv5.y, vv5.z - zOffset) glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset) glVertex3f(v0.x, v0.y, v0.z - zOffset) glEnd() - + prevNormal = normal prevVv1 = vv1 prevVv3 = vv3 diff --git a/Cura/gui/printWindow.py b/Cura/gui/printWindow.py index e2cf43d..145fd7c 100644 --- a/Cura/gui/printWindow.py +++ b/Cura/gui/printWindow.py @@ -1,714 +1,742 @@ -from __future__ import absolute_import -import __init__ - -import wx, threading, re, subprocess, sys, os, time, platform -from wx.lib import buttons - -from gui import icon -from gui import toolbarUtil -from gui import webcam -from gui import taskbar -from util import machineCom -from util import profile -from util import gcodeInterpreter -from util import power - -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() - -class printProcessMonitor(): - def __init__(self): - self.handle = None - - def loadFile(self, filename): +# coding=utf-8 +from __future__ import absolute_import + +import threading +import re +import subprocess +import sys +import time +import platform + +import wx +from wx.lib import buttons + +from gui import icon +from gui import toolbarUtil +from gui import webcam +from gui import taskbar +from util import machineCom +from util import profile +from util import gcodeInterpreter +from util import power +from 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() + + +class printProcessMonitor(): + def __init__(self): + self.handle = None + + def loadFile(self, filename): if self.handle == None: cmdList = [sys.executable, sys.argv[0], '-r', 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) - self.thread = threading.Thread(target=self.Monitor) - self.thread.start() - else: - self.handle.stdin.write(filename + '\n') - - def Monitor(self): - p = self.handle - line = p.stdout.readline() - while(len(line) > 0): - #print line.rstrip() - line = p.stdout.readline() - p.communicate() - self.handle = None - self.thread = None - -class PrintCommandButton(buttons.GenBitmapButton): - def __init__(self, parent, commandList, bitmapFilename, size=(20,20)): - self.bitmap = toolbarUtil.getBitmapImage(bitmapFilename) - super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size) - - self.commandList = commandList - self.parent = parent - - self.SetBezelWidth(1) - self.SetUseFocusIndicator(False) - - self.Bind(wx.EVT_BUTTON, self.OnClick) - - def OnClick(self, e): - if self.parent.machineCom == None or self.parent.machineCom.isPrinting(): - return; - for cmd in self.commandList: - self.parent.machineCom.sendCommand(cmd) - e.Skip() - -class printWindow(wx.Frame): - "Main user interface window" - def __init__(self): - super(printWindow, self).__init__(None, -1, title='Printing') - self.machineCom = None - self.gcode = None - self.gcodeList = None - self.sendList = [] - self.temp = None - self.bedTemp = None - self.bufferLineCount = 4 - self.sendCnt = 0 - self.feedrateRatioOuterWall = 1.0 - self.feedrateRatioInnerWall = 1.0 - self.feedrateRatioFill = 1.0 - self.feedrateRatioSupport = 1.0 - self.pause = False - self.termHistory = [] - self.termHistoryIdx = 0 - - self.cam = None - if webcam.hasWebcamSupport(): - self.cam = webcam.webcam() - if not self.cam.hasCamera(): - self.cam = None - - #self.SetIcon(icon.getMainIcon()) - - self.SetSizer(wx.BoxSizer()) - self.panel = wx.Panel(self) - self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND) - self.sizer = wx.GridBagSizer(2, 2) - self.panel.SetSizer(self.sizer) - - 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.", - style=wx.ALIGN_CENTER) - self.powerWarningText.SetBackgroundColour('red') - self.powerWarningText.SetForegroundColour('white') - boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10)) - self.powerManagement = power.PowerManagement() - self.powerWarningTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer) - self.OnPowerWarningChange(None) - self.powerWarningTimer.Start(10000) - - 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.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.progress = wx.Gauge(self.panel, -1) - - self.sizer.Add(self.connectButton, pos=(1,1), flag=wx.EXPAND) - #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND) - self.sizer.Add(self.printButton, pos=(2,1), flag=wx.EXPAND) - self.sizer.Add(self.pauseButton, pos=(3,1), flag=wx.EXPAND) - self.sizer.Add(self.cancelButton, pos=(4,1), flag=wx.EXPAND) - self.sizer.Add(self.machineLogButton, pos=(5,1), flag=wx.EXPAND) - self.sizer.Add(self.progress, pos=(7,0), span=(1,7), flag=wx.EXPAND) - - nb = wx.Notebook(self.panel) - self.sizer.Add(nb, pos=(0,2), span=(7,4), flag=wx.EXPAND) - - self.temperaturePanel = wx.Panel(nb) - sizer = wx.GridBagSizer(2, 2) - self.temperaturePanel.SetSizer(sizer) - - 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.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(self.temperatureSelect, pos=(0,1)) - 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.AddGrowableRow(2) - sizer.AddGrowableCol(1) - - nb.AddPage(self.temperaturePanel, 'Temp') - - self.directControlPanel = wx.Panel(nb) - - sizer = wx.GridBagSizer(2, 2) - self.directControlPanel.SetSizer(sizer) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0,3)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1,3)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2,3)) - - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4,3)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5,3)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6,3)) - - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3,0)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3,1)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3,2)) - - sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3,3)) - - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3,4)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3,5)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3,6)) - - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0,8)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1,8)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2,8)) - - sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3,8)) - - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4,8)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5,8)) - sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6,8)) - - sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60,20)), pos=(1,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') - - self.speedPanel = wx.Panel(nb) - sizer = wx.GridBagSizer(2, 2) - self.speedPanel.SetSizer(sizer) - - self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS) - self.outerWallSpeedSelect.SetRange(5, 1000) - self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS) - self.innerWallSpeedSelect.SetRange(5, 1000) - self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21*3,21), style=wx.SP_ARROW_KEYS) - self.fillSpeedSelect.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(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(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(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(self.supportSpeedSelect, pos=(3,1)) - sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3,2)) - - nb.AddPage(self.speedPanel, 'Speed') - - self.termPanel = wx.Panel(nb) - sizer = wx.GridBagSizer(2, 2) - self.termPanel.SetSizer(sizer) - - f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) - self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE|wx.TE_DONTWRAP) - self.termLog.SetFont(f) - self.termLog.SetEditable(0) - self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER) - self.termInput.SetFont(f) - - sizer.Add(self.termLog, pos=(0,0),flag=wx.EXPAND) - sizer.Add(self.termInput, pos=(1,0),flag=wx.EXPAND) - 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) - - self.sizer.AddGrowableRow(6) - self.sizer.AddGrowableCol(3) - - self.Bind(wx.EVT_CLOSE, self.OnClose) - self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect) - #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad) - self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint) - self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause) - self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel) - self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog) - - self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect) - self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect) - - self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect) - self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect) - self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect) - self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect) - self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput) - self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey) - - self.Layout() - self.Fit() - self.Centre() - - self.statsText.SetMinSize(self.statsText.GetSize()) + 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.UpdateButtonStates() - #self.UpdateProgress() - - def OnCameraTimer(self, e): - if not self.campreviewEnable.GetValue(): - return - if self.machineCom != None and self.machineCom.isPrinting(): - return - self.cam.takeNewImage() - self.camPreview.Refresh() - - def OnCameraEraseBackground(self, e): - dc = e.GetDC() - if not dc: - dc = wx.ClientDC(self) - rect = self.GetUpdateRegion().GetBox() - dc.SetClippingRect(rect) - dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID)) - if self.cam.getLastImage() != None: - self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight())) - self.camPage.Fit() - dc.DrawBitmap(self.cam.getLastImage(), 0, 0) - else: - dc.Clear() - - def OnPropertyPageButton(self, e): - self.cam.openPropertyPage(e.GetEventObject().index) - - def UpdateButtonStates(self): - self.connectButton.Enable(self.machineCom == 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.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') - else: - 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.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: - for button in self.cam.buttons: - button.Enable(self.machineCom == None or not self.machineCom.isPrinting()) - - def UpdateProgress(self): - status = "" - if self.gcode == None: - status += "Loading gcode...\n" - else: - status += "Filament: %.2fm %.2fg\n" % (self.gcode.extrusionAmount / 1000, 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(): - self.progress.SetValue(0) - if self.gcodeList != None: - status += 'Line: -/%d\n' % (len(self.gcodeList)) - else: - printTime = self.machineCom.getPrintTime() / 60 - printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate() - status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), self.machineCom.getPrintPos() * 100 / len(self.gcodeList)) - 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: - 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.getTemp() > 0: - status += 'Temp: %d\n' % (self.machineCom.getTemp()) - if self.machineCom.getBedTemp() > 0: - status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp()) - self.bedTemperatureLabel.Show(True) - self.bedTemperatureSelect.Show(True) - self.temperaturePanel.Layout() - status += 'Machine state:%s\n' % (self.machineCom.getStateString()) - - self.statsText.SetLabel(status.strip()) - - def OnConnect(self, e): - if self.machineCom != None: - self.machineCom.close() - self.machineCom = machineCom.MachineCom(callbackObject=self) - self.UpdateButtonStates() - taskbar.setBusy(self, True) - - def OnLoad(self, e): - pass - - def OnPrint(self, e): - if self.machineCom == None or not self.machineCom.isOperational(): - return - if self.gcodeList == 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") - self.machineCom.printGCode(self.gcodeList) - self.UpdateButtonStates() - - def OnCancel(self, e): - self.pauseButton.SetLabel('Pause') - self.machineCom.cancelPrint() - self.machineCom.sendCommand("M84") - self.UpdateButtonStates() - - def OnPause(self, e): - if self.machineCom.isPaused(): - self.machineCom.setPause(False) - else: - self.machineCom.setPause(True) - - def OnMachineLog(self, e): - LogWindow('\n'.join(self.machineCom.getLog())) - - def OnClose(self, e): - global printWindowHandle - printWindowHandle = None - if self.machineCom != None: - self.machineCom.close() - self.Destroy() - - def OnTempChange(self, e): - self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue())) - - def OnBedTempChange(self, e): - self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue())) - - def OnSpeedChange(self, e): - if self.machineCom == None: - return - self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0) - self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0) - self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0) - self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0) - - def AddTermLog(self, line): - self.termLog.AppendText(unicode(line, 'utf-8', 'replace')) - l = len(self.termLog.GetValue()) - self.termLog.SetCaret(wx.Caret(self.termLog, (l, l))) - - def OnTermEnterLine(self, e): - line = self.termInput.GetValue() - if line == '': - return - self.termLog.AppendText('>%s\n' % (line)) - self.machineCom.sendCommand(line) - self.termHistory.append(line) - self.termHistoryIdx = len(self.termHistory) - self.termInput.SetValue('') - - def OnTermKey(self, e): - if len(self.termHistory) > 0: - if e.GetKeyCode() == wx.WXK_UP: - self.termHistoryIdx = 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 - if self.termHistoryIdx >= len(self.termHistory): - self.termHistoryIdx = 0 - self.termInput.SetValue(self.termHistory[self.termHistoryIdx]) - e.Skip() - - def OnPowerWarningChange(self, e): - type = self.powerManagement.get_providing_power_source_type() - if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown(): - self.powerWarningText.Hide() - self.panel.Layout() - self.Layout() - elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown(): - self.powerWarningText.Show() - self.panel.Layout() - self.Layout() - - def LoadGCodeFile(self, filename): - if self.machineCom != None and self.machineCom.isPrinting(): - return - #Send an initial M110 to reset the line counter to zero. - prevLineType = lineType = 'CUSTOM' - gcodeList = ["M110"] - for line in open(filename, 'r'): - if line.startswith(';TYPE:'): - lineType = line[6:].strip() - if ';' in line: - line = line[0:line.find(';')] - line = line.strip() - if len(line) > 0: - if prevLineType != lineType: - gcodeList.append((line, lineType, )) - else: - gcodeList.append(line) - prevLineType = lineType - gcode = gcodeInterpreter.gcode() - gcode.loadList(gcodeList) - #print "Loaded: %s (%d)" % (filename, len(gcodeList)) - self.filename = filename - self.gcode = gcode - self.gcodeList = gcodeList - - wx.CallAfter(self.progress.SetRange, len(gcodeList)) - wx.CallAfter(self.UpdateButtonStates) - wx.CallAfter(self.UpdateProgress) - wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename)) - - def sendLine(self, lineNr): - if lineNr >= len(self.gcodeList): - return False - line = self.gcodeList[lineNr] - try: - if ('M104' in line or 'M109' in line) and 'S' in line: - n = int(re.search('S([0-9]*)', line).group(1)) - wx.CallAfter(self.temperatureSelect.SetValue, n) - if ('M140' in line or 'M190' in line) and 'S' in line: - n = int(re.search('S([0-9]*)', line).group(1)) - wx.CallAfter(self.bedTemperatureSelect.SetValue, n) - except: - print "Unexpected error:", sys.exc_info() - checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line))) - self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum)) - return True - - def mcLog(self, message): - #print message - pass - - 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) - - def mcStateChange(self, state): - if self.machineCom != None: - if state == self.machineCom.STATE_OPERATIONAL and self.cam != None: - self.cam.endTimelaps() - 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) - wx.CallAfter(self.UpdateButtonStates) - wx.CallAfter(self.UpdateProgress) - - def mcMessage(self, message): - wx.CallAfter(self.AddTermLog, message) - - def mcProgress(self, lineNr): - wx.CallAfter(self.UpdateProgress) - - def mcZChange(self, newZ): - self.currentZ = newZ - if self.cam != None: - wx.CallAfter(self.cam.takeNewImage) - wx.CallAfter(self.camPreview.Refresh) - -class temperatureGraph(wx.Panel): - def __init__(self, parent): - super(temperatureGraph, self).__init__(parent) - - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(wx.EVT_PAINT, self.OnDraw) - - self.lastDraw = time.time() - 1.0 - self.points = [] - self.backBuffer = None - self.addPoint(0,0,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(): - self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple()) - self.UpdateDrawing(True) - - def OnDraw(self, e): - dc = wx.BufferedPaintDC(self, self.backBuffer) - - def UpdateDrawing(self, force = False): - now = time.time() - if not force and now - self.lastDraw < 1.0: - return - self.lastDraw = now - dc = wx.MemoryDC() - dc.SelectObject(self.backBuffer) - dc.Clear() - dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)) - w, h = self.GetSizeTuple() - bgLinePen = wx.Pen('#A0A0A0') - tempPen = wx.Pen('#FF4040') - tempSPPen = wx.Pen('#FFA0A0') - tempPenBG = wx.Pen('#FFD0D0') - bedTempPen = wx.Pen('#4040FF') - bedTempSPPen = wx.Pen('#A0A0FF') - bedTempPenBG = wx.Pen('#D0D0FF') - - #Draw the background up to the current temperatures. - x0 = 0 - t0 = 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 - 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 - bt0 = bedTemp - tSP0 = tempSP - btSP0 = bedTempSP - x0 = x1 + 1 - - #Draw the grid - for x in xrange(w, 0, -30): - dc.SetPen(bgLinePen) - dc.DrawLine(x, 0, x, h) - tmpNr = 0 - for y in xrange(h-1, 0, -h * 50 / 300): - dc.SetPen(bgLinePen) - dc.DrawLine(0, y, w, y) - dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight()) - tmpNr += 50 - dc.DrawLine(0, 0, w, 0) - dc.DrawLine(0, 0, 0, h) - - #Draw the main lines - x0 = 0 - t0 = 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 - 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 - bt0 = bedTemp - tSP0 = tempSP - btSP0 = bedTempSP - x0 = x1 + 1 - - del dc - self.Refresh(eraseBackground=False) - self.Update() - - if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20: - self.points.pop(0) - - def addPoint(self, temp, tempSP, bedTemp, bedTempSP): - if bedTemp == None: - bedTemp = 0 - if bedTempSP == None: - bedTempSP = 0 - self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time())) - wx.CallAfter(self.UpdateDrawing) - -class LogWindow(wx.Frame): - def __init__(self, logText): - super(LogWindow, self).__init__(None, title="Machine log") - self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_READONLY) - self.SetSize((500,400)) - self.Centre() - self.Show(True) + def Monitor(self): + p = self.handle + line = p.stdout.readline() + while(len(line) > 0): + #print line.rstrip() + line = p.stdout.readline() + p.communicate() + self.handle = None + self.thread = None + + +class PrintCommandButton(buttons.GenBitmapButton): + def __init__(self, parent, commandList, bitmapFilename, size=(20, 20)): + self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename)) + super(PrintCommandButton, self).__init__(parent.directControlPanel, -1, self.bitmap, size=size) + + self.commandList = commandList + self.parent = parent + + self.SetBezelWidth(1) + self.SetUseFocusIndicator(False) + + self.Bind(wx.EVT_BUTTON, self.OnClick) + + def OnClick(self, e): + if self.parent.machineCom == None or self.parent.machineCom.isPrinting(): + return; + for cmd in self.commandList: + self.parent.machineCom.sendCommand(cmd) + e.Skip() + + +class printWindow(wx.Frame): + "Main user interface window" + + def __init__(self): + super(printWindow, self).__init__(None, -1, title='Printing') + self.machineCom = None + self.gcode = None + self.gcodeList = None + self.sendList = [] + self.temp = None + self.bedTemp = None + self.bufferLineCount = 4 + self.sendCnt = 0 + self.feedrateRatioOuterWall = 1.0 + self.feedrateRatioInnerWall = 1.0 + self.feedrateRatioFill = 1.0 + self.feedrateRatioSupport = 1.0 + self.pause = False + self.termHistory = [] + self.termHistoryIdx = 0 + + self.cam = None + if webcam.hasWebcamSupport(): + self.cam = webcam.webcam() + if not self.cam.hasCamera(): + self.cam = None + + #self.SetIcon(icon.getMainIcon()) + + self.SetSizer(wx.BoxSizer()) + self.panel = wx.Panel(self) + self.GetSizer().Add(self.panel, 1, flag=wx.EXPAND) + self.sizer = wx.GridBagSizer(2, 2) + self.panel.SetSizer(self.sizer) + + 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.", + style=wx.ALIGN_CENTER) + self.powerWarningText.SetBackgroundColour('red') + self.powerWarningText.SetForegroundColour('white') + boxsizer.AddF(self.powerWarningText, flags=wx.SizerFlags().Expand().Border(wx.BOTTOM, 10)) + self.powerManagement = power.PowerManagement() + self.powerWarningTimer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnPowerWarningChange, self.powerWarningTimer) + self.OnPowerWarningChange(None) + self.powerWarningTimer.Start(10000) + + 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.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.progress = wx.Gauge(self.panel, -1) + + self.sizer.Add(self.connectButton, pos=(1, 1), flag=wx.EXPAND) + #self.sizer.Add(self.loadButton, pos=(1,1), flag=wx.EXPAND) + self.sizer.Add(self.printButton, pos=(2, 1), flag=wx.EXPAND) + self.sizer.Add(self.pauseButton, pos=(3, 1), flag=wx.EXPAND) + self.sizer.Add(self.cancelButton, pos=(4, 1), flag=wx.EXPAND) + self.sizer.Add(self.machineLogButton, pos=(5, 1), flag=wx.EXPAND) + self.sizer.Add(self.progress, pos=(7, 0), span=(1, 7), flag=wx.EXPAND) + + nb = wx.Notebook(self.panel) + self.sizer.Add(nb, pos=(0, 2), span=(7, 4), flag=wx.EXPAND) + + self.temperaturePanel = wx.Panel(nb) + sizer = wx.GridBagSizer(2, 2) + self.temperaturePanel.SetSizer(sizer) + + 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.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(self.temperatureSelect, pos=(0, 1)) + 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.AddGrowableRow(2) + sizer.AddGrowableCol(1) + + nb.AddPage(self.temperaturePanel, 'Temp') + + self.directControlPanel = wx.Panel(nb) + + sizer = wx.GridBagSizer(2, 2) + self.directControlPanel.SetSizer(sizer) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y100 F6000', 'G90'], 'print-move-y100.png'), pos=(0, 3)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y10 F6000', 'G90'], 'print-move-y10.png'), pos=(1, 3)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y1 F6000', 'G90'], 'print-move-y1.png'), pos=(2, 3)) + + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-1 F6000', 'G90'], 'print-move-y-1.png'), pos=(4, 3)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-10 F6000', 'G90'], 'print-move-y-10.png'), pos=(5, 3)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Y-100 F6000', 'G90'], 'print-move-y-100.png'), pos=(6, 3)) + + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-100 F6000', 'G90'], 'print-move-x-100.png'), pos=(3, 0)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-10 F6000', 'G90'], 'print-move-x-10.png'), pos=(3, 1)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X-1 F6000', 'G90'], 'print-move-x-1.png'), pos=(3, 2)) + + sizer.Add(PrintCommandButton(self, ['G28 X0 Y0'], 'print-move-home.png'), pos=(3, 3)) + + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X1 F6000', 'G90'], 'print-move-x1.png'), pos=(3, 4)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X10 F6000', 'G90'], 'print-move-x10.png'), pos=(3, 5)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 X100 F6000', 'G90'], 'print-move-x100.png'), pos=(3, 6)) + + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z10 F200', 'G90'], 'print-move-z10.png'), pos=(0, 8)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z1 F200', 'G90'], 'print-move-z1.png'), pos=(1, 8)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z0.1 F200', 'G90'], 'print-move-z0.1.png'), pos=(2, 8)) + + sizer.Add(PrintCommandButton(self, ['G28 Z0'], 'print-move-home.png'), pos=(3, 8)) + + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-0.1 F200', 'G90'], 'print-move-z-0.1.png'), pos=(4, 8)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-1 F200', 'G90'], 'print-move-z-1.png'), pos=(5, 8)) + sizer.Add(PrintCommandButton(self, ['G91', 'G1 Z-10 F200', 'G90'], 'print-move-z-10.png'), pos=(6, 8)) + + sizer.Add(PrintCommandButton(self, ['G92 E0', 'G1 E2 F120'], 'extrude.png', size=(60, 20)), pos=(1, 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') + + self.speedPanel = wx.Panel(nb) + sizer = wx.GridBagSizer(2, 2) + self.speedPanel.SetSizer(sizer) + + self.outerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS) + self.outerWallSpeedSelect.SetRange(5, 1000) + self.innerWallSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS) + self.innerWallSpeedSelect.SetRange(5, 1000) + self.fillSpeedSelect = wx.SpinCtrl(self.speedPanel, -1, '100', size=(21 * 3, 21), style=wx.SP_ARROW_KEYS) + self.fillSpeedSelect.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(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(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(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(self.supportSpeedSelect, pos=(3, 1)) + sizer.Add(wx.StaticText(self.speedPanel, -1, "%"), pos=(3, 2)) + + nb.AddPage(self.speedPanel, 'Speed') + + self.termPanel = wx.Panel(nb) + sizer = wx.GridBagSizer(2, 2) + self.termPanel.SetSizer(sizer) + + f = wx.Font(8, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) + self.termLog = wx.TextCtrl(self.termPanel, style=wx.TE_MULTILINE | wx.TE_DONTWRAP) + self.termLog.SetFont(f) + self.termLog.SetEditable(0) + self.termInput = wx.TextCtrl(self.termPanel, style=wx.TE_PROCESS_ENTER) + self.termInput.SetFont(f) + + sizer.Add(self.termLog, pos=(0, 0), flag=wx.EXPAND) + sizer.Add(self.termInput, pos=(1, 0), flag=wx.EXPAND) + 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) + + self.sizer.AddGrowableRow(6) + self.sizer.AddGrowableCol(3) + + self.Bind(wx.EVT_CLOSE, self.OnClose) + self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect) + #self.loadButton.Bind(wx.EVT_BUTTON, self.OnLoad) + self.printButton.Bind(wx.EVT_BUTTON, self.OnPrint) + self.pauseButton.Bind(wx.EVT_BUTTON, self.OnPause) + self.cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel) + self.machineLogButton.Bind(wx.EVT_BUTTON, self.OnMachineLog) + + self.Bind(wx.EVT_SPINCTRL, self.OnTempChange, self.temperatureSelect) + self.Bind(wx.EVT_SPINCTRL, self.OnBedTempChange, self.bedTemperatureSelect) + + self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.outerWallSpeedSelect) + self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.innerWallSpeedSelect) + self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.fillSpeedSelect) + self.Bind(wx.EVT_SPINCTRL, self.OnSpeedChange, self.supportSpeedSelect) + self.Bind(wx.EVT_TEXT_ENTER, self.OnTermEnterLine, self.termInput) + self.termInput.Bind(wx.EVT_CHAR, self.OnTermKey) + + self.Layout() + self.Fit() + self.Centre() + + self.statsText.SetMinSize(self.statsText.GetSize()) + + self.UpdateButtonStates() + + #self.UpdateProgress() + + def OnCameraTimer(self, e): + if not self.campreviewEnable.GetValue(): + return + if self.machineCom != None and self.machineCom.isPrinting(): + return + self.cam.takeNewImage() + self.camPreview.Refresh() + + def OnCameraEraseBackground(self, e): + dc = e.GetDC() + if not dc: + dc = wx.ClientDC(self) + rect = self.GetUpdateRegion().GetBox() + dc.SetClippingRect(rect) + dc.SetBackground(wx.Brush(self.camPreview.GetBackgroundColour(), wx.SOLID)) + if self.cam.getLastImage() != None: + self.camPreview.SetMinSize((self.cam.getLastImage().GetWidth(), self.cam.getLastImage().GetHeight())) + self.camPage.Fit() + dc.DrawBitmap(self.cam.getLastImage(), 0, 0) + else: + dc.Clear() + + def OnPropertyPageButton(self, e): + self.cam.openPropertyPage(e.GetEventObject().index) + + def UpdateButtonStates(self): + self.connectButton.Enable(self.machineCom == 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.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') + else: + 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.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: + for button in self.cam.buttons: + button.Enable(self.machineCom == None or not self.machineCom.isPrinting()) + + def UpdateProgress(self): + status = "" + if self.gcode == None: + status += "Loading gcode...\n" + else: + status += "Filament: %.2fm %.2fg\n" % ( + self.gcode.extrusionAmount / 1000, 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(): + self.progress.SetValue(0) + if self.gcodeList != None: + status += 'Line: -/%d\n' % (len(self.gcodeList)) + else: + printTime = self.machineCom.getPrintTime() / 60 + printTimeLeft = self.machineCom.getPrintTimeRemainingEstimate() + status += 'Line: %d/%d %d%%\n' % (self.machineCom.getPrintPos(), len(self.gcodeList), + self.machineCom.getPrintPos() * 100 / len(self.gcodeList)) + 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: + 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.getTemp() > 0: + status += 'Temp: %d\n' % (self.machineCom.getTemp()) + if self.machineCom.getBedTemp() > 0: + status += 'Bed Temp: %d\n' % (self.machineCom.getBedTemp()) + self.bedTemperatureLabel.Show(True) + self.bedTemperatureSelect.Show(True) + self.temperaturePanel.Layout() + status += 'Machine state:%s\n' % (self.machineCom.getStateString()) + + self.statsText.SetLabel(status.strip()) + + def OnConnect(self, e): + if self.machineCom != None: + self.machineCom.close() + self.machineCom = machineCom.MachineCom(callbackObject=self) + self.UpdateButtonStates() + taskbar.setBusy(self, True) + + def OnLoad(self, e): + pass + + def OnPrint(self, e): + if self.machineCom == None or not self.machineCom.isOperational(): + return + if self.gcodeList == 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") + self.machineCom.printGCode(self.gcodeList) + self.UpdateButtonStates() + + def OnCancel(self, e): + self.pauseButton.SetLabel('Pause') + self.machineCom.cancelPrint() + self.machineCom.sendCommand("M84") + self.UpdateButtonStates() + + def OnPause(self, e): + if self.machineCom.isPaused(): + self.machineCom.setPause(False) + else: + self.machineCom.setPause(True) + + def OnMachineLog(self, e): + LogWindow('\n'.join(self.machineCom.getLog())) + + def OnClose(self, e): + global printWindowHandle + printWindowHandle = None + if self.machineCom != None: + self.machineCom.close() + self.Destroy() + + def OnTempChange(self, e): + self.machineCom.sendCommand("M104 S%d" % (self.temperatureSelect.GetValue())) + + def OnBedTempChange(self, e): + self.machineCom.sendCommand("M140 S%d" % (self.bedTemperatureSelect.GetValue())) + + def OnSpeedChange(self, e): + if self.machineCom == None: + return + self.machineCom.setFeedrateModifier('WALL-OUTER', self.outerWallSpeedSelect.GetValue() / 100.0) + self.machineCom.setFeedrateModifier('WALL-INNER', self.innerWallSpeedSelect.GetValue() / 100.0) + self.machineCom.setFeedrateModifier('FILL', self.fillSpeedSelect.GetValue() / 100.0) + self.machineCom.setFeedrateModifier('SUPPORT', self.supportSpeedSelect.GetValue() / 100.0) + + def AddTermLog(self, line): + self.termLog.AppendText(unicode(line, 'utf-8', 'replace')) + l = len(self.termLog.GetValue()) + self.termLog.SetCaret(wx.Caret(self.termLog, (l, l))) + + def OnTermEnterLine(self, e): + line = self.termInput.GetValue() + if line == '': + return + self.termLog.AppendText('>%s\n' % (line)) + self.machineCom.sendCommand(line) + self.termHistory.append(line) + self.termHistoryIdx = len(self.termHistory) + self.termInput.SetValue('') + + def OnTermKey(self, e): + if len(self.termHistory) > 0: + if e.GetKeyCode() == wx.WXK_UP: + self.termHistoryIdx = 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 + if self.termHistoryIdx >= len(self.termHistory): + self.termHistoryIdx = 0 + self.termInput.SetValue(self.termHistory[self.termHistoryIdx]) + e.Skip() + + def OnPowerWarningChange(self, e): + type = self.powerManagement.get_providing_power_source_type() + if type == power.POWER_TYPE_AC and self.powerWarningText.IsShown(): + self.powerWarningText.Hide() + self.panel.Layout() + self.Layout() + elif type != power.POWER_TYPE_AC and not self.powerWarningText.IsShown(): + self.powerWarningText.Show() + self.panel.Layout() + self.Layout() + + def LoadGCodeFile(self, filename): + if self.machineCom != None and self.machineCom.isPrinting(): + return + #Send an initial M110 to reset the line counter to zero. + prevLineType = lineType = 'CUSTOM' + gcodeList = ["M110"] + for line in open(filename, 'r'): + if line.startswith(';TYPE:'): + lineType = line[6:].strip() + if ';' in line: + line = line[0:line.find(';')] + line = line.strip() + if len(line) > 0: + if prevLineType != lineType: + gcodeList.append((line, lineType, )) + else: + gcodeList.append(line) + prevLineType = lineType + gcode = gcodeInterpreter.gcode() + gcode.loadList(gcodeList) + #print "Loaded: %s (%d)" % (filename, len(gcodeList)) + self.filename = filename + self.gcode = gcode + self.gcodeList = gcodeList + + wx.CallAfter(self.progress.SetRange, len(gcodeList)) + wx.CallAfter(self.UpdateButtonStates) + wx.CallAfter(self.UpdateProgress) + wx.CallAfter(self.SetTitle, 'Printing: %s' % (filename)) + + def sendLine(self, lineNr): + if lineNr >= len(self.gcodeList): + return False + line = self.gcodeList[lineNr] + try: + if ('M104' in line or 'M109' in line) and 'S' in line: + n = int(re.search('S([0-9]*)', line).group(1)) + wx.CallAfter(self.temperatureSelect.SetValue, n) + if ('M140' in line or 'M190' in line) and 'S' in line: + n = int(re.search('S([0-9]*)', line).group(1)) + wx.CallAfter(self.bedTemperatureSelect.SetValue, n) + except: + print "Unexpected error:", sys.exc_info() + checksum = reduce(lambda x, y: x ^ y, map(ord, "N%d%s" % (lineNr, line))) + self.machineCom.sendCommand("N%d%s*%d" % (lineNr, line, checksum)) + return True + + def mcLog(self, message): + #print message + pass + + 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) + + def mcStateChange(self, state): + if self.machineCom != None: + if state == self.machineCom.STATE_OPERATIONAL and self.cam != None: + self.cam.endTimelaps() + 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) + wx.CallAfter(self.UpdateButtonStates) + wx.CallAfter(self.UpdateProgress) + + def mcMessage(self, message): + wx.CallAfter(self.AddTermLog, message) + + def mcProgress(self, lineNr): + wx.CallAfter(self.UpdateProgress) + + def mcZChange(self, newZ): + self.currentZ = newZ + if self.cam != None: + wx.CallAfter(self.cam.takeNewImage) + wx.CallAfter(self.camPreview.Refresh) + + +class temperatureGraph(wx.Panel): + def __init__(self, parent): + super(temperatureGraph, self).__init__(parent) + + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_PAINT, self.OnDraw) + + self.lastDraw = time.time() - 1.0 + self.points = [] + self.backBuffer = None + self.addPoint(0, 0, 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(): + self.backBuffer = wx.EmptyBitmap(*self.GetSizeTuple()) + self.UpdateDrawing(True) + + def OnDraw(self, e): + dc = wx.BufferedPaintDC(self, self.backBuffer) + + def UpdateDrawing(self, force=False): + now = time.time() + if not force and now - self.lastDraw < 1.0: + return + self.lastDraw = now + dc = wx.MemoryDC() + dc.SelectObject(self.backBuffer) + dc.Clear() + dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)) + w, h = self.GetSizeTuple() + bgLinePen = wx.Pen('#A0A0A0') + tempPen = wx.Pen('#FF4040') + tempSPPen = wx.Pen('#FFA0A0') + tempPenBG = wx.Pen('#FFD0D0') + bedTempPen = wx.Pen('#4040FF') + bedTempSPPen = wx.Pen('#A0A0FF') + bedTempPenBG = wx.Pen('#D0D0FF') + + #Draw the background up to the current temperatures. + x0 = 0 + t0 = 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 + 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 + bt0 = bedTemp + tSP0 = tempSP + btSP0 = bedTempSP + x0 = x1 + 1 + + #Draw the grid + for x in xrange(w, 0, -30): + dc.SetPen(bgLinePen) + dc.DrawLine(x, 0, x, h) + tmpNr = 0 + for y in xrange(h - 1, 0, -h * 50 / 300): + dc.SetPen(bgLinePen) + dc.DrawLine(0, y, w, y) + dc.DrawText(str(tmpNr), 0, y - dc.GetFont().GetPixelSize().GetHeight()) + tmpNr += 50 + dc.DrawLine(0, 0, w, 0) + dc.DrawLine(0, 0, 0, h) + + #Draw the main lines + x0 = 0 + t0 = 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 + 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 + bt0 = bedTemp + tSP0 = tempSP + btSP0 = bedTempSP + x0 = x1 + 1 + + del dc + self.Refresh(eraseBackground=False) + self.Update() + + if len(self.points) > 0 and (time.time() - self.points[0][4]) > w + 20: + self.points.pop(0) + + def addPoint(self, temp, tempSP, bedTemp, bedTempSP): + if bedTemp == None: + bedTemp = 0 + if bedTempSP == None: + bedTempSP = 0 + self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time())) + wx.CallAfter(self.UpdateDrawing) + + +class LogWindow(wx.Frame): + def __init__(self, logText): + super(LogWindow, self).__init__(None, title="Machine log") + self.textBox = wx.TextCtrl(self, -1, logText, style=wx.TE_MULTILINE | wx.TE_DONTWRAP | wx.TE_READONLY) + self.SetSize((500, 400)) + self.Centre() + self.Show(True) diff --git a/Cura/gui/splashScreen.py b/Cura/gui/splashScreen.py index cfa4792..2654980 100644 --- a/Cura/gui/splashScreen.py +++ b/Cura/gui/splashScreen.py @@ -1,39 +1,40 @@ -import sys, os -#We only need the core here, which speeds up the import. As we want to show the splashscreen ASAP. -import wx._core +# coding=utf-8 +from __future__ import absolute_import + +import wx._core #We only need the core here, which speeds up the import. As we want to show the splashscreen ASAP. + +from util.resources import getPathForImage -def getBitmapImage(filename): - #The frozen executable has the script files in a zip, so we need to exit another level to get to our images. - if hasattr(sys, 'frozen'): - return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../images", filename))) - else: - return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", filename))) class splashScreen(wx.SplashScreen): def __init__(self, callback): self.callback = callback - bitmap = getBitmapImage("splash.png") + bitmap = wx.Bitmap(getPathForImage('splash.png')) super(splashScreen, self).__init__(bitmap, wx.SPLASH_CENTRE_ON_SCREEN, 0, None) wx.CallAfter(self.DoCallback) - + def DoCallback(self): self.callback(self) self.Destroy() + def showSplash(callback): - app = wx.App(False) + from Cura.cura import CuraApp + app = CuraApp(False) splashScreen(callback) app.MainLoop() + def testCallback(splashscreen): print "Callback!" import time + time.sleep(2) print "!Callback" + def main(): showSplash(testCallback) if __name__ == u'__main__': main() - diff --git a/Cura/gui/toolbarUtil.py b/Cura/gui/toolbarUtil.py index ad7770a..62f4d6d 100644 --- a/Cura/gui/toolbarUtil.py +++ b/Cura/gui/toolbarUtil.py @@ -1,28 +1,23 @@ +# coding=utf-8 +from __future__ import absolute_import from __future__ import division -import os, sys - import wx from wx.lib import buttons from util import profile +from util.resources import getPathForImage + ####################################################### # toolbarUtil contains help classes and functions for # toolbar buttons. ####################################################### -def getBitmapImage(filename): - #The frozen executable has the script files in a zip, so we need to exit another level to get to our images. - if hasattr(sys, 'frozen'): - return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../../images", filename))) - else: - return wx.Bitmap(os.path.normpath(os.path.join(os.path.split(__file__)[0], "../images", filename))) - class Toolbar(wx.ToolBar): def __init__(self, parent): super(Toolbar, self).__init__(parent, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER) - self.SetToolBitmapSize( ( 21, 21 ) ) + self.SetToolBitmapSize(( 21, 21 )) if not hasattr(parent, 'popup'): # Create popup window @@ -30,14 +25,14 @@ class Toolbar(wx.ToolBar): parent.popup.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK)) parent.popup.text = wx.StaticText(parent.popup, -1, '') parent.popup.sizer = wx.BoxSizer() - parent.popup.sizer.Add(parent.popup.text, flag=wx.EXPAND|wx.ALL, border=1) + parent.popup.sizer.Add(parent.popup.text, flag=wx.EXPAND | wx.ALL, border=1) parent.popup.SetSizer(parent.popup.sizer) parent.popup.owner = None def OnPopupDisplay(self, e): self.UpdatePopup(e.GetEventObject()) self.GetParent().popup.Show(True) - + def OnPopupHide(self, e): if self.GetParent().popup.owner == e.GetEventObject(): self.GetParent().popup.Show(False) @@ -50,13 +45,14 @@ class Toolbar(wx.ToolBar): popup.Fit(); x, y = control.ClientToScreenXY(0, 0) sx, sy = control.GetSizeTuple() - popup.SetPosition((x, y+sy)) + popup.SetPosition((x, y + sy)) + class ToggleButton(buttons.GenBitmapToggleButton): def __init__(self, parent, profileSetting, bitmapFilenameOn, bitmapFilenameOff, - helpText='', id=-1, callback=None, size=(20,20)): - self.bitmapOn = getBitmapImage(bitmapFilenameOn) - self.bitmapOff = getBitmapImage(bitmapFilenameOff) + helpText='', id=-1, callback=None, size=(20, 20)): + self.bitmapOn = wx.Bitmap(getPathForImage(bitmapFilenameOn)) + self.bitmapOff = wx.Bitmap(getPathForImage(bitmapFilenameOff)) super(ToggleButton, self).__init__(parent, id, self.bitmapOff, size=size) @@ -75,7 +71,7 @@ class ToggleButton(buttons.GenBitmapToggleButton): self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) - + parent.AddControl(self) def SetBitmap(self, boolValue): @@ -117,11 +113,12 @@ class ToggleButton(buttons.GenBitmapToggleButton): self.Refresh() event.Skip() + class RadioButton(buttons.GenBitmapButton): def __init__(self, parent, group, bitmapFilenameOn, bitmapFilenameOff, - helpText='', id=-1, callback=None, size=(20,20)): - self.bitmapOn = getBitmapImage(bitmapFilenameOn) - self.bitmapOff = getBitmapImage(bitmapFilenameOff) + helpText='', id=-1, callback=None, size=(20, 20)): + self.bitmapOn = wx.Bitmap(getPathForImage(bitmapFilenameOn)) + self.bitmapOff = wx.Bitmap(getPathForImage(bitmapFilenameOff)) super(RadioButton, self).__init__(parent, id, self.bitmapOff, size=size) @@ -138,10 +135,10 @@ class RadioButton(buttons.GenBitmapButton): self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) - + if len(group) == 1: self.SetValue(True) - + parent.AddControl(self) def SetBitmap(self, boolValue): @@ -158,7 +155,7 @@ class RadioButton(buttons.GenBitmapButton): for other in self.group: if other != self: other.SetValue(False) - + def GetValue(self): return self._value @@ -179,10 +176,11 @@ class RadioButton(buttons.GenBitmapButton): self.Refresh() event.Skip() + class NormalButton(buttons.GenBitmapButton): def __init__(self, parent, callback, bitmapFilename, - helpText='', id=-1, size=(20,20)): - self.bitmap = getBitmapImage(bitmapFilename) + helpText='', id=-1, size=(20, 20)): + self.bitmap = wx.Bitmap(getPathForImage(bitmapFilename)) super(NormalButton, self).__init__(parent, id, self.bitmap, size=size) self.helpText = helpText @@ -193,9 +191,9 @@ class NormalButton(buttons.GenBitmapButton): self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) - + self.Bind(wx.EVT_BUTTON, self.OnButton) - + parent.AddControl(self) def OnButton(self, event): diff --git a/Cura/gui/webcam.py b/Cura/gui/webcam.py index a704372..56d2826 100644 --- a/Cura/gui/webcam.py +++ b/Cura/gui/webcam.py @@ -1,141 +1,156 @@ -import os, glob, subprocess, platform -import wx - -from util import profile -from gui import toolbarUtil - -try: - #Try to find the OpenCV library for video capture. - from opencv import cv - from opencv import highgui -except: - cv = None - -try: - #Use the vidcap library directly from the VideoCapture package. (Windows only) - # http://videocapture.sourceforge.net/ - # We're using the binary interface, not the python interface, so we don't depend on PIL - import vidcap as win32vidcap -except: - win32vidcap = None - -def hasWebcamSupport(): - if cv == None and win32vidcap == None: - return False - if not os.path.exists(getFFMPEGpath()): - return False - return True - -def getFFMPEGpath(): - if platform.system() == "Windows": - return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe")) - elif os.path.exists('/usr/bin/ffmpeg'): - return '/usr/bin/ffmpeg' - return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg")) - -class webcam(object): - def __init__(self): - self._cam = None - self._overlayImage = toolbarUtil.getBitmapImage("cura-overlay.png") - self._overlayUltimaker = toolbarUtil.getBitmapImage("ultimaker-overlay.png") - if cv != None: - self._cam = highgui.cvCreateCameraCapture(-1) - elif win32vidcap != None: - try: - self._cam = win32vidcap.new_Dev(0, False) - except: - pass - - self._doTimelaps = False - self._bitmap = None - - def hasCamera(self): - return self._cam != None - - def propertyPages(self): - if self._cam == None: - return [] - if cv != None: - #TODO Make an OpenCV property page - return [] - elif win32vidcap != None: - return ['Image properties', 'Format properties'] - - def openPropertyPage(self, pageType = 0): - if self._cam == None: - return - if cv != None: - pass - elif win32vidcap != None: - if pageType == 0: - self._cam.displaycapturefilterproperties() - else: - del self._cam - self._cam = None - tmp = win32vidcap.new_Dev(0, False) - tmp.displaycapturepinproperties() - self._cam = tmp - - def takeNewImage(self): - if self._cam == None: - return - if cv != None: - frame = cv.QueryFrame(self._cam) - cv.CvtColor(frame, frame, cv.CV_BGR2RGB) - bitmap = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData) - elif win32vidcap != None: - buffer, width, height = self._cam.getbuffer() - try: - wxImage = wx.EmptyImage(width, height) - wxImage.SetData(buffer[::-1]) - if self._bitmap != None: - del self._bitmap - bitmap = wxImage.ConvertToBitmap() - del wxImage - del buffer - except: - pass - - dc = wx.MemoryDC() - dc.SelectObject(bitmap) - dc.DrawBitmap(self._overlayImage, bitmap.GetWidth() - self._overlayImage.GetWidth() - 5, 5, True) - if profile.getPreference('machine_type') == 'ultimaker': - dc.DrawBitmap(self._overlayUltimaker, (bitmap.GetWidth() - self._overlayUltimaker.GetWidth()) / 2, bitmap.GetHeight() - self._overlayUltimaker.GetHeight() - 5, True) - dc.SelectObject(wx.NullBitmap) - - self._bitmap = bitmap - - if self._doTimelaps: - filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg" % (self._snapshotCount))) - self._snapshotCount += 1 - bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG) - - return self._bitmap - - def getLastImage(self): - return self._bitmap - - def startTimelaps(self, filename): - if self._cam == None: - return - self._cleanTempDir() - self._timelapsFilename = filename - self._snapshotCount = 0 - self._doTimelaps = True - print "startTimelaps" - - def endTimelaps(self): - if self._doTimelaps: - ffmpeg = getFFMPEGpath() - basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg")) - subprocess.call([ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', '1500k', '-f', 'vob', self._timelapsFilename]) - self._doTimelaps = False - - def _cleanTempDir(self): - basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap")) - try: - os.makedirs(basePath) - except: - pass - for filename in glob.iglob(basePath + "/*.jpg"): - os.remove(filename) +# coding=utf-8 +from __future__ import absolute_import + +import os +import glob +import subprocess +import platform + +import wx + +from util import profile +from util.resources import getPathForImage +from gui import toolbarUtil + +try: + #Try to find the OpenCV library for video capture. + from opencv import cv + from opencv import highgui +except: + cv = None + +try: + #Use the vidcap library directly from the VideoCapture package. (Windows only) + # http://videocapture.sourceforge.net/ + # We're using the binary interface, not the python interface, so we don't depend on PIL + import vidcap as win32vidcap +except: + win32vidcap = None + +def hasWebcamSupport(): + if cv == None and win32vidcap == None: + return False + if not os.path.exists(getFFMPEGpath()): + return False + return True + + +def getFFMPEGpath(): + if platform.system() == "Windows": + return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe")) + elif os.path.exists('/usr/bin/ffmpeg'): + return '/usr/bin/ffmpeg' + return os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg")) + + +class webcam(object): + def __init__(self): + self._cam = None + self._overlayImage = wx.Bitmap(getPathForImage('cura-overlay.png')) + self._overlayUltimaker = wx.Bitmap(getPathForImage('ultimaker-overlay.png')) + if cv != None: + self._cam = highgui.cvCreateCameraCapture(-1) + elif win32vidcap != None: + try: + self._cam = win32vidcap.new_Dev(0, False) + except: + pass + + self._doTimelaps = False + self._bitmap = None + + def hasCamera(self): + return self._cam != None + + def propertyPages(self): + if self._cam == None: + return [] + if cv != None: + #TODO Make an OpenCV property page + return [] + elif win32vidcap != None: + return ['Image properties', 'Format properties'] + + def openPropertyPage(self, pageType=0): + if self._cam == None: + return + if cv != None: + pass + elif win32vidcap != None: + if pageType == 0: + self._cam.displaycapturefilterproperties() + else: + del self._cam + self._cam = None + tmp = win32vidcap.new_Dev(0, False) + tmp.displaycapturepinproperties() + self._cam = tmp + + def takeNewImage(self): + if self._cam == None: + return + if cv != None: + frame = cv.QueryFrame(self._cam) + cv.CvtColor(frame, frame, cv.CV_BGR2RGB) + bitmap = wx.BitmapFromBuffer(frame.width, frame.height, frame.imageData) + elif win32vidcap != None: + buffer, width, height = self._cam.getbuffer() + try: + wxImage = wx.EmptyImage(width, height) + wxImage.SetData(buffer[::-1]) + if self._bitmap != None: + del self._bitmap + bitmap = wxImage.ConvertToBitmap() + del wxImage + del buffer + except: + pass + + dc = wx.MemoryDC() + dc.SelectObject(bitmap) + dc.DrawBitmap(self._overlayImage, bitmap.GetWidth() - self._overlayImage.GetWidth() - 5, 5, True) + if profile.getPreference('machine_type') == 'ultimaker': + dc.DrawBitmap(self._overlayUltimaker, (bitmap.GetWidth() - self._overlayUltimaker.GetWidth()) / 2, + bitmap.GetHeight() - self._overlayUltimaker.GetHeight() - 5, True) + dc.SelectObject(wx.NullBitmap) + + self._bitmap = bitmap + + if self._doTimelaps: + filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", + "__tmp_snap_%04d.jpg" % (self._snapshotCount))) + self._snapshotCount += 1 + bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG) + + return self._bitmap + + def getLastImage(self): + return self._bitmap + + def startTimelaps(self, filename): + if self._cam == None: + return + self._cleanTempDir() + self._timelapsFilename = filename + self._snapshotCount = 0 + self._doTimelaps = True + print "startTimelaps" + + def endTimelaps(self): + if self._doTimelaps: + ffmpeg = getFFMPEGpath() + basePath = os.path.normpath( + os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg")) + subprocess.call( + [ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', + '-b:v', '1500k', '-f', 'vob', self._timelapsFilename]) + self._doTimelaps = False + + def _cleanTempDir(self): + basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap")) + try: + os.makedirs(basePath) + except: + pass + for filename in glob.iglob(basePath + "/*.jpg"): + os.remove(filename) diff --git a/Cura/setup.py b/Cura/setup.py deleted file mode 100644 index e077f67..0000000 --- a/Cura/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -import sys, os, zipfile -try: - import cx_Freeze -except: - print "ERROR: You need cx-Freeze installed to build this package" - sys.exit(1) - -freezeVersion = map(int, cx_Freeze.version.split('.')) -if freezeVersion[0] < 4 or freezeVersion[0] == 4 and freezeVersion[1] < 2: - print "ERROR: Your cx-Freeze version is too old to use with Cura." - sys.exit(1) - -sys.path.append(os.path.abspath('cura_sf')) - -# Dependencies are automatically detected, but it might need fine tuning. -build_exe_options = { -"silent": True, -"packages": [ - 'encodings.utf_8', - "OpenGL", "OpenGL.arrays", "OpenGL.platform", "OpenGL.GLU", -], "excludes": [ - 'Tkinter', 'tcl', 'cura_sf', 'fabmetheus_utilities', 'skeinforge_application', 'numpy', -], "include_files": [ - ('images', 'images'), -], "build_exe": 'freeze_build'} - -# GUI applications require a different base on Windows (the default is for a -# console application). -base = None -if sys.platform == "win32": - base = "Win32GUI" - -cx_Freeze.setup( name = "Cura", - version = "RC5", - description = "Cura", - options = {"build_exe": build_exe_options}, - executables = [cx_Freeze.Executable("cura.py", base=base)]) - -m = cx_Freeze.ModuleFinder(excludes=["gui"]) -m.IncludeFile(os.path.abspath("cura.py")) -m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py")) -m.IncludeFile(os.path.abspath("cura_sf/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py")) -m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py")) -for name in os.listdir("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins"): - if name.endswith('.py'): - m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/" + name)) -m.ReportMissingModules() -cwd = os.path.abspath(".") - -z = zipfile.ZipFile("freeze_build/cura_sf.zip", "w", zipfile.ZIP_DEFLATED) -for mod in m.modules: - if mod.file != None and mod.file.startswith(cwd): - if mod.file[len(cwd)+1:] == "cura.py": - z.write(mod.file[len(cwd)+1:], "__main__.py") - else: - z.write(mod.file[len(cwd)+1:]) -z.write('cura_sf/fabmetheus_utilities/templates/layer_template.svg') -z.write('cura_sf/fabmetheus_utilities/version.txt') -z.write('__init__.py') -z.close() - diff --git a/Cura/util/resources.py b/Cura/util/resources.py new file mode 100644 index 0000000..0db52c1 --- /dev/null +++ b/Cura/util/resources.py @@ -0,0 +1,36 @@ +# coding=utf-8 +from __future__ import absolute_import +import os +import sys + +__all__ = ['getPathForResource', 'getPathForImage', 'getPathForMesh'] + + +if sys.platform.startswith('darwin'): + if hasattr(sys, 'frozen'): + from Foundation import * + imagesPath = os.path.join(NSBundle.mainBundle().resourcePath(), 'images') + meshesPath = os.path.join(NSBundle.mainBundle().resourcePath(), 'images') + else: + imagesPath = os.path.join(os.path.dirname(__file__), "../images") + meshesPath = os.path.join(os.path.dirname(__file__), "../images") +else: + if hasattr(sys, 'frozen'): + imagesPath = os.path.join(os.path.dirname(__file__), "../../images") + meshesPath = os.path.join(os.path.dirname(__file__), "../../images") + else: + imagesPath = os.path.join(os.path.dirname(__file__), "../images") + meshesPath = os.path.join(os.path.dirname(__file__), "../images") + + +def getPathForResource(dir, resource_name): + assert os.path.isdir(dir), "{p} is not a directory".format(p=dir) + path = os.path.normpath(os.path.join(dir, resource_name)) + assert os.path.isfile(path), "{p} is not a file.".format(p=path) + return path + +def getPathForImage(name): + return getPathForResource(imagesPath, name) + +def getPathForMesh(name): + return getPathForResource(meshesPath, name) diff --git a/README.md b/README.md index 4bae532..f369799 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,77 @@ Development Cura is developed in Python. Getting Cura up and running for development is not very difficult. If you copy the python and pypy from a release into your Cura development checkout then you can use Cura right away, just like you would with a release. For development with git, check the help on github. Pull requests is the fastest way to get changes into Cura. + Packaging --------- Cura development comes with a script "package.sh", this script has been designed to run under unix like OSes (Linux, MacOS). Running it from sygwin is not a priority. The "package.sh" script generates a final release package. You should not need it during development, unless you are changing the release process. If you want to distribute your own version of Cura, then the package.sh script will allow you to do that. + +Mac OS X +-------- +The following section describes how to prepare environment for developing and packaing for Mac OS X. + +###Python +You'll need non-system, framework-based, universal with min deployment target set to 10.6 build of Python 2.7 + +**non-system**: it was not bundeled with distribution of Mac OS X. You can check this by `python -c "import sys; print sys.prefix"`. Output should *not* start with *"/System/Library/Frameworks/Python.framework/"* + +**framework-based**: Output of `python -c "import distutils.sysconfig as c; print(c.get_config_var('PYTHONFRAMEWORK'))"` should be non-empty string + +**universal**: output of ``lipo -info `which python` `` include both i386 and x86_64. E.g *"Architectures in the fat file: /usr/local/bin/python are: i386 x86_64"* + +**deployment target set to 10.6**: Output of ``otool -l `which python` `` should contain *"cmd LC_VERSION_MIN_MACOSX ... version 10.6"* + +The easiest way to install it is via [Homebrew](http://mxcl.github.com/homebrew/): `brew install --fresh osx_python_cura.rb --universal` (TODO: upload the formula). Note you'll need to uninstall Python if you already have it installed via Homebrew. + +###Virtualenv +You may skip this step if you don't bother to use [virtualenv](http://pypi.python.org/pypi/virtualenv). It's not a requirement. + +The main problem with virtualenv is that wxWidgets cannot be installed via pip. We'll have to build it manually from source by specifing prefix to our virtualenv. + +Assuming you have virtualenv at *~/.virtualenvs/Cura*: + +1. Download [wxPython sources](http://sourceforge.net/projects/wxpython/files/wxPython/2.9.4.0/wxPython-src-2.9.4.0.tar.bz2) +2. Configure project with the following flags: `./configure --prefix=$HOME/.virtualenvs/Cura/ --enable-optimise --with-libjpeg=builtin --with-libpng=builtin --with-libtiff=builtin --with-zlib=builtin --enable-monolithic --with-macosx-version-min=10.6 --disable-debug --enable-unicode --enable-std_string --enable-display --with-opengl --with-osx_cocoa --enable-dnd --enable-clipboard --enable-webkit --enable-svg --with-expat --enable-universal_binary=i386,x86_64` +3. `make install` +4. cd into the *wxPython* directory +5. Build wxPython modules: `python setup.py build_ext WXPORT=osx_cocoa WX_CONFIG=$HOME/.virtualenvs/Cura/bin/wx-config UNICODE=1 INSTALL_MULTIVERSION=0 BUILD_GLCANVAS=1 BUILD_GIZMOS=1 BUILD_STC=1` (Note that python is the python of your virtualenv) +6. Install wxPython and modules: `python setup.py install --prefix=$HOME/.virtualenvs/Cura/ WXPORT=osx_cocoa WX_CONFIG=$HOME/.virtualenvs/Cura/bin/wx-config UNICODE=1 INSTALL_MULTIVERSION=0 BUILD_GLCANVAS=1 BUILD_GIZMOS=1 BUILD_STC=1` (Note that python is the python of your virtualenv) + +Another problem is that python in virtualenv is not suitable for running GUI code. Mac OS X requires python to be inside the bundle. To workaround this issue, we will add the following script to the ~/.virtualenvs/Cura/bin: + + #!/bin/bash + ENV=`python -c "import sys; print sys.prefix"` + PYTHON=`python -c "import sys; print sys.real_prefix"`/bin/python + export PYTHONHOME=$ENV + exec $PYTHON "$@" + +I typically name this script `pythonw`. + +At this point virtualenv is configured for wxPython development. Remember to use `python` to pacakge the app and `pythonw` to run app without packaging (e.g. for debugging). + +###Requirements +Following packages are required for development: + + PyOpenGL>=3.0.2 + numpy>=1.6.2 + pyserial>=2.6 + pyobjc>=2.5 + +Following packages are required for packaging Cura into app: + + py2app>=0.7.2 + +The easiest way to install all this packages is to use virtualenv's pip: `pip install requirements_darwin.txt` + +####PyObjC +At time of writing, pyobjc 2.5 is not available at pypi. You have to clone repo and install it manually: + + hg clone https://bitbucket.org/ronaldoussoren/pyobjc + hg checkout c42c98d6e941 # last tested commit + python install.py + +###Packaging +To package Cura into application bundle simply do `python setup.py py2app`. Resulting bundle is self-contained -- it includes Python and all needed packages. diff --git a/package.sh b/package.sh index b3ec909..157d3ab 100755 --- a/package.sh +++ b/package.sh @@ -1,17 +1,17 @@ #!/bin/bash -# This script is to package the Cura package for Windows/Linux and OSx -# This script should run under Linux and OSx, as well as Windows with Cygwin. +# This script is to package the Cura package for Windows/Linux and Mac OS X +# This script should run under Linux and Mac OS X, as well as Windows with Cygwin. ############################# # CONFIGURATION ############################# ##Select the build target -BUILD_TARGET=${1:-all} +# BUILD_TARGET=${1:-all} #BUILD_TARGET=win32 #BUILD_TARGET=linux -#BUILD_TARGET=osx64 +BUILD_TARGET=darwin ##Do we need to create the final archive ARCHIVE_FOR_DISTRIBUTION=1 @@ -62,10 +62,50 @@ function extract if [ "$BUILD_TARGET" = "all" ]; then $0 win32 $0 linux - $0 osx64 + $0 darwin exit fi +############################# +# Darwin +############################# + +if [ "$BUILD_TARGET" = "darwin" ]; then + rm -rf scripts/darwin/build + rm -rf scripts/darwin/dist + + python setup.py py2app + rc=$? + if [[ $rc != 0 ]]; then + echo "Cannot build app." + exit 1 + fi + + cd scripts/darwin + + # Install QuickLook plugin + mkdir -p dist/Cura.app/Contents/Library/QuickLook + cp STLQuickLook.qlgenerator dist/Cura.app/Contents/Library/QuickLook/ + + # Archive app + $TAR cfp - dist/Cura.app | gzip --best -c > ../../${TARGET_DIR}.tar.gz + + # Create sparse image for distribution + hdiutil detach /Volumes/Cura\ -\ Ultimaker/ + rm -rf Cura.dmg.sparseimage + hdiutil convert DmgTemplateCompressed.dmg -format UDSP -o Cura.dmg + hdiutil resize -size 500m Cura.dmg.sparseimage + hdiutil attach Cura.dmg.sparseimage + cp -a dist/Cura.app /Volumes/Cura\ -\ Ultimaker/Cura/ + hdiutil detach /Volumes/Cura\ -\ Ultimaker + hdiutil convert Cura.dmg.sparseimage -format UDZO -imagekey zlib-level=9 -ov -o ../../${TARGET_DIR}.dmg + exit +fi + +############################# +# Rest +############################# + # Change working directory to the directory the script is in # http://stackoverflow.com/a/246128 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" @@ -100,13 +140,6 @@ if [ $BUILD_TARGET = "win32" ]; then downloadURL http://www.uwe-sieber.de/files/ejectmedia.zip #Get pypy downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-win32.zip -elif [ $BUILD_TARGET = "osx64" ]; then - downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-${BUILD_TARGET}.tar.bz2 - downloadURL http://python.org/ftp/python/2.7.3/python-2.7.3-macosx10.6.dmg - downloadURL http://sourceforge.net/projects/numpy/files/NumPy/1.6.2/numpy-1.6.2-py2.7-python.org-macosx10.3.dmg - downloadURL http://pypi.python.org/packages/source/p/pyserial/pyserial-2.6.tar.gz - downloadURL http://pypi.python.org/packages/source/P/PyOpenGL/PyOpenGL-3.0.2.tar.gz - downloadURL http://downloads.sourceforge.net/wxpython/wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg else downloadURL https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-${BUILD_TARGET}.tar.bz2 fi @@ -131,7 +164,7 @@ if [ $BUILD_TARGET = "win32" ]; then extract ffmpeg-20120927-git-13f0cd6-win32-static.7z ffmpeg-20120927-git-13f0cd6-win32-static/licenses extract comtypes-0.6.2.win32.exe extract ejectmedia.zip Win32 - + mkdir -p ${TARGET_DIR}/python mkdir -p ${TARGET_DIR}/Cura/ mv \$_OUTDIR/App/* ${TARGET_DIR}/python @@ -150,7 +183,7 @@ if [ $BUILD_TARGET = "win32" ]; then rm -rf VideoCapture-0.9-5 rm -rf numpy-1.6.2-sse2.exe rm -rf ffmpeg-20120927-git-13f0cd6-win32-static - + #Clean up portable python a bit, to keep the package size down. rm -rf ${TARGET_DIR}/python/PyScripter.* rm -rf ${TARGET_DIR}/python/Doc @@ -194,12 +227,12 @@ if (( ${ARCHIVE_FOR_DISTRIBUTION} )); then #cd ${TARGET_DIR} #7z a ../${TARGET_DIR}.zip * #cd .. - + if [ ! -z `which wine` ]; then #if we have wine, try to run our nsis script. rm -rf scripts/win32/dist ln -sf `pwd`/${TARGET_DIR} scripts/win32/dist - wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe /DVERSION=${BUILD_NAME} scripts/win32/installer.nsi + wine ~/.wine/drive_c/Program\ Files/NSIS/makensis.exe /DVERSION=${BUILD_NAME} scripts/win32/installer.nsi mv scripts/win32/Cura_${BUILD_NAME}.exe ./ fi if [ -f '/c/Program Files (x86)/NSIS/makensis.exe' ]; then @@ -208,28 +241,6 @@ if (( ${ARCHIVE_FOR_DISTRIBUTION} )); then '/c/Program Files (x86)/NSIS/makensis.exe' -DVERSION=${BUILD_NAME} 'scripts/win32/installer.nsi' >> log.txt mv scripts/win32/Cura_${BUILD_NAME}.exe ./ fi - elif [ $BUILD_TARGET = "osx64" ]; then - echo "Building osx app" - mkdir -p scripts/osx64/Cura.app/Contents/Resources - mkdir -p scripts/osx64/Cura.app/Contents/Pkgs - rm -rf scripts/osx64/Cura.app/Contents/Resources/Cura - rm -rf scripts/osx64/Cura.app/Contents/Resources/pypy - cp -a ${TARGET_DIR}/* scripts/osx64/Cura.app/Contents/Resources - cp python-2.7.3-macosx10.6.dmg scripts/osx64/Cura.app/Contents/Pkgs - cp numpy-1.6.2-py2.7-python.org-macosx10.3.dmg scripts/osx64/Cura.app/Contents/Pkgs - cp pyserial-2.6.tar.gz scripts/osx64/Cura.app/Contents/Pkgs - cp PyOpenGL-3.0.2.tar.gz scripts/osx64/Cura.app/Contents/Pkgs - cp wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg scripts/osx64/Cura.app/Contents/Pkgs - cd scripts/osx64 - $TAR cfp - Cura.app | gzip --best -c > ../../${TARGET_DIR}.tar.gz - hdiutil detach /Volumes/Cura\ -\ Ultimaker/ - rm -rf Cura.dmg.sparseimage - hdiutil convert DmgTemplateCompressed.dmg -format UDSP -o Cura.dmg - hdiutil resize -size 500m Cura.dmg.sparseimage - hdiutil attach Cura.dmg.sparseimage - cp -a Cura.app /Volumes/Cura\ -\ Ultimaker/Cura/ - hdiutil detach /Volumes/Cura\ -\ Ultimaker - hdiutil convert Cura.dmg.sparseimage -format UDZO -imagekey zlib-level=9 -ov -o ../../${TARGET_DIR}.dmg else echo "Archiving to ${TARGET_DIR}.tar.gz" $TAR cfp - ${TARGET_DIR} | gzip --best -c > ${TARGET_DIR}.tar.gz diff --git a/requirements_darwin.txt b/requirements_darwin.txt new file mode 100644 index 0000000..9469160 --- /dev/null +++ b/requirements_darwin.txt @@ -0,0 +1,4 @@ +PyOpenGL>=3.0.2 +numpy>=1.6.2 +pyserial>=2.6 +py2app>=0.7.2 diff --git a/scripts/osx64/DmgTemplateCompressed.dmg b/scripts/darwin/DmgTemplateCompressed.dmg similarity index 100% rename from scripts/osx64/DmgTemplateCompressed.dmg rename to scripts/darwin/DmgTemplateCompressed.dmg diff --git a/scripts/darwin/STLQuickLook.qlgenerator/Contents/Info.plist b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Info.plist new file mode 100644 index 0000000..44f52f4 --- /dev/null +++ b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Info.plist @@ -0,0 +1,75 @@ + + + + + BuildMachineOSBuild + 12C60 + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + + CFBundleTypeRole + QLGenerator + LSItemContentTypes + + com.pleasantsoftware.uti.stl + + + + CFBundleExecutable + STLQuickLook + CFBundleIdentifier + com.pleasantsoftware.qlgenerator.STLQuickLook + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + STLQuickLook + CFBundleShortVersionString + 1.1 + CFBundleVersion + 2 + CFPlugInDynamicRegisterFunction + + CFPlugInDynamicRegistration + YES + CFPlugInFactories + + C02E94A7-9A81-43CB-9663-4C4F30F4D259 + QuickLookGeneratorPluginFactory + + CFPlugInTypes + + 5E2D9680-5022-40FA-B806-43349622E5B9 + + C02E94A7-9A81-43CB-9663-4C4F30F4D259 + + + CFPlugInUnloadFunction + + DTCompiler + + DTPlatformBuild + 4G2008a + DTPlatformVersion + GM + DTSDKBuild + 12C37 + DTSDKName + macosx10.8 + DTXcode + 0452 + DTXcodeBuild + 4G2008a + QLNeedsToBeRunInMainThread + + QLPreviewHeight + 600 + QLPreviewWidth + 800 + QLSupportsConcurrentRequests + + QLThumbnailMinimumSize + 48 + + diff --git a/scripts/darwin/STLQuickLook.qlgenerator/Contents/MacOS/STLQuickLook b/scripts/darwin/STLQuickLook.qlgenerator/Contents/MacOS/STLQuickLook new file mode 100755 index 0000000..f004690 Binary files /dev/null and b/scripts/darwin/STLQuickLook.qlgenerator/Contents/MacOS/STLQuickLook differ diff --git a/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/English.lproj/InfoPlist.strings b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..1a053bd Binary files /dev/null and b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/English.lproj/InfoPlist.strings differ diff --git a/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/stlTagImage.png b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/stlTagImage.png new file mode 100755 index 0000000..e0c61a2 Binary files /dev/null and b/scripts/darwin/STLQuickLook.qlgenerator/Contents/Resources/stlTagImage.png differ diff --git a/scripts/osx64/Cura.app/Contents/Info.plist b/scripts/osx64/Cura.app/Contents/Info.plist deleted file mode 100644 index 856d01b..0000000 --- a/scripts/osx64/Cura.app/Contents/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleName - Cura - CFBundleDisplayName - Cura 12.10 - CFBundleIdentifer - daid.cura - CFBundleIconFile> - Resources/Cura.icns - CFBundleVersion - 12.10 - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleExecutable - Cura - - diff --git a/scripts/osx64/Cura.app/Contents/MacOS/Cura b/scripts/osx64/Cura.app/Contents/MacOS/Cura deleted file mode 100755 index 58d6f75..0000000 --- a/scripts/osx64/Cura.app/Contents/MacOS/Cura +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/sh - -SCRIPTDIR=`dirname "$0"` -RESDIR=${SCRIPTDIR}/../Resources/ -PKGDIR=${SCRIPTDIR}/../Pkgs/ - -#run the path_helper to set the $PATH for accessing python -if [ -x /usr/libexec/path_helper ]; then - eval `/usr/libexec/path_helper -s` -fi - -displayMessage() -{ - /usr/bin/osascript > /dev/null << EOF -tell application "System Events" - activate - set question to display dialog "$@" -end tell -return button returned of question -EOF - if [ $? != 0 ]; then - /usr/bin/osascript > /dev/null << EOF -tell application "System Events" - activate - display dialog "User Cancelled Install" buttons {"Ok"} -end tell -EOF - exit 1 - fi -} - -#Testing for python2.7, which we need and is not always installed on MacOS 1.6 -PY="python2.7" -$PY -c '' -if [ $? != 0 ]; then - displayMessage "Python 2.7 is missing from your system. Cura requires Python2.7.\nStarting the installer" $PATH - # Install python2.7 - hdiutil attach $PKGDIR/python-2.7.3-macosx10.6.dmg - open -W /Volumes/Python\ 2.7.3/Python.mpkg - hdiutil detach /Volumes/Python\ 2.7.3 - # Check the installation - $PY -c '' - if [ $? != 0 ]; then - displayMessage "Failed to install python2.7" - exit 1 - fi -fi - -#Next check for numpy, numpy does not always run under 64bit, so we need to check if we need to use "arch -i386" -$PY -c 'import numpy' 2> /dev/null -if [ $? != 0 ]; then - PY="arch -i386 python2.7" - $PY -c 'import numpy' - if [ $? != 0 ]; then - displayMessage "Numpy is missing from your system, this is required.\nStarting the installer" - # Install numpy - hdiutil attach $PKGDIR/numpy-1.6.2-py2.7-python.org-macosx10.3.dmg - open -W /Volumes/numpy/numpy-1.6.2-py2.7.mpkg - hdiutil detach /Volumes/numpy - #After installing numpy, we need to check if we need to use arch -386 again - PY="python2.7" - $PY -c 'import numpy' - if [ $? != 0 ]; then - PY="arch -i386 python2.7" - $PY -c 'import numpy' - if [ $? != 0 ]; then - displayMessage "Failed to install numpy." - exit 1 - fi - fi - fi -fi - -#Check for wxPython -$PY -c 'import wx' -if [ $? != 0 ]; then - displayMessage "wxPython is missing from your system. Cura requires wxPython.\nStarting the installer" - # Start wxPython installer - hdiutil attach $PKGDIR/wxPython2.9-osx-2.9.4.0-cocoa-py2.7.dmg - open -W /Volumes/wxPython2.9-osx-2.9.4.0-cocoa-py2.7/wxPython2.9-osx-cocoa-py2.7.pkg - hdiutil detach /Volumes/wxPython2.9-osx-2.9.4.0-cocoa-py2.7 - #Check if wxPython is installed correctly - $PY -c 'import wx' - if [ $? != 0 ]; then - displayMessage "Failed to properly install wxPython." - exit 1 - fi -fi - -#Check for PyOpenGL -$PY -c 'import OpenGL' -if [ $? != 0 ]; then - # Unpackage PyOpenGL - if [ ! -d "$PKGDIR/PyOpenGL-3.0.2/build/lib" ]; then - cd $PKGDIR - tar -xzf PyOpenGL-3.0.2.tar.gz - cd PyOpenGL-3.0.2 - $PY setup.py build - fi - export PYTHONPATH="$PYTHONPATH:$PKGDIR/PyOpenGL-3.0.2/build/lib" - # Test if the installation was succesful - echo $PYTHONPATH - $PY -c 'import OpenGL' - if [ $? != 0 ]; then - displayMessage "Failed to properly use PyOpenGL." - exit 1 - fi -fi - -#Check for pyserial -$PY -c 'import serial' -if [ $? != 0 ]; then - #Unpackage PySerial - if [ ! -d "$PKGDIR/pyserial-2.6/build/lib" ]; then - cd $PKGDIR - tar -xzf pyserial-2.6.tar.gz - cd pyserial-2.6 - $PY setup.py build - fi - export PYTHONPATH="$PYTHONPATH:$PKGDIR/pyserial-2.6/build/lib" - - #Test if we have pyserial now - $PY -c 'import serial' - if [ $? != 0 ]; then - displayMessage "Failed to properly use PySerial." - exit 1 - fi -fi - -#All checks passed, start Cura -$PY "${RESDIR}Cura/cura.py" & -sleep 1 - -exit 0 diff --git a/scripts/osx64/Cura.app/Contents/PkgInfo b/scripts/osx64/Cura.app/Contents/PkgInfo deleted file mode 100644 index bd04210..0000000 --- a/scripts/osx64/Cura.app/Contents/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPL???? \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4a48301 --- /dev/null +++ b/setup.py @@ -0,0 +1,130 @@ +# coding=utf-8 +import sys +import os + +if sys.platform.startswith('darwin'): + from setuptools import setup + + APP = ['Cura/cura.py'] + DATA_FILES = ['Cura/images', 'Cura/LICENSE', 'Cura/stl.ico'] + PLIST = { + u'CFBundleName': u'Cura', + u'CFBundleShortVersionString': u'12.11', + u'CFBundleVersion': u'12.11', + u'CFBundleIdentifier': u'com.ultimaker.Cura', + u'LSMinimumSystemVersion': u'10.6', + u'LSApplicationCategoryType': u'public.app-category.graphics-design', + u'CFBundleDocumentTypes': [ + { + u'CFBundleTypeRole': u'Viewer', + u'LSItemContentTypes': [u'com.pleasantsoftware.uti.stl'], + u'LSHandlerRank': u'Alternate', + }, + { + u'CFBundleTypeRole': u'Viewer', + u'LSItemContentTypes': [u'org.khronos.collada.digital-asset-exchange'], + u'LSHandlerRank': u'Alternate' + }, + { + u'CFBundleTypeName': u'Wavefront 3D Object', + u'CFBundleTypeExtensions': [u'obj'], + u'CFBundleTypeMIMETypes': [u'application/obj-3d'], + u'CFBundleTypeRole': u'Viewer', + u'LSHandlerRank': u'Alternate' + } + ], + u'UTImportedTypeDeclarations': [ + { + u'UTTypeIdentifier': u'com.pleasantsoftware.uti.stl', + u'UTTypeConformsTo': [u'public.data'], + u'UTTypeDescription': u'Stereo Lithography 3D object', + u'UTTypeReferenceURL': u'http://en.wikipedia.org/wiki/STL_(file_format)', + u'UTTypeTagSpecification': {u'public.filename-extension': [u'stl'], u'public.mime-type': [u'text/plain']} + }, + { + u'UTTypeIdentifier': u'org.khronos.collada.digital-asset-exchange', + u'UTTypeConformsTo': [u'public.xml', u'public.audiovisual-content'], + u'UTTypeDescription': u'Digital Asset Exchange (DAE)', + u'UTTypeTagSpecification': {u'public.filename-extension': [u'dae'], u'public.mime-type': [u'model/vnd.collada+xml']} + } + ] + } + OPTIONS = { + 'argv_emulation': True, + 'iconfile': 'Cura/Cura.icns', + 'includes': ['objc', 'Foundation'], + 'resources': DATA_FILES, + 'optimize': '2', + 'plist': PLIST, + 'bdist_base': 'scripts/darwin/build', + 'dist_dir': 'scripts/darwin/dist' + } + + setup( + name="Cura", + app=APP, + data_files=DATA_FILES, + options={'py2app': OPTIONS}, + setup_requires=['py2app'] + ) +else: + import zipfile + try: + import cx_Freeze + except: + print "ERROR: You need cx-Freeze installed to build this package" + sys.exit(1) + + freezeVersion = map(int, cx_Freeze.version.split('.')) + if freezeVersion[0] < 4 or freezeVersion[0] == 4 and freezeVersion[1] < 2: + print "ERROR: Your cx-Freeze version is too old to use with Cura." + sys.exit(1) + + sys.path.append(os.path.abspath('cura_sf')) + + # Dependencies are automatically detected, but it might need fine tuning. + build_exe_options = { + "silent": True, + "packages": [ + 'encodings.utf_8', + "OpenGL", "OpenGL.arrays", "OpenGL.platform", "OpenGL.GLU", + ], "excludes": [ + 'Tkinter', 'tcl', 'cura_sf', 'fabmetheus_utilities', 'skeinforge_application', 'numpy', + ], "include_files": [ + ('images', 'images'), + ], "build_exe": 'freeze_build'} + + # GUI applications require a different base on Windows (the default is for a + # console application). + base = None + if sys.platform == "win32": + base = "Win32GUI" + + cx_Freeze.setup( name = "Cura", + version = "RC5", + description = "Cura", + options = {"build_exe": build_exe_options}, + executables = [cx_Freeze.Executable("cura.py", base=base)]) + + m = cx_Freeze.ModuleFinder(excludes=["gui"]) + m.IncludeFile(os.path.abspath("cura.py")) + m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py")) + m.IncludeFile(os.path.abspath("cura_sf/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py")) + m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py")) + for name in os.listdir("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins"): + if name.endswith('.py'): + m.IncludeFile(os.path.abspath("cura_sf/skeinforge_application/skeinforge_plugins/craft_plugins/" + name)) + m.ReportMissingModules() + cwd = os.path.abspath(".") + + z = zipfile.ZipFile("freeze_build/cura_sf.zip", "w", zipfile.ZIP_DEFLATED) + for mod in m.modules: + if mod.file != None and mod.file.startswith(cwd): + if mod.file[len(cwd)+1:] == "cura.py": + z.write(mod.file[len(cwd)+1:], "__main__.py") + else: + z.write(mod.file[len(cwd)+1:]) + z.write('cura_sf/fabmetheus_utilities/templates/layer_template.svg') + z.write('cura_sf/fabmetheus_utilities/version.txt') + z.write('__init__.py') + z.close()