Merge branch '2.3' of https://github.com/Ultimaker/Cura into 2.3

This commit is contained in:
fieldOfView 2016-09-08 18:50:53 +02:00
commit 40ae75f54f
7 changed files with 91 additions and 78 deletions

View File

@ -322,6 +322,7 @@ class CuraApplication(QtApplication):
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name) path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
if path: if path:
instance.setPath(path)
with SaveFile(path, "wt", -1, "utf-8") as f: with SaveFile(path, "wt", -1, "utf-8") as f:
f.write(data) f.write(data)
@ -346,6 +347,7 @@ class CuraApplication(QtApplication):
elif stack_type == "extruder_train": elif stack_type == "extruder_train":
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name) path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
if path: if path:
stack.setPath(path)
with SaveFile(path, "wt", -1, "utf-8") as f: with SaveFile(path, "wt", -1, "utf-8") as f:
f.write(data) f.write(data)

View File

@ -150,8 +150,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
@pyqtSlot(int) @pyqtSlot(int)
def setTargetBedTemperature(self, temperature): def setTargetBedTemperature(self, temperature):
self._setTargetBedTemperature(temperature) self._setTargetBedTemperature(temperature)
self._target_bed_temperature = temperature if self._target_bed_temperature != temperature:
self.targetBedTemperatureChanged.emit() self._target_bed_temperature = temperature
self.targetBedTemperatureChanged.emit()
## Time the print has been printing. ## Time the print has been printing.
# Note that timeTotal - timeElapsed should give time remaining. # Note that timeTotal - timeElapsed should give time remaining.
@ -212,8 +213,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
# This simply sets the bed temperature, but ensures that a signal is emitted. # This simply sets the bed temperature, but ensures that a signal is emitted.
# /param temperature temperature of the bed. # /param temperature temperature of the bed.
def _setBedTemperature(self, temperature): def _setBedTemperature(self, temperature):
self._bed_temperature = temperature if self._bed_temperature != temperature:
self.bedTemperatureChanged.emit() self._bed_temperature = temperature
self.bedTemperatureChanged.emit()
## Get the target bed temperature if connected printer (if any) ## Get the target bed temperature if connected printer (if any)
@pyqtProperty(int, notify = targetBedTemperatureChanged) @pyqtProperty(int, notify = targetBedTemperatureChanged)
@ -228,8 +230,10 @@ class PrinterOutputDevice(QObject, OutputDevice):
@pyqtSlot(int, int) @pyqtSlot(int, int)
def setTargetHotendTemperature(self, index, temperature): def setTargetHotendTemperature(self, index, temperature):
self._setTargetHotendTemperature(index, temperature) self._setTargetHotendTemperature(index, temperature)
self._target_hotend_temperatures[index] = temperature
self.targetHotendTemperaturesChanged.emit() if self._target_hotend_temperatures[index] != temperature:
self._target_hotend_temperatures[index] = temperature
self.targetHotendTemperaturesChanged.emit()
## Implementation function of setTargetHotendTemperature. ## Implementation function of setTargetHotendTemperature.
# /param index Index of the hotend to set the temperature of # /param index Index of the hotend to set the temperature of
@ -251,8 +255,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
# /param index Index of the hotend # /param index Index of the hotend
# /param temperature temperature of the hotend (in deg C) # /param temperature temperature of the hotend (in deg C)
def _setHotendTemperature(self, index, temperature): def _setHotendTemperature(self, index, temperature):
self._hotend_temperatures[index] = temperature if self._hotend_temperatures[index] != temperature:
self.hotendTemperaturesChanged.emit() self._hotend_temperatures[index] = temperature
self.hotendTemperaturesChanged.emit()
@pyqtProperty("QVariantList", notify = materialIdChanged) @pyqtProperty("QVariantList", notify = materialIdChanged)
def materialIds(self): def materialIds(self):
@ -267,7 +272,6 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._material_ids[index] = material_id self._material_ids[index] = material_id
self.materialIdChanged.emit(index, material_id) self.materialIdChanged.emit(index, material_id)
@pyqtProperty("QVariantList", notify = hotendIdChanged) @pyqtProperty("QVariantList", notify = hotendIdChanged)
def hotendIds(self): def hotendIds(self):
return self._hotend_ids return self._hotend_ids
@ -302,8 +306,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
## Set the connection state of this output device. ## Set the connection state of this output device.
# /param connection_state ConnectionState enum. # /param connection_state ConnectionState enum.
def setConnectionState(self, connection_state): def setConnectionState(self, connection_state):
self._connection_state = connection_state if self._connection_state != connection_state:
self.connectionStateChanged.emit(self._id) self._connection_state = connection_state
self.connectionStateChanged.emit(self._id)
@pyqtProperty(str, notify = connectionTextChanged) @pyqtProperty(str, notify = connectionTextChanged)
def connectionText(self): def connectionText(self):
@ -351,6 +356,7 @@ class PrinterOutputDevice(QObject, OutputDevice):
if self._head_z != z: if self._head_z != z:
self._head_z = z self._head_z = z
position_changed = True position_changed = True
if position_changed: if position_changed:
self.headPositionChanged.emit() self.headPositionChanged.emit()

View File

@ -167,6 +167,19 @@ class ContainerManager(QObject):
return True return True
@pyqtSlot(str, str, result=str)
def getContainerMetaDataEntry(self, container_id, entry_name):
containers = self._container_registry.findContainers(None, id=container_id)
if not containers:
UM.Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id)
return False
result = containers[0].getMetaDataEntry(entry_name)
if result:
return result
else:
return ""
## Set a metadata entry of the specified container. ## Set a metadata entry of the specified container.
# #
# This will set the specified entry of the container's metadata to the specified # This will set the specified entry of the container's metadata to the specified
@ -577,6 +590,29 @@ class ContainerManager(QObject):
return new_name return new_name
@pyqtSlot(str, result = str)
def duplicateMaterial(self, material_id):
containers = self._container_registry.findInstanceContainers(id=material_id)
if not containers:
UM.Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id)
return ""
# Ensure all settings are saved.
UM.Application.getInstance().saveSettings()
# Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName(material_id)
container_type = type(containers[0]) # Could be either a XMLMaterialProfile or a InstanceContainer
duplicated_container = container_type(new_id)
# Instead of duplicating we load the data from the basefile again.
# This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile
# are also correctly created.
with open(containers[0].getPath(), encoding="utf-8") as f:
duplicated_container.deserialize(f.read())
duplicated_container.setDirty(True)
self._container_registry.addContainer(duplicated_container)
# Factory function, used by QML # Factory function, used by QML
@staticmethod @staticmethod
def createContainerManager(engine, js_engine): def createContainerManager(engine, js_engine):

View File

@ -27,15 +27,15 @@ class CuraProfileReader(ProfileReader):
# returned. # returned.
def read(self, file_name): def read(self, file_name):
try: try:
archive = zipfile.ZipFile(file_name, "r") with zipfile.ZipFile(file_name, "r") as archive:
results = [] results = []
for profile_id in archive.namelist(): for profile_id in archive.namelist():
with archive.open(profile_id) as f: with archive.open(profile_id) as f:
serialized = f.read() serialized = f.read()
profile = self._loadProfile(serialized.decode("utf-8"), profile_id) profile = self._loadProfile(serialized.decode("utf-8"), profile_id)
if profile is not None: if profile is not None:
results.append(profile) results.append(profile)
return results return results
except zipfile.BadZipFile: except zipfile.BadZipFile:
# It must be an older profile from Cura 2.1. # It must be an older profile from Cura 2.1.

View File

@ -22,35 +22,6 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
super().__init__(container_id, *args, **kwargs) super().__init__(container_id, *args, **kwargs)
self._inherited_files = [] self._inherited_files = []
## Overridden from InstanceContainer
def duplicate(self, new_id, new_name = None):
base_file = self.getMetaDataEntry("base_file", None)
if base_file != self.id:
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = base_file)
if containers:
new_basefile = containers[0].duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name)
base_file = new_basefile.id
UM.Settings.ContainerRegistry.getInstance().addContainer(new_basefile)
new_id = self.getMetaDataEntry("brand") + "_" + new_id + "_" + self.getDefinition().getId()
variant = self.getMetaDataEntry("variant")
if variant:
variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant)
if variant_containers:
new_id += "_" + variant_containers[0].getName().replace(" ", "_")
has_base_file = True
else:
has_base_file = False
new_id = UM.Settings.ContainerRegistry.getInstance().createUniqueName("material", self._id, new_id, "")
result = super().duplicate(new_id, new_name)
if has_base_file:
result.setMetaDataEntry("base_file", base_file)
else:
result.setMetaDataEntry("base_file", result.id)
return result
def getInheritedFiles(self): def getInheritedFiles(self):
return self._inherited_files return self._inherited_files
@ -63,6 +34,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
container._read_only = read_only # prevent loop instead of calling setReadOnly container._read_only = read_only # prevent loop instead of calling setReadOnly
## Overridden from InstanceContainer ## Overridden from InstanceContainer
# set the meta data for all machine / variant combinations
def setMetaDataEntry(self, key, value): def setMetaDataEntry(self, key, value):
if self.isReadOnly(): if self.isReadOnly():
return return
@ -103,10 +75,17 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
# #
# basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile. # basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
# for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): # for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
# container._dirty = True # if not container.isReadOnly():
# container.setDirty(True)
## Overridden from InstanceContainer ## Overridden from InstanceContainer
# base file: global settings + supported machines
# machine / variant combination: only changes for itself.
def serialize(self): def serialize(self):
if self._read_only:
Logger.log("w", "Serializing read-only container [%s], probably a programming error." % self.id)
return
registry = UM.Settings.ContainerRegistry.getInstance() registry = UM.Settings.ContainerRegistry.getInstance()
base_file = self.getMetaDataEntry("base_file", "") base_file = self.getMetaDataEntry("base_file", "")
@ -114,7 +93,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
# Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile,
# we should only serialize the "base" material definition, since that can then take care of # we should only serialize the "base" material definition, since that can then take care of
# serializing the machine/nozzle specific profiles. # serializing the machine/nozzle specific profiles.
raise NotImplementedError("Cannot serialize non-root XML materials") raise NotImplementedError("Ignoring serializing non-root XML materials, the data is contained in the base material")
builder = ET.TreeBuilder() builder = ET.TreeBuilder()

View File

@ -2478,19 +2478,6 @@
"enabled": "support_enable", "enabled": "support_enable",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_area_smoothing":
{
"label": "Support Area Smoothing",
"description": "Maximum distance in the X/Y directions of a line segment which is to be smoothed out. Ragged lines are introduced by the join distance and support bridge, which cause the machine to resonate. Smoothing the support areas won't cause them to break with the constraints, except it might change the overhang.",
"unit": "mm",
"type": "float",
"default_value": 0.6,
"global_inherits_stack": "support_extruder_nr",
"minimum_value": "0",
"maximum_value_warning": "1.0",
"enabled": "support_enable",
"settable_per_mesh": true
},
"support_interface_enable": "support_interface_enable":
{ {
"label": "Enable Support Interface", "label": "Enable Support Interface",
@ -2546,6 +2533,19 @@
} }
} }
}, },
"support_interface_skip_height":
{
"label": "Support Interface Resolution",
"description": "When checking where there's model above the support, take steps of the given height. Lower values will slice slower, while higher values may cause normal support to be printed in some places where there should have been support interface.",
"unit": "mm",
"type": "float",
"default_value": 0.3,
"minimum_value": "0",
"global_inherits_stack": "support_extruder_nr",
"maximum_value_warning": "support_interface_height",
"enabled": "extruderValue(support_extruder_nr, 'support_interface_enable') and support_enable",
"settable_per_mesh": true
},
"support_interface_density": "support_interface_density":
{ {
"label": "Support Interface Density", "label": "Support Interface Density",

View File

@ -129,7 +129,6 @@ UM.ManagementPage
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
}, },
/* // apparently visible does not work on OS X
Button Button
{ {
text: catalog.i18nc("@action:button", "Duplicate"); text: catalog.i18nc("@action:button", "Duplicate");
@ -137,24 +136,17 @@ UM.ManagementPage
enabled: base.currentItem != null enabled: base.currentItem != null
onClicked: onClicked:
{ {
var material_id = Cura.ContainerManager.duplicateContainer(base.currentItem.id) var base_file = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "base_file")
// We need to copy the base container instead of the specific variant.
var material_id = base_file == "" ? Cura.ContainerManager.duplicateMaterial(base.currentItem.id): Cura.ContainerManager.duplicateMaterial(base_file)
if(material_id == "") if(material_id == "")
{ {
return return
} }
if(Cura.MachineManager.filterQualityByMachine)
{
var quality_id = Cura.ContainerManager.duplicateContainer(Cura.MachineManager.activeQualityId)
Cura.ContainerManager.setContainerMetaDataEntry(quality_id, "material", material_id)
Cura.MachineManager.setActiveQuality(quality_id)
}
Cura.MachineManager.setActiveMaterial(material_id) Cura.MachineManager.setActiveMaterial(material_id)
} }
visible: false;
}, },
*/
Button Button
{ {
text: catalog.i18nc("@action:button", "Remove"); text: catalog.i18nc("@action:button", "Remove");
@ -162,15 +154,13 @@ UM.ManagementPage
enabled: base.currentItem != null && !base.currentItem.readOnly && !Cura.ContainerManager.isContainerUsed(base.currentItem.id) enabled: base.currentItem != null && !base.currentItem.readOnly && !Cura.ContainerManager.isContainerUsed(base.currentItem.id)
onClicked: confirmDialog.open() onClicked: confirmDialog.open()
}, },
/* // apparently visible does not work on OS X
Button Button
{ {
text: catalog.i18nc("@action:button", "Import"); text: catalog.i18nc("@action:button", "Import");
iconName: "document-import"; iconName: "document-import";
onClicked: importDialog.open(); onClicked: importDialog.open();
visible: false; visible: true;
}, },
*/
Button Button
{ {
text: catalog.i18nc("@action:button", "Export") text: catalog.i18nc("@action:button", "Export")