diff --git a/Cura/alterations/end.gcode b/Cura/alterations/end.gcode deleted file mode 100644 index a84b13158e..0000000000 --- a/Cura/alterations/end.gcode +++ /dev/null @@ -1,6 +0,0 @@ -M104 S0 ;extruder heat off -G91 ;relative positioning -G1 Z+10 E-5 F{max_z_speed} ;move Z up a bit and retract filament by 5mm -G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way -M84 ;steppers off -G90 ;absolute positioning diff --git a/Cura/alterations/nextobject.gcode b/Cura/alterations/nextobject.gcode deleted file mode 100644 index be4ffc1a38..0000000000 --- a/Cura/alterations/nextobject.gcode +++ /dev/null @@ -1,9 +0,0 @@ -;Move to next object on the platform. clear_z is the minimal z height we need to make sure we do not hit any objects. -G92 E0 -G1 Z{clear_z} E-5 F{max_z_speed} -G92 E0 -G1 X{machine_center_x} Y{machine_center_y} F{travel_speed} -G1 F200 E5.5 -G92 E0 -G1 Z0 F{max_z_speed} - diff --git a/Cura/alterations/start.gcode b/Cura/alterations/start.gcode deleted file mode 100644 index 94869d21b2..0000000000 --- a/Cura/alterations/start.gcode +++ /dev/null @@ -1,21 +0,0 @@ -G21 ;metric values -G90 ;absolute positioning - -G28 X0 Y0 ;move X/Y to min endstops -G28 Z0 ;move Z to min endstops - -; if your prints start too high, try changing the Z0.0 below -; to Z1.0 - the number after the Z is the actual, physical -; height of the nozzle in mm. This can take some messing around -; with to get just right... -G92 X0 Y0 Z0 E0 ;reset software position to front/left/z=0.0 -G1 Z15.0 F{max_z_speed} ;move the platform down 15mm -G92 E0 ;zero the extruded length - -G1 F200 E5 ;extrude 5mm of feed stock -G1 F200 E3.5 ;reverse feed stock by 1.5mm -G92 E0 ;zero the extruded length again - -;go to the middle of the platform, and move to Z=0 before starting the print. -G1 X{machine_center_x} Y{machine_center_y} F{travel_speed} -G1 Z0.0 F{max_z_speed} diff --git a/Cura/cura_sf/fabmetheus_utilities/settings.py b/Cura/cura_sf/fabmetheus_utilities/settings.py index bbc9ff397f..0e053c86b2 100644 --- a/Cura/cura_sf/fabmetheus_utilities/settings.py +++ b/Cura/cura_sf/fabmetheus_utilities/settings.py @@ -446,7 +446,7 @@ def getAlterationLines(fileName): return archive.getTextLines(getAlterationFile(fileName)) def getAlterationFile(fileName): - return profile.getAlterationFileContents(fileName) + return unicode(profile.getAlterationFileContents(fileName)).encode("utf-8") #################################### ## Configuration settings classes ## diff --git a/Cura/gui/alterationPanel.py b/Cura/gui/alterationPanel.py index 2692b7fad3..c102f19b1f 100644 --- a/Cura/gui/alterationPanel.py +++ b/Cura/gui/alterationPanel.py @@ -7,7 +7,7 @@ class alterationPanel(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent,-1) - self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'replace.csv'] + self.alterationFileList = ['start.gcode', 'end.gcode', 'support_start.gcode', 'support_end.gcode', 'nextobject.gcode', 'replace.csv'] self.currentFile = None self.textArea = wx.TextCtrl(self, style=wx.TE_MULTILINE|wx.TE_DONTWRAP|wx.TE_PROCESS_TAB) @@ -32,11 +32,9 @@ class alterationPanel(wx.Panel): self.currentFile = self.list.GetSelection() def loadFile(self, filename): - self.textArea.SetValue(unicode(profile.getAlterationFileContents(filename, False), "utf-8")) + self.textArea.SetValue(profile.getAlterationFile(filename)) def OnFocusLost(self, e): if self.currentFile == self.list.GetSelection(): - filename = profile.getAlterationFilePath(self.alterationFileList[self.list.GetSelection()]) - f = open(filename, "wb") - f.write(self.textArea.GetValue().encode("utf-8")) - f.close() + profile.setAlterationFile(self.alterationFileList[self.list.GetSelection()], self.textArea.GetValue()) + diff --git a/Cura/gui/projectPlanner.py b/Cura/gui/projectPlanner.py index f5340ee08f..18027f67f1 100644 --- a/Cura/gui/projectPlanner.py +++ b/Cura/gui/projectPlanner.py @@ -36,18 +36,6 @@ class projectPlanner(wx.Frame): wx.EVT_CLOSE(self, self.OnClose) #self.SetIcon(icon.getMainIcon()) - menubar = wx.MenuBar() - fileMenu = wx.Menu() - i = fileMenu.Append(-1, 'Open Project...') - self.Bind(wx.EVT_MENU, self.OnLoadProject, i) - i = fileMenu.Append(-1, 'Save Project...') - self.Bind(wx.EVT_MENU, self.OnSaveProject, i) - fileMenu.AppendSeparator() - i = fileMenu.Append(wx.ID_EXIT, 'Quit') - self.Bind(wx.EVT_MENU, self.OnQuit, i) - menubar.Append(fileMenu, '&File') - self.SetMenuBar(menubar) - self.list = [] self.selection = None @@ -57,9 +45,14 @@ class projectPlanner(wx.Frame): self.toolbar = toolbarUtil.Toolbar(self) + toolbarUtil.NormalButton(self.toolbar, self.OnLoadProject, 'open.png', 'Open project') + toolbarUtil.NormalButton(self.toolbar, self.OnSaveProject, 'save.png', 'Save project') + self.toolbar.AddSeparator() group = [] toolbarUtil.RadioButton(self.toolbar, group, 'object-3d-on.png', 'object-3d-off.png', '3D view', callback=self.On3DClick) toolbarUtil.RadioButton(self.toolbar, group, 'object-top-on.png', 'object-top-off.png', 'Topdown view', callback=self.OnTopClick).SetValue(True) + self.toolbar.AddSeparator() + toolbarUtil.NormalButton(self.toolbar, self.OnQuit, 'exit.png', 'Close project planner') self.toolbar.Realize() diff --git a/Cura/images/exit.png b/Cura/images/exit.png new file mode 100644 index 0000000000..846776e3b6 Binary files /dev/null and b/Cura/images/exit.png differ diff --git a/Cura/images/open.png b/Cura/images/open.png new file mode 100644 index 0000000000..32ec3edbb8 Binary files /dev/null and b/Cura/images/open.png differ diff --git a/Cura/images/save.png b/Cura/images/save.png new file mode 100644 index 0000000000..8072aea326 Binary files /dev/null and b/Cura/images/save.png differ diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 421a14b3e7..5fa85b7ff2 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -3,10 +3,10 @@ from __future__ import division #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. import __init__ -import ConfigParser, os, traceback, math, re +import ConfigParser, os, traceback, math, re, zlib, base64 ######################################################### -## Profile and preferences functions +## Default settings when none are found. ######################################################### #Single place to store the defaults, so we have a consistent set of default settings. @@ -53,6 +53,7 @@ profileDefaultSettings = { 'fill_overlap': '15', 'support_rate': '50', 'support_distance': '0.5', + 'support_margin': '3.0', 'joris': 'False', 'enable_skin': 'False', 'enable_raft': 'False', @@ -63,9 +64,62 @@ profileDefaultSettings = { 'raft_base_material_amount': '100', 'raft_interface_material_amount': '100', 'bottom_thickness': '0.3', + 'add_start_end_gcode': 'True', 'gcode_extension': 'gcode', } +alterationDefault = { +####################################################################################### + 'start.gcode': """;Start GCode +G21 ;metric values +G90 ;absolute positioning + +G28 X0 Y0 ;move X/Y to min endstops +G28 Z0 ;move Z to min endstops + +; if your prints start too high, try changing the Z0.0 below +; to Z1.0 - the number after the Z is the actual, physical +; height of the nozzle in mm. This can take some messing around +; with to get just right... +G92 X0 Y0 Z0 E0 ;reset software position to front/left/z=0.0 +G1 Z15.0 F{max_z_speed} ;move the platform down 15mm +G92 E0 ;zero the extruded length + +G1 F200 E5 ;extrude 5mm of feed stock +G1 F200 E3.5 ;reverse feed stock by 1.5mm +G92 E0 ;zero the extruded length again + +;go to the middle of the platform, and move to Z=0 before starting the print. +G1 X{machine_center_x} Y{machine_center_y} F{travel_speed} +G1 Z0.0 F{max_z_speed} +""", +####################################################################################### + 'end.gcode': """;End GCode +M104 S0 ;extruder heat off +G91 ;relative positioning +G1 Z+10 E-5 F{max_z_speed} ;move Z up a bit and retract filament by 5mm +G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way +M84 ;steppers off +G90 ;absolute positioning +""", +####################################################################################### + 'support_start.gcode': '', + 'support_end.gcode': '', + 'cool_start.gcode': '', + 'cool_end.gcode': '', + 'replace.csv': '', +####################################################################################### + 'nextobject.gcode': """;Move to next object on the platform. clear_z is the minimal z height we need to make sure we do not hit any objects. +G92 E0 +G1 Z{clear_z} E-5 F{max_z_speed} +G92 E0 +G1 X{machine_center_x} Y{machine_center_y} F{travel_speed} +G1 F200 E5.5 +G92 E0 +G1 Z0 F{max_z_speed} +""", +####################################################################################### +} preferencesDefaultSettings = { 'wizardDone': 'False', 'startMode': 'Simple', @@ -83,6 +137,10 @@ preferencesDefaultSettings = { 'filament_cost_meter': '0', } +######################################################### +## Profile and preferences functions +######################################################### + def getDefaultProfilePath(): return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../current_profile.ini")) @@ -100,19 +158,31 @@ def loadGlobalProfileFromString(options): global globalProfileParser globalProfileParser = ConfigParser.ConfigParser() globalProfileParser.add_section('profile') - for option in options.split('#'): + globalProfileParser.add_section('alterations') + options = base64.b64decode(options) + options = zlib.decompress(options) + (profileOpts, alt) = options.split('\f', 1) + for option in profileOpts.split('\b'): (key, value) = option.split('=', 1) globalProfileParser.set('profile', key, value) + for option in alt.split('\b'): + (key, value) = option.split('=', 1) + globalProfileParser.set('alterations', key, value) def getGlobalProfileString(): global globalProfileParser if not globals().has_key('globalProfileParser'): loadGlobalProfile(getDefaultProfilePath()) - ret = [] + p = [] + alt = [] for key in globalProfileParser.options('profile'): - ret.append(key + "=" + globalProfileParser.get('profile', key)) - return '#'.join(ret) + p.append(key + "=" + globalProfileParser.get('profile', key)) + for key in globalProfileParser.options('alterations'): + alt.append(key + "=" + globalProfileParser.get('alterations', key)) + ret = '\b'.join(p) + '\f' + '\b'.join(alt) + ret = base64.b64encode(zlib.compress(ret, 9)) + return ret def getProfileSetting(name): #Check if we have a configuration file loaded, else load the default. @@ -221,40 +291,56 @@ def calculateSolidLayerCount(): ######################################################### ## Alteration file functions ######################################################### -def getCuraBasePath(): - return os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) - -def getAlterationFilePath(filename): - return os.path.join(getCuraBasePath(), "alterations", filename) - def replaceTagMatch(m): tag = m.group(0)[1:-1] if tag in ['print_speed', 'retraction_speed', 'travel_speed', 'max_z_speed', 'bottom_layer_speed', 'cool_min_feedrate']: return str(getProfileSettingFloat(tag) * 60) return str(getProfileSettingFloat(tag)) -def getAlterationFileContents(filename, modifyForOutput = True): - "Get the file from the fileName or the lowercase fileName in the alterations directories." - prefix = '' - if modifyForOutput: - if filename == 'start.gcode': - #For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion. - #We also set our steps per E here, if configured. - eSteps = float(getPreference('steps_per_e')) - if eSteps > 0: - prefix += 'M92 E'+str(eSteps)+'\n' - temp = getProfileSettingFloat('print_temperature') - if temp > 0: - prefix += 'M109 S'+str(temp)+'\n' - elif filename == 'replace.csv': - prefix = 'M101\nM103\n' - fullFilename = getAlterationFilePath(filename) - if os.path.isfile(fullFilename): - file = open(fullFilename, "r") - fileText = file.read() - file.close() - if modifyForOutput: - fileText = re.sub("\{[^\}]*\}", replaceTagMatch, fileText) - return prefix + fileText - return prefix +### Get aleration raw contents. (Used internally in Cura) +def getAlterationFile(filename): + #Check if we have a configuration file loaded, else load the default. + if not globals().has_key('globalProfileParser'): + loadGlobalProfile(getDefaultProfilePath()) + + if not globalProfileParser.has_option('alterations', filename): + if filename in alterationDefault: + default = alterationDefault[filename] + else: + print "Missing default alteration for: '" + filename + "'" + alterationDefault[filename] = '' + default = '' + if not globalProfileParser.has_section('alterations'): + globalProfileParser.add_section('alterations') + #print "Using default for: %s" % (filename) + globalProfileParser.set('alterations', filename, default) + return unicode(globalProfileParser.get('alterations', filename), "utf-8") + +def setAlterationFile(filename, value): + #Check if we have a configuration file loaded, else load the default. + if not globals().has_key('globalProfileParser'): + loadGlobalProfile(getDefaultProfilePath()) + if not globalProfileParser.has_section('alterations'): + globalProfileParser.add_section('alterations') + globalProfileParser.set('alterations', filename, value.encode("utf-8")) + saveGlobalProfile(getDefaultProfilePath()) + +### Get the alteration file for output. (Used by Skeinforge) +def getAlterationFileContents(filename): + prefix = '' + alterationContents = getAlterationFile(filename) + if filename == 'start.gcode': + #For the start code, hack the temperature and the steps per E value into it. So the temperature is reached before the start code extrusion. + #We also set our steps per E here, if configured. + eSteps = float(getPreference('steps_per_e')) + if eSteps > 0: + prefix += 'M92 E%f\n' % (eSteps) + temp = getProfileSettingFloat('print_temperature') + if temp > 0 and not '{print_temperature}' in alterationContents: + prefix += 'M109 S%f\n' % (temp) + elif filename == 'replace.csv': + #Always remove the extruder on/off M codes. These are no longer needed in 5D printing. + prefix = 'M101\nM103\n' + + return prefix + re.sub("\{[^\}]*\}", replaceTagMatch, alterationContents)