mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 04:39:02 +08:00
Merge branch 'main' into PP-108_Improved_support_settings
This commit is contained in:
commit
8a9c202aef
@ -5,13 +5,13 @@ import os
|
||||
from typing import Any, cast, Dict, List, Set, Tuple, TYPE_CHECKING, Optional
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.CuraApplication import CuraApplication # To find some resource types.
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
from UM.PackageManager import PackageManager # The class we're extending.
|
||||
from UM.Resources import Resources # To find storage paths for some resource types.
|
||||
from UM.i18n import i18nCatalog
|
||||
from plugins.XmlMaterialProfile.XmlMaterialProfile import XmlMaterialProfile
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
@ -68,7 +68,8 @@ class CuraPackageManager(PackageManager):
|
||||
with open(root + "/" + file_name, encoding="utf-8") as f:
|
||||
# Make sure the file we found has the same guid as our material
|
||||
# Parsing this xml would be better but the namespace is needed to search it.
|
||||
parsed_guid = XmlMaterialProfile.getMetadataFromSerialized(f.read(), "GUID")
|
||||
parsed_guid = PluginRegistry.getInstance().getPluginObject("XmlMaterialProfile").getMetadataFromSerialized(
|
||||
f.read(), "GUID")
|
||||
if guid == parsed_guid:
|
||||
return package_id
|
||||
|
||||
|
@ -23,6 +23,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
from cura.CuraPackageManager import CuraPackageManager
|
||||
|
||||
from cura.Machines.ContainerTree import ContainerTree
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
@ -579,6 +580,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
is_printer_group = True
|
||||
machine_name = group_name
|
||||
|
||||
# Getting missing required package ids
|
||||
package_metadata = self._parse_packages_metadata(archive)
|
||||
missing_package_metadata = self._filter_missing_package_metadata(package_metadata)
|
||||
|
||||
# Show the dialog, informing the user what is about to happen.
|
||||
self._dialog.setMachineConflict(machine_conflict)
|
||||
self._dialog.setIsPrinterGroup(is_printer_group)
|
||||
@ -599,6 +604,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
self._dialog.setExtruders(extruders)
|
||||
self._dialog.setVariantType(variant_type_name)
|
||||
self._dialog.setHasObjectsOnPlate(Application.getInstance().platformActivity)
|
||||
self._dialog.setMissingPackagesMetadata(missing_package_metadata)
|
||||
self._dialog.show()
|
||||
|
||||
# Block until the dialog is closed.
|
||||
@ -1243,3 +1249,26 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
metadata = data.iterfind("./um:metadata/um:name/um:label", {"um": "http://www.ultimaker.com/material"})
|
||||
for entry in metadata:
|
||||
return entry.text
|
||||
|
||||
@staticmethod
|
||||
def _parse_packages_metadata(archive: zipfile.ZipFile) -> List[Dict[str, str]]:
|
||||
try:
|
||||
package_metadata = json.loads(archive.open("Metadata/packages.json").read().decode("utf-8"))
|
||||
return package_metadata["packages"]
|
||||
except Exception:
|
||||
Logger.error("Failed to load packes metadata from .3mf file")
|
||||
return []
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _filter_missing_package_metadata(package_metadata: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
||||
"""Filters out installed packages from package_metadata"""
|
||||
missing_packages = []
|
||||
package_manager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager())
|
||||
|
||||
for package in package_metadata:
|
||||
package_id = package["id"]
|
||||
if not package_manager.isPackageInstalled(package_id):
|
||||
missing_packages.append(package)
|
||||
|
||||
return missing_packages
|
||||
|
@ -2,14 +2,18 @@
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import List, Optional, Dict, cast
|
||||
|
||||
from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication
|
||||
from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication, QUrl
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Application import Application
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from plugins.Marketplace.InstallMissingPackagesDialog import InstallMissingPackageDialog
|
||||
from .UpdatableMachinesModel import UpdatableMachinesModel
|
||||
from UM.Message import Message
|
||||
|
||||
import os
|
||||
import threading
|
||||
@ -23,7 +27,7 @@ i18n_catalog = i18nCatalog("cura")
|
||||
class WorkspaceDialog(QObject):
|
||||
showDialogSignal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._component = None
|
||||
self._context = None
|
||||
@ -59,6 +63,9 @@ class WorkspaceDialog(QObject):
|
||||
self._objects_on_plate = False
|
||||
self._is_printer_group = False
|
||||
self._updatable_machines_model = UpdatableMachinesModel(self)
|
||||
self._missing_package_metadata: List[Dict[str, str]] = []
|
||||
self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry()
|
||||
self._install_missing_package_dialog: Optional[QObject] = None
|
||||
|
||||
machineConflictChanged = pyqtSignal()
|
||||
qualityChangesConflictChanged = pyqtSignal()
|
||||
@ -79,6 +86,7 @@ class WorkspaceDialog(QObject):
|
||||
variantTypeChanged = pyqtSignal()
|
||||
extrudersChanged = pyqtSignal()
|
||||
isPrinterGroupChanged = pyqtSignal()
|
||||
missingPackagesChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify = isPrinterGroupChanged)
|
||||
def isPrinterGroup(self) -> bool:
|
||||
@ -274,6 +282,19 @@ class WorkspaceDialog(QObject):
|
||||
self._has_quality_changes_conflict = quality_changes_conflict
|
||||
self.qualityChangesConflictChanged.emit()
|
||||
|
||||
def setMissingPackagesMetadata(self, missing_package_metadata: List[Dict[str, str]]) -> None:
|
||||
self._missing_package_metadata = missing_package_metadata
|
||||
self.missingPackagesChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariantList", notify=missingPackagesChanged)
|
||||
def missingPackages(self) -> List[Dict[str, str]]:
|
||||
return self._missing_package_metadata
|
||||
|
||||
@pyqtSlot()
|
||||
def installMissingPackages(self) -> None:
|
||||
self._install_missing_package_dialog = InstallMissingPackageDialog(self._missing_package_metadata, self.showMissingMaterialsWarning)
|
||||
self._install_missing_package_dialog.show()
|
||||
|
||||
def getResult(self) -> Dict[str, Optional[str]]:
|
||||
if "machine" in self._result and self.updatableMachinesModel.count <= 1:
|
||||
self._result["machine"] = None
|
||||
@ -360,6 +381,41 @@ class WorkspaceDialog(QObject):
|
||||
time.sleep(1 / 50)
|
||||
QCoreApplication.processEvents() # Ensure that the GUI does not freeze.
|
||||
|
||||
@pyqtSlot()
|
||||
def showMissingMaterialsWarning(self) -> None:
|
||||
result_message = Message(
|
||||
i18n_catalog.i18nc("@info:status", "The material used in this project relies on some material definitions not available in Cura, this might produce undesirable print results. We highly recommend installing the full material package from the Marketplace."),
|
||||
lifetime=0,
|
||||
title=i18n_catalog.i18nc("@info:title", "Material profiles not installed"),
|
||||
message_type=Message.MessageType.WARNING
|
||||
)
|
||||
result_message.addAction(
|
||||
"learn_more",
|
||||
name=i18n_catalog.i18nc("@action:button", "Learn more"),
|
||||
icon="",
|
||||
description="Learn more about project materials.",
|
||||
button_align=Message.ActionButtonAlignment.ALIGN_LEFT,
|
||||
button_style=Message.ActionButtonStyle.LINK
|
||||
)
|
||||
result_message.addAction(
|
||||
"install_materials",
|
||||
name=i18n_catalog.i18nc("@action:button", "Install Materials"),
|
||||
icon="",
|
||||
description="Install missing materials from project file.",
|
||||
button_align=Message.ActionButtonAlignment.ALIGN_RIGHT,
|
||||
button_style=Message.ActionButtonStyle.DEFAULT
|
||||
)
|
||||
result_message.actionTriggered.connect(self._onMessageActionTriggered)
|
||||
result_message.show()
|
||||
|
||||
def _onMessageActionTriggered(self, message: Message, sync_message_action: str) -> None:
|
||||
if sync_message_action == "install_materials":
|
||||
self.installMissingPackages()
|
||||
message.hide()
|
||||
elif sync_message_action == "learn_more":
|
||||
QDesktopServices.openUrl(QUrl("https://support.ultimaker.com/hc/en-us/articles/360011968360-Using-the-Ultimaker-Marketplace"))
|
||||
|
||||
|
||||
def __show(self) -> None:
|
||||
if self._view is None:
|
||||
self._createViewFromQML()
|
||||
|
@ -17,7 +17,7 @@ UM.Dialog
|
||||
minimumWidth: UM.Theme.getSize("popup_dialog").width
|
||||
minimumHeight: UM.Theme.getSize("popup_dialog").height
|
||||
width: minimumWidth
|
||||
|
||||
margin: UM.Theme.getSize("default_margin").width
|
||||
property int comboboxHeight: UM.Theme.getSize("default_margin").height
|
||||
|
||||
onClosing: manager.notifyClosed()
|
||||
@ -31,337 +31,220 @@ UM.Dialog
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
Flickable
|
||||
{
|
||||
id: dialogSummaryItem
|
||||
clip: true
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
anchors.margins: 10 * screenScaleFactor
|
||||
height: parent.height
|
||||
contentHeight: dialogSummaryItem.height
|
||||
ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar }
|
||||
|
||||
UM.I18nCatalog
|
||||
Item
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
ListModel
|
||||
{
|
||||
id: resolveStrategiesModel
|
||||
// Instead of directly adding the list elements, we add them afterwards.
|
||||
// This is because it's impossible to use setting function results to be bound to listElement properties directly.
|
||||
// See http://stackoverflow.com/questions/7659442/listelement-fields-as-properties
|
||||
Component.onCompleted:
|
||||
{
|
||||
append({"key": "override", "label": catalog.i18nc("@action:ComboBox Update/override existing profile", "Update existing")});
|
||||
append({"key": "new", "label": catalog.i18nc("@action:ComboBox Save settings in a new profile", "Create new")});
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
id: dialogSummaryItem
|
||||
width: verticalScrollBar.visible ? parent.width - verticalScrollBar.width - UM.Theme.getSize("default_margin").width : parent.width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
anchors.margins: 10 * screenScaleFactor
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
|
||||
ListModel
|
||||
{
|
||||
id: resolveStrategiesModel
|
||||
// Instead of directly adding the list elements, we add them afterwards.
|
||||
// This is because it's impossible to use setting function results to be bound to listElement properties directly.
|
||||
// See http://stackoverflow.com/questions/7659442/listelement-fields-as-properties
|
||||
Component.onCompleted:
|
||||
{
|
||||
append({"key": "override", "label": catalog.i18nc("@action:ComboBox Update/override existing profile", "Update existing")});
|
||||
append({"key": "new", "label": catalog.i18nc("@action:ComboBox Save settings in a new profile", "Create new")});
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
UM.Label
|
||||
Column
|
||||
{
|
||||
id: titleLabel
|
||||
text: catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: separator
|
||||
color: UM.Theme.getColor("text")
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
}
|
||||
}
|
||||
height: childrenRect.height
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: machineResolveStrategyTooltip
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: base.visible && machineResolveComboBox.model.count > 1
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
|
||||
Cura.ComboBox
|
||||
UM.Label
|
||||
{
|
||||
id: machineResolveComboBox
|
||||
model: manager.updatableMachinesModel
|
||||
visible: machineResolveStrategyTooltip.visible
|
||||
textRole: "displayName"
|
||||
id: titleLabel
|
||||
text: catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: separator
|
||||
color: UM.Theme.getColor("text")
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onCurrentIndexChanged:
|
||||
{
|
||||
if (model.getItem(currentIndex).id == "new"
|
||||
&& model.getItem(currentIndex).type == "default_option")
|
||||
{
|
||||
manager.setResolveStrategy("machine", "new")
|
||||
}
|
||||
else
|
||||
{
|
||||
manager.setResolveStrategy("machine", "override")
|
||||
manager.setMachineToOverride(model.getItem(currentIndex).id)
|
||||
}
|
||||
}
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (!visible) {return}
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
currentIndex = 0
|
||||
// If the project printer exists in Cura, set it as the default dropdown menu option.
|
||||
// No need to check object 0, which is the "Create new" option
|
||||
for (var i = 1; i < model.count; i++)
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: machineResolveStrategyTooltip
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: base.visible && machineResolveComboBox.model.count > 1
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
id: machineResolveComboBox
|
||||
model: manager.updatableMachinesModel
|
||||
visible: machineResolveStrategyTooltip.visible
|
||||
textRole: "displayName"
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onCurrentIndexChanged:
|
||||
{
|
||||
if (model.getItem(i).name == manager.machineName)
|
||||
if (model.getItem(currentIndex).id == "new"
|
||||
&& model.getItem(currentIndex).type == "default_option")
|
||||
{
|
||||
currentIndex = i
|
||||
break
|
||||
manager.setResolveStrategy("machine", "new")
|
||||
}
|
||||
else
|
||||
{
|
||||
manager.setResolveStrategy("machine", "override")
|
||||
manager.setMachineToOverride(model.getItem(currentIndex).id)
|
||||
}
|
||||
}
|
||||
// The project printer does not exist in Cura. If there is at least one printer of the same
|
||||
// type, select the first one, else set the index to "Create new"
|
||||
if (currentIndex == 0 && model.count > 1)
|
||||
|
||||
onVisibleChanged:
|
||||
{
|
||||
currentIndex = 1
|
||||
if (!visible) {return}
|
||||
|
||||
currentIndex = 0
|
||||
// If the project printer exists in Cura, set it as the default dropdown menu option.
|
||||
// No need to check object 0, which is the "Create new" option
|
||||
for (var i = 1; i < model.count; i++)
|
||||
{
|
||||
if (model.getItem(i).name == manager.machineName)
|
||||
{
|
||||
currentIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
// The project printer does not exist in Cura. If there is at least one printer of the same
|
||||
// type, select the first one, else set the index to "Create new"
|
||||
if (currentIndex == 0 && model.count > 1)
|
||||
{
|
||||
currentIndex = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
id: printer_settings_label
|
||||
text: catalog.i18nc("@action:label", "Printer settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Row
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Type")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineType
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.qualityChangesConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: qualityChangesResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Profile settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.qualityName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Intent")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.intentName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Not in profile")
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings)
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Derivative from")
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges)
|
||||
width: (parent.width / 3) | 0
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: materialResolveTooltip
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.materialConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the material be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: materialResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("material", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Row
|
||||
{
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("narrow_margin").width
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Material settings")
|
||||
id: printer_settings_label
|
||||
text: catalog.i18nc("@action:label", "Printer settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.materialLabels
|
||||
delegate: Row
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Type")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineType
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.qualityChangesConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: qualityChangesResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Profile settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
@ -369,76 +252,250 @@ UM.Dialog
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: modelData
|
||||
text: manager.qualityName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Intent")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.intentName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Not in profile")
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings)
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Derivative from")
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges)
|
||||
width: (parent.width / 3) | 0
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
Item
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Setting visibility")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: materialResolveTooltip
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.materialConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the material be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: materialResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("material", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Row
|
||||
{
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("narrow_margin").width
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Material settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.materialLabels
|
||||
delegate: Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: modelData
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Setting visibility")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Mode")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.activeMode
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
visible: manager.hasVisibleSettingsField
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Visible settings:")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings)
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
UM.Label
|
||||
visible: manager.hasObjectsOnPlate
|
||||
UM.ColorImage
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Mode")
|
||||
width: (parent.width / 3) | 0
|
||||
width: warningLabel.height
|
||||
height: width
|
||||
source: UM.Theme.getIcon("Information")
|
||||
color: UM.Theme.getColor("text")
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.activeMode
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
visible: manager.hasVisibleSettingsField
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Visible settings:")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings)
|
||||
width: (parent.width / 3) | 0
|
||||
id: warningLabel
|
||||
text: catalog.i18nc("@action:warning", "Loading a project will clear all models on the build plate.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
property bool warning: manager.missingPackages.length > 0
|
||||
|
||||
footerComponent: Rectangle
|
||||
{
|
||||
color: warning ? UM.Theme.getColor("warning") : "transparent"
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: childrenRect.height + 2 * base.margin
|
||||
|
||||
Column
|
||||
{
|
||||
height: childrenRect.height
|
||||
spacing: base.margin
|
||||
|
||||
anchors.margins: base.margin
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
|
||||
RowLayout
|
||||
{
|
||||
width: parent.width
|
||||
id: warningRow
|
||||
height: childrenRect.height
|
||||
visible: manager.hasObjectsOnPlate
|
||||
visible: warning
|
||||
spacing: base.margin
|
||||
UM.ColorImage
|
||||
{
|
||||
width: warningLabel.height
|
||||
height: width
|
||||
source: UM.Theme.getIcon("Information")
|
||||
color: UM.Theme.getColor("text")
|
||||
width: UM.Theme.getSize("extruder_icon").width
|
||||
height: UM.Theme.getSize("extruder_icon").height
|
||||
source: UM.Theme.getIcon("Warning")
|
||||
}
|
||||
|
||||
UM.Label
|
||||
{
|
||||
id: warningLabel
|
||||
text: catalog.i18nc("@action:warning", "Loading a project will clear all models on the build plate.")
|
||||
id: warningText
|
||||
text: "The material used in this project is currently not installed in Cura.<br/>Install the material profile and reopen the project."
|
||||
}
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
sourceComponent: buttonRow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,13 +504,30 @@ UM.Dialog
|
||||
rightButtons: [
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
visible: !warning
|
||||
text: catalog.i18nc("@action:button", "Cancel")
|
||||
onClicked: reject()
|
||||
},
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
visible: !warning
|
||||
text: catalog.i18nc("@action:button", "Open")
|
||||
onClicked: accept()
|
||||
},
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
visible: warning
|
||||
text: catalog.i18nc("@action:button", "Open project anyway")
|
||||
onClicked: {
|
||||
manager.showMissingMaterialsWarning();
|
||||
accept();
|
||||
}
|
||||
},
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
visible: warning
|
||||
text: catalog.i18nc("@action:button", "Install missing material")
|
||||
onClicked: manager.installMissingPackages()
|
||||
}
|
||||
]
|
||||
|
||||
|
66
plugins/Marketplace/InstallMissingPackagesDialog.py
Normal file
66
plugins/Marketplace/InstallMissingPackagesDialog.py
Normal file
@ -0,0 +1,66 @@
|
||||
import os
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty, QUrl
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
from typing import Optional, List, Dict, cast, Callable
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
from plugins.Marketplace.MissingPackageList import MissingPackageList
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
class InstallMissingPackageDialog(QObject):
|
||||
"""Dialog used to display packages that need to be installed to load 3mf file materials"""
|
||||
def __init__(self, packages_metadata: List[Dict[str, str]], show_missing_materials_warning: Callable[[], None]) -> None:
|
||||
"""Initialize
|
||||
|
||||
:param packages_metadata: List of dictionaries containing information about missing packages.
|
||||
"""
|
||||
super().__init__()
|
||||
|
||||
self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry()
|
||||
self._package_manager: CuraPackageManager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager())
|
||||
self._package_manager.installedPackagesChanged.connect(self.checkIfRestartNeeded)
|
||||
|
||||
self._dialog: Optional[QObject] = None
|
||||
self._restart_needed = False
|
||||
self._package_metadata: List[Dict[str, str]] = packages_metadata
|
||||
|
||||
self._package_model: MissingPackageList = MissingPackageList(packages_metadata)
|
||||
self._show_missing_materials_warning = show_missing_materials_warning
|
||||
|
||||
def show(self) -> None:
|
||||
plugin_path = self._plugin_registry.getPluginPath("Marketplace")
|
||||
if plugin_path is None:
|
||||
plugin_path = os.path.dirname(__file__)
|
||||
|
||||
# create a QML component for the license dialog
|
||||
license_dialog_component_path = os.path.join(plugin_path, "resources", "qml", "InstallMissingPackagesDialog.qml")
|
||||
self._dialog = CuraApplication.getInstance().createQmlComponent(license_dialog_component_path, {"manager": self})
|
||||
self._dialog.show()
|
||||
|
||||
def checkIfRestartNeeded(self) -> None:
|
||||
if self._dialog is None:
|
||||
return
|
||||
|
||||
self._restart_needed = self._package_manager.hasPackagesToRemoveOrInstall
|
||||
self.showRestartChanged.emit()
|
||||
|
||||
showRestartChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=showRestartChanged)
|
||||
def showRestartNotification(self) -> bool:
|
||||
return self._restart_needed
|
||||
|
||||
@pyqtProperty(QObject)
|
||||
def model(self) -> MissingPackageList:
|
||||
return self._package_model
|
||||
|
||||
@pyqtSlot()
|
||||
def showMissingMaterialsWarning(self) -> None:
|
||||
self._show_missing_materials_warning()
|
@ -103,6 +103,9 @@ class Marketplace(Extension, QObject):
|
||||
self.setTabShown(1)
|
||||
|
||||
def checkIfRestartNeeded(self) -> None:
|
||||
if self._window is None:
|
||||
return
|
||||
|
||||
if self._package_manager.hasPackagesToRemoveOrInstall or \
|
||||
cast(PluginRegistry, self._plugin_registry).getCurrentSessionActivationChangedPlugins():
|
||||
self._restart_needed = True
|
||||
|
46
plugins/Marketplace/MissingPackageList.py
Normal file
46
plugins/Marketplace/MissingPackageList.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional, TYPE_CHECKING, Dict, List
|
||||
|
||||
from .Constants import PACKAGES_URL
|
||||
from .PackageModel import PackageModel
|
||||
from .RemotePackageList import RemotePackageList
|
||||
from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication
|
||||
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To request the package list from the API.
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
class MissingPackageList(RemotePackageList):
|
||||
def __init__(self, packages_metadata: List[Dict[str, str]], parent: Optional["QObject"] = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._packages_metadata: List[Dict[str, str]] = packages_metadata
|
||||
self._package_type_filter = "material"
|
||||
self._search_type = "package_ids"
|
||||
self._requested_search_string = ",".join(map(lambda package: package["id"], packages_metadata))
|
||||
|
||||
def _parseResponse(self, reply: "QNetworkReply") -> None:
|
||||
super()._parseResponse(reply)
|
||||
|
||||
# At the end of the list we want to show some information about packages the user is missing that can't be found
|
||||
# This will add cards with some information about the missing packages
|
||||
if not self.hasMore:
|
||||
self._addPackagesMissingFromRequest()
|
||||
|
||||
def _addPackagesMissingFromRequest(self) -> None:
|
||||
"""Create cards for packages the user needs to install that could not be found"""
|
||||
returned_packages_ids = [item["package"].packageId for item in self._items]
|
||||
|
||||
for package_metadata in self._packages_metadata:
|
||||
if package_metadata["id"] not in returned_packages_ids:
|
||||
package = PackageModel.fromIncompletePackageInformation(package_metadata["display_name"], package_metadata["package_version"], self._package_type_filter)
|
||||
self.appendItem({"package": package})
|
||||
|
||||
self.itemsChanged.emit()
|
||||
|
||||
|
@ -84,6 +84,20 @@ class PackageModel(QObject):
|
||||
|
||||
self._is_busy = False
|
||||
|
||||
self._is_missing_package_information = False
|
||||
|
||||
@classmethod
|
||||
def fromIncompletePackageInformation(cls, display_name: str, package_version: str, package_type: str) -> "PackageModel":
|
||||
package_data = {
|
||||
"display_name": display_name,
|
||||
"package_version": package_version,
|
||||
"package_type": package_type,
|
||||
"description": "The material package associated with the Cura project could not be found on the Ultimaker marketplace. Use the partial material profile definition stored in the Cura project file at your own risk."
|
||||
}
|
||||
package_model = cls(package_data)
|
||||
package_model.setIsMissingPackageInformation(True)
|
||||
return package_model
|
||||
|
||||
@pyqtSlot()
|
||||
def _processUpdatedPackages(self):
|
||||
self.setCanUpdate(self._package_manager.checkIfPackageCanUpdate(self._package_id))
|
||||
@ -385,3 +399,14 @@ class PackageModel(QObject):
|
||||
def canUpdate(self) -> bool:
|
||||
"""Flag indicating if the package can be updated"""
|
||||
return self._can_update
|
||||
|
||||
isMissingPackageInformationChanged = pyqtSignal()
|
||||
|
||||
def setIsMissingPackageInformation(self, isMissingPackageInformation: bool) -> None:
|
||||
self._is_missing_package_information = isMissingPackageInformation
|
||||
self.isMissingPackageInformationChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify=isMissingPackageInformationChanged)
|
||||
def isMissingPackageInformation(self) -> bool:
|
||||
"""Flag indicating if the package can be updated"""
|
||||
return self._is_missing_package_information
|
||||
|
@ -28,6 +28,7 @@ class RemotePackageList(PackageList):
|
||||
self._package_type_filter = ""
|
||||
self._requested_search_string = ""
|
||||
self._current_search_string = ""
|
||||
self._search_type = "search"
|
||||
self._request_url = self._initialRequestUrl()
|
||||
self._ongoing_requests["get_packages"] = None
|
||||
self.isLoadingChanged.connect(self._onLoadingChanged)
|
||||
@ -100,7 +101,7 @@ class RemotePackageList(PackageList):
|
||||
if self._package_type_filter != "":
|
||||
request_url += f"&package_type={self._package_type_filter}"
|
||||
if self._current_search_string != "":
|
||||
request_url += f"&search={self._current_search_string}"
|
||||
request_url += f"&{self._search_type}={self._current_search_string}"
|
||||
return request_url
|
||||
|
||||
def _parseResponse(self, reply: "QNetworkReply") -> None:
|
||||
|
@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import UM 1.5 as UM
|
||||
import Cura 1.6 as Cura
|
||||
|
||||
Marketplace
|
||||
{
|
||||
modality: Qt.ApplicationModal
|
||||
title: catalog.i18nc("@title", "Install missing Materials")
|
||||
pageContentsSource: "MissingPackages.qml"
|
||||
showSearchHeader: false
|
||||
showOnboadBanner: false
|
||||
|
||||
onClosing: manager.showMissingMaterialsWarning()
|
||||
}
|
@ -16,6 +16,10 @@ Window
|
||||
|
||||
signal searchStringChanged(string new_search)
|
||||
|
||||
property alias showOnboadBanner: onBoardBanner.visible
|
||||
property alias showSearchHeader: searchHeader.visible
|
||||
property alias pageContentsSource: content.source
|
||||
|
||||
minimumWidth: UM.Theme.getSize("modal_window_minimum").width
|
||||
minimumHeight: UM.Theme.getSize("modal_window_minimum").height
|
||||
width: minimumWidth
|
||||
@ -86,6 +90,7 @@ Window
|
||||
|
||||
OnboardBanner
|
||||
{
|
||||
id: onBoardBanner
|
||||
visible: content.item && content.item.bannerVisible
|
||||
text: content.item && content.item.bannerText
|
||||
icon: content.item && content.item.bannerIcon
|
||||
@ -100,6 +105,7 @@ Window
|
||||
// Search & Top-Level Tabs
|
||||
Item
|
||||
{
|
||||
id: searchHeader
|
||||
implicitHeight: childrenRect.height
|
||||
implicitWidth: parent.width - 2 * UM.Theme.getSize("default_margin").width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@ -186,7 +192,7 @@ Window
|
||||
{
|
||||
text: catalog.i18nc("@info", "Search in the browser")
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
visible: pageSelectionTabBar.currentItem.hasSearch
|
||||
visible: pageSelectionTabBar.currentItem.hasSearch && searchHeader.visible
|
||||
isIconOnRightSide: true
|
||||
height: fontMetrics.height
|
||||
textFont: fontMetrics.font
|
||||
|
15
plugins/Marketplace/resources/qml/MissingPackages.qml
Normal file
15
plugins/Marketplace/resources/qml/MissingPackages.qml
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import UM 1.4 as UM
|
||||
|
||||
Packages
|
||||
{
|
||||
pageTitle: catalog.i18nc("@header", "Install Materials")
|
||||
|
||||
bannerVisible: false
|
||||
showUpdateButton: false
|
||||
showInstallButton: true
|
||||
|
||||
model: manager.model
|
||||
}
|
@ -18,6 +18,8 @@ Rectangle
|
||||
height: childrenRect.height
|
||||
color: UM.Theme.getColor("main_background")
|
||||
radius: UM.Theme.getSize("default_radius").width
|
||||
border.color: packageData.isMissingPackageInformation ? UM.Theme.getColor("warning") : "transparent"
|
||||
border.width: packageData.isMissingPackageInformation ? UM.Theme.getSize("default_lining").width : 0
|
||||
|
||||
PackageCardHeader
|
||||
{
|
||||
|
@ -19,6 +19,8 @@ Item
|
||||
property bool showInstallButton: false
|
||||
property bool showUpdateButton: false
|
||||
|
||||
property string missingPackageReadMoreUrl: "https://ultimaker.atlassian.net/wiki/spaces/SD/pages/1231916580/Campaign+links+from+Cura+to+the+Ultimaker+domain"
|
||||
|
||||
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("card").height
|
||||
@ -87,6 +89,14 @@ Item
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
|
||||
UM.StatusIcon
|
||||
{
|
||||
width: UM.Theme.getSize("section_icon").width + UM.Theme.getSize("narrow_margin").width
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
status: UM.StatusIcon.Status.WARNING
|
||||
visible: packageData.isMissingPackageInformation
|
||||
}
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: packageData.displayName
|
||||
@ -109,6 +119,7 @@ Item
|
||||
Button
|
||||
{
|
||||
id: externalLinkButton
|
||||
visible: !packageData.isMissingPackageInformation
|
||||
|
||||
// For some reason if i set padding, they don't match up. If i set all of them explicitly, it does work?
|
||||
leftPadding: UM.Theme.getSize("narrow_margin").width
|
||||
@ -155,6 +166,7 @@ Item
|
||||
UM.Label
|
||||
{
|
||||
id: authorBy
|
||||
visible: !packageData.isMissingPackageInformation
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
|
||||
text: catalog.i18nc("@label Is followed by the name of an author", "By")
|
||||
@ -165,6 +177,7 @@ Item
|
||||
// clickable author name
|
||||
Item
|
||||
{
|
||||
visible: !packageData.isMissingPackageInformation
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: authorBy.height
|
||||
Layout.alignment: Qt.AlignTop
|
||||
@ -182,10 +195,29 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
visible: packageData.isMissingPackageInformation
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: readMoreButton.height
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
id: readMoreButton
|
||||
text: catalog.i18nc("@button:label", "Learn More")
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
isIconOnRightSide: true
|
||||
|
||||
onClicked: Qt.openUrlExternally(missingPackageReadMoreUrl)
|
||||
}
|
||||
}
|
||||
|
||||
ManageButton
|
||||
{
|
||||
id: enableManageButton
|
||||
visible: showDisableButton && packageData.isInstalled && !packageData.isToBeInstalled && packageData.packageType != "material"
|
||||
visible: showDisableButton && packageData.isInstalled && !packageData.isToBeInstalled && packageData.packageType != "material" && !packageData.isMissingPackageInformation
|
||||
enabled: !packageData.busy
|
||||
|
||||
button_style: !packageData.isActive
|
||||
@ -199,7 +231,7 @@ Item
|
||||
ManageButton
|
||||
{
|
||||
id: installManageButton
|
||||
visible: showInstallButton && (packageData.canDowngrade || !packageData.isBundled)
|
||||
visible: showInstallButton && (packageData.canDowngrade || !packageData.isBundled) && !packageData.isMissingPackageInformation
|
||||
enabled: !packageData.busy
|
||||
busy: packageData.busy
|
||||
button_style: !(packageData.isInstalled || packageData.isToBeInstalled)
|
||||
@ -229,7 +261,7 @@ Item
|
||||
ManageButton
|
||||
{
|
||||
id: updateManageButton
|
||||
visible: showUpdateButton && packageData.canUpdate
|
||||
visible: showUpdateButton && packageData.canUpdate && !packageData.isMissingPackageInformation
|
||||
enabled: !packageData.busy
|
||||
busy: packageData.busy
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
@ -62,8 +62,11 @@ ListView
|
||||
hoverEnabled: true
|
||||
onClicked:
|
||||
{
|
||||
packages.selectedPackage = model.package;
|
||||
contextStack.push(packageDetailsComponent);
|
||||
if (!model.package.isMissingPackageInformation)
|
||||
{
|
||||
packages.selectedPackage = model.package;
|
||||
contextStack.push(packageDetailsComponent);
|
||||
}
|
||||
}
|
||||
|
||||
PackageCard
|
||||
|
@ -169,7 +169,10 @@ class ClusterApiClient:
|
||||
"""
|
||||
|
||||
def parse() -> None:
|
||||
self._anti_gc_callbacks.remove(parse)
|
||||
try:
|
||||
self._anti_gc_callbacks.remove(parse)
|
||||
except ValueError: # Already removed asynchronously.
|
||||
return # Then the rest of the function is also already executed.
|
||||
|
||||
# Don't try to parse the reply if we didn't get one
|
||||
if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) is None:
|
||||
|
@ -29,7 +29,7 @@ UM.Dialog
|
||||
// the size of the dialog ourselves.
|
||||
// Ugly workaround for windows having overlapping elements due to incorrect dialog width
|
||||
minimumWidth: content.width + (Qt.platform.os == "windows" ? 4 * margin : 2 * margin)
|
||||
minimumHeight: content.height + buttonArea.height + (Qt.platform.os == "windows" ? 5 * margin : 3 * margin)
|
||||
minimumHeight: content.height + footer.height + (Qt.platform.os == "windows" ? 5 * margin : 3 * margin)
|
||||
|
||||
property alias color: colorInput.text
|
||||
property var swatchColors: [
|
||||
|
Loading…
x
Reference in New Issue
Block a user