Merge pull request #1757 from fieldOfView/fix_material_uniqueness

Fix material uniqueness
This commit is contained in:
jack 2017-05-16 09:44:10 +02:00 committed by GitHub
commit 85a29e967f
4 changed files with 222 additions and 49 deletions

View file

@ -3,11 +3,13 @@
import os.path import os.path
import urllib import urllib
import uuid
from typing import Dict, Union from typing import Dict, Union
from PyQt5.QtCore import QObject, QUrl, QVariant from PyQt5.QtCore import QObject, QUrl, QVariant
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from UM.Util import parseBool
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.SaveFile import SaveFile from UM.SaveFile import SaveFile
@ -671,6 +673,9 @@ class ContainerManager(QObject):
return new_change_instances return new_change_instances
## Create a duplicate of a material, which has the same GUID and base_file metadata
#
# \return \type{str} the id of the newly created container.
@pyqtSlot(str, result = str) @pyqtSlot(str, result = str)
def duplicateMaterial(self, material_id: str) -> str: def duplicateMaterial(self, material_id: str) -> str:
containers = self._container_registry.findInstanceContainers(id=material_id) containers = self._container_registry.findInstanceContainers(id=material_id)
@ -693,6 +698,104 @@ class ContainerManager(QObject):
duplicated_container.deserialize(f.read()) duplicated_container.deserialize(f.read())
duplicated_container.setDirty(True) duplicated_container.setDirty(True)
self._container_registry.addContainer(duplicated_container) self._container_registry.addContainer(duplicated_container)
return self._getMaterialContainerIdForActiveMachine(new_id)
## Create a new material by cloning Generic PLA and setting the GUID to something unqiue
#
# \return \type{str} the id of the newly created container.
@pyqtSlot(result = str)
def createMaterial(self) -> str:
# Ensure all settings are saved.
Application.getInstance().saveSettings()
containers = self._container_registry.findInstanceContainers(id="generic_pla")
if not containers:
Logger.log("d", "Unable to create a new material by cloning generic_pla, because it doesn't exist.")
return ""
# Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName("custom_material")
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.setMetaDataEntry("GUID", str(uuid.uuid4()))
duplicated_container.setMetaDataEntry("brand", catalog.i18nc("@label", "Custom"))
duplicated_container.setMetaDataEntry("material", catalog.i18nc("@label", "Custom"))
duplicated_container.setName(catalog.i18nc("@label", "Custom Material"))
self._container_registry.addContainer(duplicated_container)
return self._getMaterialContainerIdForActiveMachine(new_id)
## Find the id of a material container based on the new material
# Utilty function that is shared between duplicateMaterial and createMaterial
#
# \param base_file \type{str} the id of the created container.
def _getMaterialContainerIdForActiveMachine(self, base_file):
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return base_file
has_machine_materials = parseBool(global_stack.getMetaDataEntry("has_machine_materials", default = False))
has_variant_materials = parseBool(global_stack.getMetaDataEntry("has_variant_materials", default = False))
has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False))
if has_machine_materials or has_variant_materials:
if has_variants:
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
else:
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())
if materials:
return materials[0].getId()
Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file)
return "" # do not activate a new material if a container can not be found
return base_file
## Get a list of materials that have the same GUID as the reference material
#
# \param material_id \type{str} the id of the material for which to get the linked materials.
# \return \type{list} a list of names of materials with the same GUID
@pyqtSlot(str, result = "QStringList")
def getLinkedMaterials(self, material_id: str):
containers = self._container_registry.findInstanceContainers(id=material_id)
if not containers:
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't exist.", material_id)
return []
material_container = containers[0]
material_base_file = material_container.getMetaDataEntry("base_file", "")
material_guid = material_container.getMetaDataEntry("GUID", "")
if not material_guid:
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't have a GUID.", material_id)
return []
containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_guid)
linked_material_names = []
for container in containers:
if container.getId() in [material_id, material_base_file] or container.getMetaDataEntry("base_file") != container.getId():
continue
linked_material_names.append(container.getName())
return linked_material_names
## Unlink a material from all other materials by creating a new GUID
# \param material_id \type{str} the id of the material to create a new GUID for.
@pyqtSlot(str)
def unlinkMaterial(self, material_id: str):
containers = self._container_registry.findInstanceContainers(id=material_id)
if not containers:
Logger.log("d", "Unable to make the material with id %s unique, because it doesn't exist.", material_id)
return ""
containers[0].setMetaDataEntry("GUID", str(uuid.uuid4()))
## Get the singleton instance for this class. ## Get the singleton instance for this class.
@classmethod @classmethod

View file

@ -24,66 +24,89 @@ TabView
property double spoolLength: calculateSpoolLength() property double spoolLength: calculateSpoolLength()
property real costPerMeter: calculateCostPerMeter() property real costPerMeter: calculateCostPerMeter()
property bool reevaluateLinkedMaterials: false
property string linkedMaterialNames:
{
if (reevaluateLinkedMaterials)
{
reevaluateLinkedMaterials = false;
}
if(!base.containerId || !base.editingEnabled)
{
return ""
}
var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.containerId);
return linkedMaterials.join(", ");
}
Tab Tab
{ {
title: catalog.i18nc("@title","Information") title: catalog.i18nc("@title","Information")
anchors anchors.margins: UM.Theme.getSize("default_margin").width
{
leftMargin: UM.Theme.getSize("default_margin").width
topMargin: UM.Theme.getSize("default_margin").height
bottomMargin: UM.Theme.getSize("default_margin").height
rightMargin: 0
}
ScrollView ScrollView
{ {
id: scrollView
anchors.fill: parent anchors.fill: parent
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
flickableItem.flickableDirection: Flickable.VerticalFlick flickableItem.flickableDirection: Flickable.VerticalFlick
frameVisible: true
property real columnWidth: Math.floor(viewport.width * 0.5) - UM.Theme.getSize("default_margin").width
Flow Flow
{ {
id: containerGrid id: containerGrid
width: base.width; x: UM.Theme.getSize("default_margin").width
y: UM.Theme.getSize("default_lining").height
property real rowHeight: textField.height; width: base.width
property real rowHeight: textField.height + UM.Theme.getSize("default_lining").height
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") }
ReadOnlyTextField ReadOnlyTextField
{ {
id: displayNameTextField; id: displayNameTextField;
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.name; text: properties.name;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setName(properties.name, text) onEditingFinished: base.setName(properties.name, text)
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
ReadOnlyTextField ReadOnlyTextField
{ {
id: textField; id: textField;
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.supplier; text: properties.supplier;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("brand", properties.supplier, text) onEditingFinished:
{
base.setMetaDataEntry("brand", properties.supplier, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId);
}
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
ReadOnlyTextField ReadOnlyTextField
{ {
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.material_type; text: properties.material_type;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("material", properties.material_type, text) onEditingFinished:
{
base.setMetaDataEntry("material", properties.material_type, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
}
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
Row Row
{ {
width: base.secondColumnWidth; width: scrollView.columnWidth;
height: parent.rowHeight; height: parent.rowHeight;
spacing: UM.Theme.getSize("default_margin").width/2 spacing: UM.Theme.getSize("default_margin").width/2
@ -115,11 +138,11 @@ TabView
Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") } Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") }
ReadOnlySpinBox ReadOnlySpinBox
{ {
id: densitySpinBox id: densitySpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: properties.density value: properties.density
decimals: 2 decimals: 2
suffix: " g/cm³" suffix: " g/cm³"
@ -130,11 +153,11 @@ TabView
onValueChanged: updateCostPerMeter() onValueChanged: updateCostPerMeter()
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") }
ReadOnlySpinBox ReadOnlySpinBox
{ {
id: diameterSpinBox id: diameterSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: properties.diameter value: properties.diameter
decimals: 2 decimals: 2
suffix: " mm" suffix: " mm"
@ -145,11 +168,11 @@ TabView
onValueChanged: updateCostPerMeter() onValueChanged: updateCostPerMeter()
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") }
SpinBox SpinBox
{ {
id: spoolCostSpinBox id: spoolCostSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") value: base.getMaterialPreferenceValue(properties.guid, "spool_cost")
prefix: base.currency + " " prefix: base.currency + " "
decimals: 2 decimals: 2
@ -161,11 +184,11 @@ TabView
} }
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") }
SpinBox SpinBox
{ {
id: spoolWeightSpinBox id: spoolWeightSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: base.getMaterialPreferenceValue(properties.guid, "spool_weight") value: base.getMaterialPreferenceValue(properties.guid, "spool_weight")
suffix: " g" suffix: " g"
stepSize: 100 stepSize: 100
@ -178,24 +201,45 @@ TabView
} }
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") }
Label Label
{ {
width: base.secondColumnWidth width: scrollView.columnWidth
text: "~ %1 m".arg(Math.round(base.spoolLength)) text: "~ %1 m".arg(Math.round(base.spoolLength))
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
height: parent.rowHeight height: parent.rowHeight
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") }
Label Label
{ {
width: base.secondColumnWidth width: scrollView.columnWidth
text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency)
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
height: parent.rowHeight height: parent.rowHeight
} }
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height; visible: unlinkMaterialButton.visible }
Label
{
width: 2 * scrollView.columnWidth
verticalAlignment: Qt.AlignVCenter
text: catalog.i18nc("@label", "This material is linked to %1 and shares some of its properties.").arg(base.linkedMaterialNames)
wrapMode: Text.WordWrap
visible: unlinkMaterialButton.visible
}
Button
{
id: unlinkMaterialButton
text: catalog.i18nc("@label", "Unlink Material")
visible: base.linkedMaterialNames != ""
onClicked:
{
Cura.ContainerManager.unlinkMaterial(base.containerId)
base.reevaluateLinkedMaterials = true
}
}
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") } Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") }
@ -203,7 +247,7 @@ TabView
ReadOnlyTextArea ReadOnlyTextArea
{ {
text: properties.description; text: properties.description;
width: base.firstColumnWidth + base.secondColumnWidth width: 2 * scrollView.columnWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
@ -216,13 +260,15 @@ TabView
ReadOnlyTextArea ReadOnlyTextArea
{ {
text: properties.adhesion_info; text: properties.adhesion_info;
width: base.firstColumnWidth + base.secondColumnWidth width: 2 * scrollView.columnWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text)
} }
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
} }
function updateCostPerMeter() function updateCostPerMeter()
@ -266,8 +312,10 @@ TabView
{ {
id: label id: label
width: base.firstColumnWidth; width: base.firstColumnWidth;
height: spinBox.height height: spinBox.height + UM.Theme.getSize("default_lining").height
text: model.label text: model.label
elide: Text.ElideRight
verticalAlignment: Qt.AlignVCenter
} }
ReadOnlySpinBox ReadOnlySpinBox
{ {
@ -380,6 +428,7 @@ TabView
Cura.ContainerManager.setContainerName(base.containerId, new_value); Cura.ContainerManager.setContainerName(base.containerId, new_value);
// update material name label. not so pretty, but it works // update material name label. not so pretty, but it works
materialProperties.name = new_value; materialProperties.name = new_value;
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
} }
} }
} }

View file

@ -14,6 +14,12 @@ UM.ManagementPage
title: catalog.i18nc("@title:tab", "Materials"); title: catalog.i18nc("@title:tab", "Materials");
Component.onCompleted:
{
// Workaround to make sure all of the items are visible
objectList.positionViewAtBeginning();
}
model: UM.InstanceContainersModel model: UM.InstanceContainersModel
{ {
filter: filter:
@ -81,6 +87,7 @@ UM.ManagementPage
anchors.fill: parent; anchors.fill: parent;
onClicked: onClicked:
{ {
forceActiveFocus();
if(!parent.ListView.isCurrentItem) if(!parent.ListView.isCurrentItem)
{ {
parent.ListView.view.currentIndex = index; parent.ListView.view.currentIndex = index;
@ -91,9 +98,11 @@ UM.ManagementPage
} }
activeId: Cura.MachineManager.activeMaterialId activeId: Cura.MachineManager.activeMaterialId
activeIndex: { activeIndex: getIndexById(activeId)
function getIndexById(material_id)
{
for(var i = 0; i < model.rowCount(); i++) { for(var i = 0; i < model.rowCount(); i++) {
if (model.getItem(i).id == Cura.MachineManager.activeMaterialId) { if (model.getItem(i).id == material_id) {
return i; return i;
} }
} }
@ -135,6 +144,24 @@ UM.ManagementPage
} }
}, },
Button Button
{
text: catalog.i18nc("@action:button", "Create")
iconName: "list-add"
onClicked:
{
var material_id = Cura.ContainerManager.createMaterial()
if(material_id == "")
{
return
}
if(Cura.MachineManager.hasMaterials)
{
Cura.MachineManager.setActiveMaterial(material_id)
}
base.objectList.currentIndex = base.getIndexById(material_id);
}
},
Button
{ {
text: catalog.i18nc("@action:button", "Duplicate"); text: catalog.i18nc("@action:button", "Duplicate");
iconName: "list-add"; iconName: "list-add";
@ -152,6 +179,7 @@ UM.ManagementPage
{ {
Cura.MachineManager.setActiveMaterial(material_id) Cura.MachineManager.setActiveMaterial(material_id)
} }
base.objectList.currentIndex = base.getIndexById(material_id);
} }
}, },
Button Button
@ -206,6 +234,8 @@ UM.ManagementPage
properties: materialProperties properties: materialProperties
containerId: base.currentItem != null ? base.currentItem.id : "" containerId: base.currentItem != null ? base.currentItem.id : ""
property alias pane: base
} }
QtObject QtObject
@ -251,6 +281,10 @@ UM.ManagementPage
{ {
Cura.ContainerManager.removeContainer(containers[i]) Cura.ContainerManager.removeContainer(containers[i])
} }
if(base.objectList.currentIndex > 0)
{
base.objectList.currentIndex--;
}
currentItem = base.model.getItem(base.objectList.currentIndex) // Refresh the current item. currentItem = base.model.getItem(base.objectList.currentIndex) // Refresh the current item.
} }
} }

View file

@ -45,17 +45,4 @@ Item
} }
} }
} }
Label
{
visible: base.readOnly
text: textArea.text
anchors.fill: parent
anchors.margins: textArea.__style ? textArea.__style.textMargin : 4
color: palette.buttonText
}
SystemPalette { id: palette }
} }