chiark / gitweb /
Merge tag '15.01-RC8' into upstream
authorYouness Alaoui <kakaroto@kakaroto.homelinux.net>
Fri, 6 Mar 2015 17:49:10 +0000 (12:49 -0500)
committerYouness Alaoui <kakaroto@kakaroto.homelinux.net>
Fri, 6 Mar 2015 17:49:10 +0000 (12:49 -0500)
Conflicts:
Cura/gui/sceneView.py
Cura/util/profile.py
Cura/util/sliceEngine.py
package.sh
resources/meshes/ultimaker2_platform.stl

1  2 
Cura/gui/preferencesDialog.py
Cura/gui/sceneView.py
Cura/gui/util/openglGui.py
Cura/util/bigDataStorage.py
Cura/util/gcodeInterpreter.py
Cura/util/profile.py
Cura/util/sliceEngine.py
README.md
package.sh
scripts/win32/installer.nsi

index 9a0d65c9cb524502e4a785fb0f6ba5eb071b5f0d,3f9acd21ac199389a6ae99b2cb4fc6ced873c7f8..ddd401682775f6427e0d032420117e0a1d43c77a
@@@ -45,14 -45,15 +45,15 @@@ class preferencesDialog(wx.Dialog)
                #configBase.TitleRow(right, 'Slicer settings')
                #configBase.SettingRow(right, 'save_profile')
  
-               #configBase.TitleRow(right, 'SD Card settings')
-               configBase.TitleRow(right, _("Cura settings"))
+               configBase.TitleRow(right, 'SD Card settings')
                configBase.SettingRow(right, 'auto_detect_sd')
+               configBase.SettingRow(right, 'sdcard_rootfolder')
+               
+               configBase.TitleRow(right, _("Cura settings"))
                configBase.SettingRow(right, 'check_for_updates')
 -              configBase.SettingRow(right, 'submit_slice_information')
 +              #configBase.SettingRow(right, 'submit_slice_information')
  
 -              self.okButton = wx.Button(right, -1, 'Ok')
 +              self.okButton = wx.Button(right, -1, _('Ok'))
                right.GetSizer().Add(self.okButton, (right.GetSizer().GetRows(), 0), flag=wx.BOTTOM, border=5)
                self.okButton.Bind(wx.EVT_BUTTON, lambda e: self.Close())
  
index 3d5bfa280f18f7cf16923b36706c6fd868f7debe,29193f8a6662db11b947fb288129402174029ef0..ba660b0c493e6d679e8893d85a185d3c59f0567e
@@@ -49,11 -49,10 +49,12 @@@ class SceneView(openglGui.glGuiPanel)
                self._mouseY = -1
                self._mouseState = None
                self._viewTarget = numpy.array([0,0,0], numpy.float32)
 +              self._mouse3Dpos = numpy.array([0,0,0], numpy.float32)
                self._animView = None
                self._animZoom = None
+               self._lastObjectSink = None
                self._platformMesh = {}
 +              self.glReleaseList = []
                self._platformTexture = None
                self._isSimpleMode = True
                self._printerConnectionManager = printerConnectionManager.PrinterConnectionManager()
                        if len(removableStorage.getPossibleSDcardDrives()) > 0 and (connectionGroup is None or connectionGroup.getPriority() < 0):
                                drives = removableStorage.getPossibleSDcardDrives()
                                if len(drives) > 1:
 -                                      dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
 -                                      if dlg.ShowModal() != wx.ID_OK:
 -                                              dlg.Destroy()
 -                                              return
 -                                      drive = drives[dlg.GetSelection()]
 +                                      choices = map(lambda n: n[0], drives)
 +                                      choices += (_("Custom file destination"), )
 +                                      title = _("Multiple removable drives have been found")
 +                              else:
 +                                      choices = [drives[0][0], _("Custom file destination")]
 +                                      title = _("A removable drive has been found")
 +
 +                              dlg = wx.SingleChoiceDialog(self, _("Select destination SD card drive\nYou can also select a custom file to save to"), title, choices)
 +                              if dlg.ShowModal() != wx.ID_OK:
                                        dlg.Destroy()
 +                                      return
 +                              try:
 +                                      drive = drives[dlg.GetSelection()]
 +                              except:
 +                                      drive = None
 +                              dlg.Destroy()
 +
 +                              if drive is None:
 +                                      self.showSaveGCode()
                                else:
 -                                      drive = drives[0]
 -                              filename = self._scene._objectList[0].getName() + profile.getGCodeExtension()
 -                              
 -                              #check if the file is part of the root folder. If so, create folders on sd card to get the same folder hierarchy.
 -                              repDir = profile.getPreference("sdcard_rootfolder")
 -                              if os.path.exists(repDir) and os.path.isdir(repDir):
 -                                      repDir = os.path.abspath(repDir)
 -                                      originFilename = os.path.abspath( self._scene._objectList[0].getOriginFilename() )
 -                                      if os.path.dirname(originFilename).startswith(repDir):
 -                                              filename = os.path.splitext(originFilename[len(repDir):])[0] + profile.getGCodeExtension()
 -                                              sdPath = os.path.dirname(os.path.join( drive[1], filename))
 -                                              if not os.path.exists(sdPath):
 -                                                      print "Creating replication directory:", sdPath
 -                                                      os.makedirs(sdPath)
 -
 -                              threading.Thread(target=self._saveGCode,args=(drive[1] + filename, drive[1])).start()
 +                                      filename = self._scene._objectList[0].getName() + profile.getGCodeExtension()
++
++                                      #check if the file is part of the root folder.
++                                      # If so, create folders on sd card to get the same folder hierarchy.
++                                      repDir = profile.getPreference("sdcard_rootfolder")
++                                      if os.path.exists(repDir) and os.path.isdir(repDir):
++                                              repDir = os.path.abspath(repDir)
++                                              originFilename = os.path.abspath( self._scene._objectList[0].getOriginFilename() )
++                                              if os.path.dirname(originFilename).startswith(repDir):
++                                                      filename = os.path.splitext(originFilename[len(repDir):])[0] + profile.getGCodeExtension()
++                                                      sdPath = os.path.dirname(os.path.join( drive[1], filename))
++                                                      if not os.path.exists(sdPath):
++                                                              print "Creating replication directory:", sdPath
++                                                              os.makedirs(sdPath)
 +                                      threading.Thread(target=self._saveGCode,args=(drive[1] + filename, drive[1])).start()
                        elif connectionGroup is not None:
                                connections = connectionGroup.getAvailableConnections()
                                if len(connections) < 2:
Simple merge
index c32eb2cbc70221bd01d1b258fdec3101eb6682d1,b3894dace654e0a2f8161ab2bd0c7868dc45a207..f5c2940c0f201f0173585607dcbea9ec9b72e047
@@@ -29,17 -32,19 +29,19 @@@ class BigDataStorage(object)
                                self._read_index += 1\r
                                self._active = self._list[self._read_index]\r
                                self._active.seek(0)\r
 -                              ret = self.activeRead(size)\r
 +                              ret = self._active.read(size)\r
                return ret\r
  \r
-       def replaceAtStart(self, key, value):\r
+       def replaceAtStart(self, dictionary):\r
                data = self._list[0].getvalue()\r
                block0 = data[0:2048]\r
-               value = (value + ' ' * len(key))[:len(key)]\r
-               block0 = block0.replace(key, value)\r
+               block1 = StringIO.StringIO()\r
                self._list[0] = StringIO.StringIO()\r
+               block1.write(data[2048:])\r
+               self._list.insert(1, block1)\r
+               for key, value in dictionary.items():\r
+                       block0 = block0.replace(key, str(value))\r
                self._list[0].write(block0)\r
-               self._list[0].write(data[2048:])\r
  \r
        def __len__(self):\r
                ret = 0\r
Simple merge
index 914245e30434f191e0c8b023f84548a98a8c769b,3df63b3d56c88b7a9b987ae0dde856884224b9ff..a7d273dab66dce82b546c68a85b95a647709d305
@@@ -179,15 -179,16 +179,16 @@@ setting('layer_height',              0.
  setting('wall_thickness',            0.8, float, 'basic',    _('Quality')).setRange(0.0).setLabel(_("Shell thickness (mm)"), _("Thickness of the outside shell in the horizontal direction.\nThis is used in combination with the nozzle size to define the number\nof perimeter lines and the thickness of those perimeter lines."))
  setting('retraction_enable',        True, bool,  'basic',    _('Quality')).setExpertSubCategory(_('Retraction')).setLabel(_("Enable retraction"), _("Retract the filament when the nozzle is moving over a none-printed area. Details about the retraction can be configured in the advanced tab."))
  setting('solid_layer_thickness',     0.6, float, 'basic',    _('Fill')).setRange(0).setLabel(_("Bottom/Top thickness (mm)"), _("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 multiple of the layer thickness makes sense. And keep it near your wall thickness to make an evenly strong part."))
 -setting('fill_density',               20, float, 'basic',    _('Fill')).setExpertSubCategory(_('Infill')).setRange(0, 100).setLabel(_("Fill Density (%)"), _("This controls how densely 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.\nThis won't affect the outside of the print and only adjusts how strong the part becomes."))
 +setting('fill_density',               20, float, 'basic',    _('Fill')).setExpertSubCategory(_('Infill')).setRange(0, 100).setLabel(_("Fill Density (%)"), _("This controls how densely 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.\nThis will not affect the outside of the print and only adjusts how strong the part becomes."))
  setting('nozzle_size',               0.4, float, 'advanced', _('Machine')).setRange(0.1,10).setLabel(_("Nozzle size (mm)"), _("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."))
 -setting('print_speed',                50, float, 'basic',    _('Speed and Temperature')).setRange(1).setLabel(_("Print speed (mm/s)"), _("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."))
 -setting('print_temperature',         210, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("Printing temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required."))
 -setting('print_temperature2',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("2nd nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required."))
 -setting('print_temperature3',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("3th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required."))
 -setting('print_temperature4',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("4th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required."))
 -setting('print_temperature5',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("5th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required."))
 -setting('print_bed_temperature',      70, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("Bed temperature (C)"), _("Temperature used for the heated printer bed. Set at 0 to pre-heat yourself."))
 -setting('support',                'None', [_('None'), _('Touching buildplate'), _('Everywhere')], 'basic', _('Support')).setExpertSubCategory(_('Support')).setLabel(_("Support type"), _("Type of support structure build.\n\"Touching buildplate\" is the most commonly used support setting.\n\nNone does not do any support.\nTouching buildplate only creates support where the support structure will touch the build platform.\nEverywhere creates support even on top of parts of the model."))
 +setting('print_speed',                50, float, 'basic',    _('Speed and Temperature')).setRange(1).setLabel(_("Print speed (mm/s)"), _("Speed at which printing happens. A well adjusted 3D printer can reach high speeds. However, for high quality prints slower speeds are required. Printing speed depends on a lot of factors. You will be experimenting with optimal settings for this."))
 +setting('print_temperature',         210, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("Printing temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA 205C is recommended.\nFor ABS and HIPS 240C is recommended."))
 +setting('print_temperature2',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("2nd nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA 205C is recommended.\nFor ABS and HIPS 240C is recommended."))
 +setting('print_temperature3',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("3th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA 205C is recommended.\nFor ABS and HIPS 240C is recommended."))
 +setting('print_temperature4',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("4th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA 205C is recommended.\nFor ABS and HIPS 240C is recommended."))
++setting('print_temperature5',          0, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("5th nozzle temperature (C)"), _("Temperature used for printing. Set at 0 to pre-heat yourself.\nFor PLA 205C is recommended.\nFor ABS and HIPS 240C is recommended."))
 +setting('print_bed_temperature',      70, int,   'basic',    _('Speed and Temperature')).setRange(0,340).setLabel(_("Bed temperature (C)"), _("Temperature used for the heated printer bed. Set at 0 to pre-heat yourself.\nFor PLA 60C is recommended.\nFor ABS and HIPS 110C is recommended."))
 +setting('support',                'None', [_('None'), _('Touching buildplate'), _('Everywhere')], 'basic', _('Support')).setExpertSubCategory(_('Support')).setLabel(_("Support type"), _("Type of support structure build.\n\"Support is useful when a model has severe over hangs. Using this option does require some finishing of the print, including removing the support material.\n\"Touching buildplate\" is the most commonly used support setting.\n\nNone does not do any support.\nTouching buildplate only creates support where the support structure will touch the build platform.\nEverywhere creates support even on top of parts of the model."))
  setting('platform_adhesion',      'None', [_('None'), _('Brim'), _('Raft')], 'basic', _('Support')).setExpertSubCategory([_('Skirt'), _('Brim'), _('Raft')]).setLabel(_("Platform adhesion type"), _("Different options that help in preventing corners from lifting due to warping.\nBrim adds a single layer thick flat area around your object which is easy to cut off afterwards, and it is the recommended option.\nRaft adds a thick raster below the object and a thin interface between this and your object.\n(Note that enabling the brim or raft disables the skirt)"))
  setting('support_dual_extrusion',  'Both', [_('Both'), _('First extruder'), _('Second extruder')], 'basic', _('Support')).setLabel(_("Support dual extrusion"), _("Which extruder to use for support material, for break-away support you can use both extruders.\nBut if one of the materials is more expensive then the other you could select an extruder to use for support material. This causes more extruder switches.\nYou can also use the 2nd extruder for soluble support materials."))
  setting('wipe_tower',              False, bool,  'basic',    _('Dual extrusion')).setLabel(_("Wipe&prime tower"), _("The wipe-tower is a tower printed on every layer when switching between nozzles.\nThe old nozzle is wiped off on the tower before the new nozzle is used to print the 2nd color."))
@@@ -197,11 -198,12 +198,12 @@@ setting('filament_diameter',        2.8
  setting('filament_diameter2',          0, float, 'basic',    _('Filament')).setRange(0).setLabel(_("Diameter2 (mm)"), _("Diameter of your filament for the 2nd nozzle. Use 0 to use the same diameter as for nozzle 1."))
  setting('filament_diameter3',          0, float, 'basic',    _('Filament')).setRange(0).setLabel(_("Diameter3 (mm)"), _("Diameter of your filament for the 3th nozzle. Use 0 to use the same diameter as for nozzle 1."))
  setting('filament_diameter4',          0, float, 'basic',    _('Filament')).setRange(0).setLabel(_("Diameter4 (mm)"), _("Diameter of your filament for the 4th nozzle. Use 0 to use the same diameter as for nozzle 1."))
+ setting('filament_diameter5',          0, float, 'basic',    _('Filament')).setRange(0).setLabel(_("Diameter5 (mm)"), _("Diameter of your filament for the 5th nozzle. Use 0 to use the same diameter as for nozzle 1."))
  setting('filament_flow',            100., float, 'basic',    _('Filament')).setRange(5,300).setLabel(_("Flow (%)"), _("Flow compensation, the amount of material extruded is multiplied by this value"))
  setting('retraction_speed',         40.0, float, 'advanced', _('Retraction')).setRange(0.1).setLabel(_("Speed (mm/s)"), _("Speed at which the filament is retracted, a higher retraction speed works better. But a very high retraction speed can lead to filament grinding."))
 -setting('retraction_amount',         4.5, float, 'advanced', _('Retraction')).setRange(0).setLabel(_("Distance (mm)"), _("Amount of retraction, set at 0 for no retraction at all. A value of 4.5mm seems to generate good results."))
 +setting('retraction_amount',         4.5, float, 'advanced', _('Retraction')).setRange(0).setLabel(_("Distance (mm)"), _("Amount of retraction, set at 0 for no retraction at all. A value between 1 and 2 millimeters provides good results for most materials."))
  setting('retraction_dual_amount',   16.5, float, 'advanced', _('Retraction')).setRange(0).setLabel(_("Dual extrusion switch amount (mm)"), _("Amount of retraction when switching nozzle with dual-extrusion, set at 0 for no retraction at all. A value of 16.0mm seems to generate good results."))
 -setting('retraction_min_travel',     1.5, float, 'expert',   _('Retraction')).setRange(0).setLabel(_("Minimum travel (mm)"), _("Minimum amount of travel needed for a retraction to happen at all. To make sure you do not get a lot of retractions in a small area."))
 +setting('retraction_min_travel',     1.5, float, 'expert',   _('Retraction')).setRange(0).setLabel(_("Minimum travel (mm)"), _("Minimum amount of travel needed for a retraction to happen at all. This setting is used to prevent from having too many retractions in a small area."))
  setting('retraction_combing',       True, bool,  'expert',   _('Retraction')).setLabel(_("Enable combing"), _("Combing is the act of avoiding holes in the print for the head to travel over. If combing is disabled the printer head moves straight from the start point to the end point and it will always retract."))
  setting('retraction_minimal_extrusion',0.02, float,'expert', _('Retraction')).setRange(0).setLabel(_("Minimal extrusion before retracting (mm)"), _("The minimal amount of extrusion that needs to be done before retracting again if a retraction needs to happen before this minimal is reached the retraction is ignored.\nThis avoids retracting a lot on the same piece of filament which flattens the filament and causes grinding issues."))
  setting('retraction_hop',            0.0, float, 'expert',   _('Retraction')).setRange(0).setLabel(_("Z hop when retracting (mm)"), _("When a retraction is done, the head is lifted by this amount to travel over the print. A value of 0.075 works well. This feature has a lot of positive effect on delta towers."))
@@@ -487,7 -487,8 +489,8 @@@ setting('save_profile', 'False', bool, 
  setting('filament_cost_kg', '0', float, 'preference', 'hidden').setLabel(_("Cost (price/kg)"), _("Cost of your filament per kg, to estimate the cost of the final print."))
  setting('filament_cost_meter', '0', float, 'preference', 'hidden').setLabel(_("Cost (price/m)"), _("Cost of your filament per meter, to estimate the cost of the final print."))
  setting('auto_detect_sd', 'True', bool, 'preference', 'hidden').setLabel(_("Auto detect SD card drive"), _("Auto detect the SD card. You can disable this because on some systems external hard-drives or USB sticks are detected as SD card."))
 -setting('check_for_updates', 'True', bool, 'preference', 'hidden').setLabel(_("Check for updates"), _("Check for newer versions of Cura on startup"))
+ setting('sdcard_rootfolder', '', str, 'preference', 'hidden').setLabel(_("Base folder to replicate on SD card"), _("The specified folder will be used as a base path. Any gcode generated from object coming from within that folder will be automatically saved on the SD card at the same sub-folder. Any object coming from outside of this path will save the gcode on the root folder of the card."))
 +setting('check_for_updates', 'False', bool, 'preference', 'hidden').setLabel(_("Check for updates"), _("Check for newer versions of Cura on startup"))
  setting('submit_slice_information', 'False', bool, 'preference', 'hidden').setLabel(_("Send usage statistics"), _("Submit anonymous usage information to improve future versions of Cura"))
  setting('youmagine_token', '', str, 'preference', 'hidden')
  setting('filament_physical_density', '1240', float, 'preference', 'hidden').setRange(500.0, 3000.0).setLabel(_("Density (kg/m3)"), _("Weight of the filament per m3. Around 1240 for PLA. And around 1040 for ABS. This value is used to estimate the weight if the filament used for the print."))
@@@ -516,17 -517,17 +519,18 @@@ setting('machine_height', '200', float
  setting('machine_center_is_zero', 'False', bool, 'machine', 'hidden').setLabel(_("Machine center 0,0"), _("Machines firmware defines the center of the bed as 0,0 instead of the front left corner."))
  setting('machine_shape', 'Square', ['Square','Circular'], 'machine', 'hidden').setLabel(_("Build area shape"), _("The shape of machine build area."))
  setting('ultimaker_extruder_upgrade', 'False', bool, 'machine', 'hidden')
 -setting('has_heated_bed', 'False', bool, 'machine', 'hidden').setLabel(_("Heated bed"), _("If you have an heated bed, this enabled heated bed settings (requires restart)"))
 +setting('has_heated_bed', 'False', bool, 'machine', 'hidden').setLabel(_("Heated bed"), _("If you have a heated bed, this enabled heated bed settings (requires restart)"))
  setting('gcode_flavor', 'RepRap (Marlin/Sprinter)', ['RepRap (Marlin/Sprinter)', 'RepRap (Volumetric)', 'UltiGCode', 'MakerBot', 'BFB', 'Mach3'], 'machine', 'hidden').setLabel(_("GCode Flavor"), _("Flavor of generated GCode.\nRepRap is normal 5D GCode which works on Marlin/Sprinter based firmwares.\nUltiGCode is a variation of the RepRap GCode which puts more settings in the machine instead of the slicer.\nMakerBot GCode has a few changes in the way GCode is generated, but still requires MakerWare to generate to X3G.\nBFB style generates RPM based code.\nMach3 uses A,B,C instead of E for extruders."))
- setting('extruder_amount', '1', ['1','2','3','4'], 'machine', 'hidden').setLabel(_("Extruder count"), _("Amount of extruders in your machine."))
+ setting('extruder_amount', '1', ['1','2','3','4','5'], 'machine', 'hidden').setLabel(_("Extruder count"), _("Amount of extruders in your machine."))
  setting('extruder_offset_x1', '0.0', float, 'machine', 'hidden').setLabel(_("Offset X"), _("The offset of your secondary extruder compared to the primary."))
  setting('extruder_offset_y1', '21.6', float, 'machine', 'hidden').setLabel(_("Offset Y"), _("The offset of your secondary extruder compared to the primary."))
  setting('extruder_offset_x2', '0.0', float, 'machine', 'hidden').setLabel(_("Offset X"), _("The offset of your tertiary extruder compared to the primary."))
  setting('extruder_offset_y2', '0.0', float, 'machine', 'hidden').setLabel(_("Offset Y"), _("The offset of your tertiary extruder compared to the primary."))
  setting('extruder_offset_x3', '0.0', float, 'machine', 'hidden').setLabel(_("Offset X"), _("The offset of your forth extruder compared to the primary."))
  setting('extruder_offset_y3', '0.0', float, 'machine', 'hidden').setLabel(_("Offset Y"), _("The offset of your forth extruder compared to the primary."))
+ setting('extruder_offset_x4', '0.0', float, 'machine', 'hidden').setLabel(_("Offset X"), _("The offset of your forth extruder compared to the primary."))
+ setting('extruder_offset_y4', '0.0', float, 'machine', 'hidden').setLabel(_("Offset Y"), _("The offset of your forth extruder compared to the primary."))
 +setting('extruder_z_offset', '0.0', float, 'machine', 'hidden').setLabel(_("Z-Offset (mm)"), _("This value will be added to the Z coordinate of every line in the output G-Code to compensate for a badly calibrate Z height endstop."))
  setting('steps_per_e', '0', float, 'machine', 'hidden').setLabel(_("E-Steps per 1mm filament"), _("Amount of steps per mm filament extrusion. If set to 0 then this value is ignored and the value in your firmware is used."))
  setting('serial_port', 'AUTO', str, 'machine', 'hidden').setLabel(_("Serial port"), _("Serial port to use for communication with the printer"))
  setting('serial_port_auto', '', str, 'machine', 'hidden')
index 1989d692d4fb2344db46f3bb24153746c4fbeb91,f4e521e2380486ccbeb8b9c4e9aa4cb295600d7b..ee884bd8bd9edf07d10cda5703ca56834d8fb685
@@@ -289,10 -295,13 +295,13 @@@ class Engine(object)
                self._thread.daemon = True
                self._thread.start()
  
 -      def _runEngine(self, scene, overrides, old_thread):
 +      def _runEngine(self, scene, overrides, old_thread, pluginConfig):
                if old_thread is not None:
                        if self._process is not None:
-                               self._process.terminate()
+                               try:
+                                       self._process.terminate()
+                               except:
+                                       pass
                        old_thread.join()
                self._callback(-1.0)
  
  
                        returnCode = self._process.wait()
                        logThread.join()
 +                      self._result.addLog("Slicer process returned : %d" % returnCode)
                        if returnCode == 0:
 -                              plugin_error = pluginInfo.runPostProcessingPlugins(self._result)
+                               self._result.addReplaceTag('#P_TIME#', self._result.getPrintTime())
+                               self._result.addReplaceTag('#F_AMNT#', self._result.getFilamentAmountMeters(0))
+                               self._result.addReplaceTag('#F_WGHT#', math.floor(self._result.getFilamentWeight(0) * 1000.0))
+                               self._result.addReplaceTag('#F_COST#', self._result.getFilamentCost(0))
+                               self._result.applyReplaceTags()
 +                              plugin_error = pluginInfo.runPostProcessingPlugins(self._result, pluginConfig)
                                if plugin_error is not None:
 -                                      print plugin_error
                                        self._result.addLog(plugin_error)
                                self._result.setFinished(True)
                                self._callback(1.0)
                                self._callback(-1.0)
                        self._process = None
                except MemoryError:
+                       traceback.print_exc()
                        self._result.addLog("MemoryError")
                        self._callback(-1.0)
 +              finally:
 +                      try:
 +                              with open(os.path.join(profile.getBasePath(), 'engine.log'), "w") as f:
 +                                      for line in self._result.getLog():
 +                                              f.write(line + "\n")
 +                      except:
 +                              pass
  
        def _watchStderr(self, stderr):
                objectNr = 0
diff --cc README.md
Simple merge
diff --cc package.sh
index 1969b9deb7e471bedf477665d634e9b11b32e975,d492947e1d18c9d22fc24760fc400b5b07966dad..c0c38b436738c1b1b268141290aff83094f921c4
@@@ -19,21 -22,15 +22,21 @@@ BUILD_TARGET=${1:-none
  ##Do we need to create the final archive
  ARCHIVE_FOR_DISTRIBUTION=1
  ##Which version name are we appending to the final archive
- export BUILD_NAME=15.01-RC7
+ export BUILD_NAME=15.01-RC8
  TARGET_DIR=Cura-${BUILD_NAME}-${BUILD_TARGET}
  
 +##Revision
 +export REVISION=1.19
 +
 +##Git commit
 +GIT_HASH=$(git rev-parse --short=4 HEAD)
 +
  ##Which versions of external programs to use
  WIN_PORTABLE_PY_VERSION=2.7.2.1
  
  ##Which CuraEngine to use
- if [ -z ${CURA_ENGINE_REPO} ] ; then
+ if [ -z ${CURA_ENGINE_REPO:-} ] ; then
 -      CURA_ENGINE_REPO="git@github.com:Ultimaker/CuraEngine.git"
 +      CURA_ENGINE_REPO="git@github.com:alephobjects/CuraEngine.git"
  fi
  
  #############################
Simple merge