Merge branch '4.4' into translations_4.4

This commit is contained in:
Remco Burema 2019-11-19 11:54:08 +01:00
commit 5cae2fd098
No known key found for this signature in database
GPG key ID: 215C49431D43F98C
14 changed files with 107 additions and 34 deletions

View file

@ -140,7 +140,7 @@ class MachineNode(ContainerNode):
elif groups_by_name[name].intent_category == "default": # Intent category should be stored as "default" if everything is default or as the intent if any of the extruder have an actual intent.
groups_by_name[name].intent_category = quality_changes.get("intent_category", "default")
if "position" in quality_changes: # An extruder profile.
if quality_changes.get("position") is not None: # An extruder profile.
groups_by_name[name].metadata_per_extruder[int(quality_changes["position"])] = quality_changes
else: # Global profile.
groups_by_name[name].metadata_for_global = quality_changes

View file

@ -6,6 +6,7 @@ from typing import Dict, Set
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty
from UM.Qt.ListModel import ListModel
from UM.Logger import Logger
import cura.CuraApplication # Imported like this to prevent a circular reference.
from cura.Machines.ContainerTree import ContainerTree
@ -153,7 +154,12 @@ class BaseMaterialsModel(ListModel):
if not extruder_stack:
return
nozzle_name = extruder_stack.variant.getName()
materials = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[nozzle_name].materials
machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
if nozzle_name not in machine_node.variants:
Logger.log("w", "Unable to find variant %s in container tree", nozzle_name)
self._available_materials = {}
return
materials = machine_node.variants[nozzle_name].materials
approximate_material_diameter = extruder_stack.getApproximateMaterialDiameter()
self._available_materials = {key: material for key, material in materials.items() if float(material.getMetaDataEntry("approximate_diameter", -1)) == approximate_material_diameter}

View file

@ -1,9 +1,10 @@
#Copyright (c) 2019 Ultimaker B.V.
#Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, QTimer
import collections
from PyQt5.QtCore import Qt, QTimer
from typing import TYPE_CHECKING, Optional, Dict
from cura.Machines.Models.IntentTranslations import intent_translations
from cura.Machines.Models.IntentModel import IntentModel
from cura.Settings.IntentManager import IntentManager
@ -53,7 +54,6 @@ class IntentCategoryModel(ListModel):
}
return cls._translations
## Creates a new model for a certain intent category.
# \param The category to list the intent profiles for.
def __init__(self, intent_category: str) -> None:
@ -110,7 +110,7 @@ class IntentCategoryModel(ListModel):
result.sort(key = lambda k: k["weight"])
self.setItems(result)
## Get a display value for a category. See IntenCategoryModel._translations
## Get a display value for a category.
## for categories and keys
@staticmethod
def translation(category: str, key: str, default: Optional[str] = None):

View file

@ -0,0 +1,21 @@
import collections
from UM.i18n import i18nCatalog
from typing import Dict, Optional
catalog = i18nCatalog("cura")
intent_translations = collections.OrderedDict() # type: "collections.OrderedDict[str, Dict[str, Optional[str]]]"
intent_translations["default"] = {
"name": catalog.i18nc("@label", "Default")
}
intent_translations["visual"] = {
"name": catalog.i18nc("@label", "Visual"),
"description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality.")
}
intent_translations["engineering"] = {
"name": catalog.i18nc("@label", "Engineering"),
"description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances.")
}
intent_translations["quick"] = {
"name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction.")
}

View file

@ -14,6 +14,7 @@ from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_quality_changes_container
from cura.Settings.IntentManager import IntentManager
from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
from cura.Machines.Models.IntentTranslations import intent_translations
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
@ -336,10 +337,11 @@ class QualityManagementModel(ListModel):
"quality_type": quality_type,
"quality_changes_group": None,
"intent_category": intent_category,
"section_name": catalog.i18nc("@label", intent_category.capitalize()),
"section_name": catalog.i18nc("@label", intent_translations.get(intent_category, {}).get("name", catalog.i18nc("@label", "Unknown"))),
})
# Sort by quality_type for each intent category
result = sorted(result, key = lambda x: (x["intent_category"], x["quality_type"]))
result = sorted(result, key = lambda x: (list(intent_translations).index(x["intent_category"]), x["quality_type"]))
item_list += result
# Create quality_changes group items

View file

@ -85,11 +85,20 @@ class VariantNode(ContainerNode):
for base_material, material_node in self.materials.items():
if self.machine.preferred_material == base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
return material_node
# First fallback: Choose any material with matching diameter.
# First fallback: Check if we should be checking for the 175 variant.
if approximate_diameter == 2:
preferred_material = self.machine.preferred_material + "_175"
for base_material, material_node in self.materials.items():
if preferred_material == base_material and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
return material_node
# Second fallback: Choose any material with matching diameter.
for material_node in self.materials.values():
if material_node.getMetaDataEntry("approximate_diameter") and approximate_diameter == int(material_node.getMetaDataEntry("approximate_diameter")):
Logger.log("w", "Could not find preferred material %s, falling back to whatever works", self.machine.preferred_material)
return material_node
fallback = next(iter(self.materials.values())) # Should only happen with empty material node.
Logger.log("w", "Could not find preferred material {preferred_material} with diameter {diameter} for variant {variant_id}, falling back to {fallback}.".format(
preferred_material = self.machine.preferred_material,

View file

@ -99,7 +99,7 @@ class AuthorizationHelpers:
})
except requests.exceptions.ConnectionError:
# Connection was suddenly dropped. Nothing we can do about that.
Logger.log("w", "Something failed while attempting to parse the JWT token")
Logger.logException("w", "Something failed while attempting to parse the JWT token")
return None
if token_request.status_code not in (200, 201):
Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text)

View file

@ -339,11 +339,11 @@ class ContainerManager(QObject):
# \return A list of names of materials with the same GUID.
@pyqtSlot("QVariant", bool, result = "QStringList")
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]:
same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(guid = material_node.guid)
same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(GUID = material_node.guid)
if exclude_self:
return [metadata["name"] for metadata in same_guid if metadata["base_file"] != material_node.base_file]
return list({meta["name"] for meta in same_guid if meta["base_file"] != material_node.base_file})
else:
return [metadata["name"] for metadata in same_guid]
return list({meta["name"] for meta in same_guid})
## 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.

View file

@ -3,11 +3,13 @@
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
import cura.CuraApplication
from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer
import cura.CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container
from UM.Settings.InstanceContainer import InstanceContainer
if TYPE_CHECKING:
from UM.Settings.InstanceContainer import InstanceContainer
@ -36,8 +38,12 @@ class IntentManager(QObject):
# \return A list of metadata dictionaries matching the search criteria, or
# an empty list if nothing was found.
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
material_node = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials[material_base_file]
intent_metadatas = []
intent_metadatas = [] # type: List[Dict[str, Any]]
materials = ContainerTree.getInstance().machines[definition_id].variants[nozzle_name].materials
if material_base_file not in materials:
return intent_metadatas
material_node = materials[material_base_file]
for quality_node in material_node.qualities.values():
for intent_node in quality_node.intents.values():
intent_metadatas.append(intent_node.getMetadata())

View file

@ -59,13 +59,15 @@ class Toolbox(QObject, Extension):
# The responses as given by the server parsed to a list.
self._server_response_data = {
"authors": [],
"packages": []
"packages": [],
"updates": [],
} # type: Dict[str, List[Any]]
# Models:
self._models = {
"authors": AuthorsModel(self),
"packages": PackagesModel(self),
"updates": PackagesModel(self),
} # type: Dict[str, Union[AuthorsModel, PackagesModel]]
self._plugins_showcase_model = PackagesModel(self)
@ -186,18 +188,24 @@ class Toolbox(QObject, Extension):
cloud_api_version = self._cloud_api_version,
sdk_version = self._sdk_version
)
# We need to construct a query like installed_packages=ID:VERSION&installed_packages=ID:VERSION, etc.
installed_package_ids_with_versions = [":".join(items) for items in
self._package_manager.getAllInstalledPackageIdsAndVersions()]
installed_packages_query = "&installed_packages=".join(installed_package_ids_with_versions)
self._request_urls = {
"authors": QUrl("{base_url}/authors".format(base_url = self._api_url)),
"packages": QUrl("{base_url}/packages".format(base_url = self._api_url))
"packages": QUrl("{base_url}/packages".format(base_url = self._api_url)),
"updates": QUrl("{base_url}/packages/package-updates?installed_packages={query}".format(
base_url = self._api_url, query = installed_packages_query))
}
# Request the latest and greatest!
self._fetchPackageData()
# On boot we check which packages have updates.
if len(installed_package_ids_with_versions) > 0:
self._fetchPackageUpdates()
def _fetchPackageData(self):
# Create the network manager:
# This was formerly its own function but really had no reason to be as
# it was never called more than once ever.
def _prepareNetworkManager(self):
if self._network_manager is not None:
self._network_manager.finished.disconnect(self._onRequestFinished)
self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccessibleChanged)
@ -205,10 +213,15 @@ class Toolbox(QObject, Extension):
self._network_manager.finished.connect(self._onRequestFinished)
self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccessibleChanged)
def _fetchPackageUpdates(self):
self._prepareNetworkManager()
self._makeRequestByType("updates")
def _fetchPackageData(self):
self._prepareNetworkManager()
# Make remote requests:
self._makeRequestByType("packages")
self._makeRequestByType("authors")
# Gather installed packages:
self._updateInstalledModels()
@ -630,6 +643,10 @@ class Toolbox(QObject, Extension):
elif response_type == "authors":
self._models[response_type].setFilter({"package_types": "material"})
self._models[response_type].setFilter({"tags": "generic"})
elif response_type == "updates":
# Tell the package manager that there's a new set of updates available.
packages = set([pkg["package_id"] for pkg in self._server_response_data[response_type]])
self._package_manager.setPackagesWithUpdate(packages)
self.metadataChanged.emit()

View file

@ -100,7 +100,6 @@ UM.Dialog
{
text: styleData.value
font: UM.Theme.getFont("system")
color: UM.Theme.getColor("setting_control_disabled_text")
}
}

View file

@ -46,7 +46,7 @@ TabView
{
return ""
}
return linkedMaterials.join(", ");
return linkedMaterials;
}
function getApproximateDiameter(diameter)

View file

@ -160,15 +160,25 @@ Item
enabled: model.description !== undefined
acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
onEntered:
Timer
{
base.showTooltip(
id: intentTooltipTimer
interval: 500
running: false
repeat: false
onTriggered: base.showTooltip(
intentCategoryLabel,
Qt.point(-(intentCategoryLabel.x - qualityRow.x) - UM.Theme.getSize("thick_margin").width, 0),
model.description
)
}
onExited: base.hideTooltip()
onEntered: intentTooltipTimer.start()
onExited:
{
base.hideTooltip()
intentTooltipTimer.stop()
}
}
NoIntentIcon // This icon has hover priority over intentDescriptionHoverArea, so draw it above it.

View file

@ -139,6 +139,7 @@ def test_preferredMaterialExactMatch(empty_variant_node):
"some_different_material": MagicMock(getMetaDataEntry = lambda x: 3),
"preferred_material_base_file": MagicMock(getMetaDataEntry = lambda x: 3) # Exact match.
}
empty_variant_node.machine.preferred_material = "preferred_material_base_file"
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_base_file"], "It should match exactly on this one since it's the preferred material."
@ -149,6 +150,7 @@ def test_preferredMaterialSubmaterial(empty_variant_node):
"some_different_material": MagicMock(getMetaDataEntry = lambda x: 3),
"preferred_material_base_file_aa04": MagicMock(getMetaDataEntry = lambda x: 3) # This is a submaterial of the preferred material.
}
empty_variant_node.machine.preferred_material = "preferred_material_base_file_aa04"
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_base_file_aa04"], "It should match on the submaterial just as well."
@ -160,6 +162,7 @@ def test_preferredMaterialDiameter(empty_variant_node):
"preferred_material_wrong_diameter": MagicMock(getMetaDataEntry = lambda x: 2), # Approximate diameter is 2 instead of 3.
"preferred_material_correct_diameter": MagicMock(getMetaDataEntry = lambda x: 3) # Correct approximate diameter.
}
empty_variant_node.machine.preferred_material = "preferred_material_correct_diameter"
assert empty_variant_node.preferredMaterial(approximate_diameter = 3) == empty_variant_node.materials["preferred_material_correct_diameter"], "It should match only on the material with correct diameter."