mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-09 06:45:09 -06:00
Merge branch 'master' into upgrade_to_2.4
This commit is contained in:
commit
13af609830
12 changed files with 14160 additions and 91 deletions
|
@ -12,6 +12,8 @@ from UM.Job import Job
|
|||
from UM.Preferences import Preferences
|
||||
from .WorkspaceDialog import WorkspaceDialog
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
import zipfile
|
||||
|
@ -52,7 +54,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
else:
|
||||
Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace")
|
||||
return WorkspaceReader.PreReadResult.failed
|
||||
|
||||
machine_name = ""
|
||||
# Check if there are any conflicts, so we can ask the user.
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
||||
|
@ -62,16 +64,19 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
quality_changes_conflict = False
|
||||
for container_stack_file in container_stack_files:
|
||||
container_id = self._stripFileToId(container_stack_file)
|
||||
serialized = archive.open(container_stack_file).read().decode("utf-8")
|
||||
if machine_name == "":
|
||||
machine_name = self._getMachineNameFromSerializedStack(serialized)
|
||||
stacks = self._container_registry.findContainerStacks(id=container_id)
|
||||
if stacks:
|
||||
# Check if there are any changes at all in any of the container stacks.
|
||||
id_list = self._getContainerIdListFromSerialized(archive.open(container_stack_file).read().decode("utf-8"))
|
||||
id_list = self._getContainerIdListFromSerialized(serialized)
|
||||
for index, container_id in enumerate(id_list):
|
||||
if stacks[0].getContainer(index).getId() != container_id:
|
||||
machine_conflict = True
|
||||
break
|
||||
Job.yieldThread()
|
||||
|
||||
material_labels = []
|
||||
material_conflict = False
|
||||
xml_material_profile = self._getXmlProfileClass()
|
||||
if self._material_container_suffix is None:
|
||||
|
@ -81,12 +86,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
for material_container_file in material_container_files:
|
||||
container_id = self._stripFileToId(material_container_file)
|
||||
materials = self._container_registry.findInstanceContainers(id=container_id)
|
||||
material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8")))
|
||||
if materials and not materials[0].isReadOnly(): # Only non readonly materials can be in conflict
|
||||
material_conflict = True
|
||||
Job.yieldThread()
|
||||
|
||||
# Check if any quality_changes instance container is in conflict.
|
||||
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
|
||||
quality_name = ""
|
||||
quality_type = ""
|
||||
num_settings_overriden_by_quality_changes = 0 # How many settings are changed by the quality changes
|
||||
for instance_container_file in instance_container_files:
|
||||
container_id = self._stripFileToId(instance_container_file)
|
||||
instance_container = InstanceContainer(container_id)
|
||||
|
@ -95,35 +103,58 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
|
||||
container_type = instance_container.getMetaDataEntry("type")
|
||||
if container_type == "quality_changes":
|
||||
quality_name = instance_container.getName()
|
||||
num_settings_overriden_by_quality_changes += len(instance_container._instances)
|
||||
# Check if quality changes already exists.
|
||||
quality_changes = self._container_registry.findInstanceContainers(id = container_id)
|
||||
if quality_changes:
|
||||
# Check if there really is a conflict by comparing the values
|
||||
if quality_changes[0] != instance_container:
|
||||
quality_changes_conflict = True
|
||||
break
|
||||
elif container_type == "quality":
|
||||
# If the quality name is not set (either by quality or changes, set it now)
|
||||
# Quality changes should always override this (as they are "on top")
|
||||
if quality_name == "":
|
||||
quality_name = instance_container.getName()
|
||||
quality_type = instance_container.getName()
|
||||
Job.yieldThread()
|
||||
num_visible_settings = 0
|
||||
try:
|
||||
archive.open("Cura/preferences.cfg")
|
||||
temp_preferences = Preferences()
|
||||
temp_preferences.readFromFile(io.TextIOWrapper(archive.open("Cura/preferences.cfg"))) # We need to wrap it, else the archive parser breaks.
|
||||
|
||||
visible_settings_string = temp_preferences.getValue("general/visible_settings")
|
||||
if visible_settings_string is not None:
|
||||
num_visible_settings = len(visible_settings_string.split(";"))
|
||||
active_mode = temp_preferences.getValue("cura/active_mode")
|
||||
if not active_mode:
|
||||
active_mode = Preferences.getInstance().getValue("cura/active_mode")
|
||||
except KeyError:
|
||||
# If there is no preferences file, it's not a workspace, so notify user of failure.
|
||||
Logger.log("w", "File %s is not a valid workspace.", file_name)
|
||||
return WorkspaceReader.PreReadResult.failed
|
||||
|
||||
if machine_conflict or quality_changes_conflict or material_conflict:
|
||||
# There is a conflict; User should choose to either update the existing data, add everything as new data or abort
|
||||
self._dialog.setMachineConflict(machine_conflict)
|
||||
self._dialog.setQualityChangesConflict(quality_changes_conflict)
|
||||
self._dialog.setMaterialConflict(material_conflict)
|
||||
self._dialog.show()
|
||||
# Show the dialog, informing the user what is about to happen.
|
||||
self._dialog.setMachineConflict(machine_conflict)
|
||||
self._dialog.setQualityChangesConflict(quality_changes_conflict)
|
||||
self._dialog.setMaterialConflict(material_conflict)
|
||||
self._dialog.setNumVisibleSettings(num_visible_settings)
|
||||
self._dialog.setQualityName(quality_name)
|
||||
self._dialog.setQualityType(quality_type)
|
||||
self._dialog.setNumSettingsOverridenByQualityChanges(num_settings_overriden_by_quality_changes)
|
||||
self._dialog.setActiveMode(active_mode)
|
||||
self._dialog.setMachineName(machine_name)
|
||||
self._dialog.setMaterialLabels(material_labels)
|
||||
self._dialog.setHasObjectsOnPlate(Application.getInstance().getPlatformActivity)
|
||||
self._dialog.show()
|
||||
|
||||
# Block until the dialog is closed.
|
||||
self._dialog.waitForClose()
|
||||
# Block until the dialog is closed.
|
||||
self._dialog.waitForClose()
|
||||
|
||||
if self._dialog.getResult() == {}:
|
||||
return WorkspaceReader.PreReadResult.cancelled
|
||||
if self._dialog.getResult() == {}:
|
||||
return WorkspaceReader.PreReadResult.cancelled
|
||||
|
||||
self._resolve_strategies = self._dialog.getResult()
|
||||
self._resolve_strategies = self._dialog.getResult()
|
||||
|
||||
return WorkspaceReader.PreReadResult.accepted
|
||||
|
||||
|
@ -381,16 +412,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
ExtruderManager.getInstance().registerExtruder(None, global_stack.getId())
|
||||
|
||||
Logger.log("d", "Workspace loading is notifying rest of the code of changes...")
|
||||
# Notify everything/one that is to notify about changes.
|
||||
for container in global_stack.getContainers():
|
||||
global_stack.containersChanged.emit(container)
|
||||
|
||||
Job.yieldThread()
|
||||
# Notify everything/one that is to notify about changes.
|
||||
global_stack.containersChanged.emit(global_stack.getTop())
|
||||
|
||||
for stack in extruder_stacks:
|
||||
stack.setNextStack(global_stack)
|
||||
for container in stack.getContainers():
|
||||
stack.containersChanged.emit(container)
|
||||
Job.yieldThread()
|
||||
stack.containersChanged.emit(stack.getTop())
|
||||
|
||||
# Actually change the active machine.
|
||||
Application.getInstance().setGlobalContainerStack(global_stack)
|
||||
|
@ -414,3 +442,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
|||
container_string = parser["general"].get("containers", "")
|
||||
container_list = container_string.split(",")
|
||||
return [container_id for container_id in container_list if container_id != ""]
|
||||
|
||||
def _getMachineNameFromSerializedStack(self, serialized):
|
||||
parser = configparser.ConfigParser(interpolation=None, empty_lines_in_values=False)
|
||||
parser.read_string(serialized)
|
||||
return parser["general"].get("name", "")
|
||||
|
||||
def _getMaterialLabelFromSerialized(self, serialized):
|
||||
data = ET.fromstring(serialized)
|
||||
metadata = data.iterfind("./um:metadata/um:name/um:label", {"um": "http://www.ultimaker.com/material"})
|
||||
for entry in metadata:
|
||||
return entry.text
|
||||
pass
|
|
@ -3,10 +3,13 @@ from PyQt5.QtQml import QQmlComponent, QQmlContext
|
|||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
class WorkspaceDialog(QObject):
|
||||
showDialogSignal = pyqtSignal()
|
||||
|
@ -28,10 +31,97 @@ class WorkspaceDialog(QObject):
|
|||
self._has_quality_changes_conflict = False
|
||||
self._has_machine_conflict = False
|
||||
self._has_material_conflict = False
|
||||
self._num_visible_settings = 0
|
||||
self._active_mode = ""
|
||||
self._quality_name = ""
|
||||
self._num_settings_overriden_by_quality_changes = 0
|
||||
self._quality_type = ""
|
||||
self._machine_name = ""
|
||||
self._material_labels = []
|
||||
self._objects_on_plate = False
|
||||
|
||||
machineConflictChanged = pyqtSignal()
|
||||
qualityChangesConflictChanged = pyqtSignal()
|
||||
materialConflictChanged = pyqtSignal()
|
||||
numVisibleSettingsChanged = pyqtSignal()
|
||||
activeModeChanged = pyqtSignal()
|
||||
qualityNameChanged = pyqtSignal()
|
||||
numSettingsOverridenByQualityChangesChanged = pyqtSignal()
|
||||
qualityTypeChanged = pyqtSignal()
|
||||
machineNameChanged = pyqtSignal()
|
||||
materialLabelsChanged = pyqtSignal()
|
||||
objectsOnPlateChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=objectsOnPlateChanged)
|
||||
def hasObjectsOnPlate(self):
|
||||
return self._objects_on_plate
|
||||
|
||||
def setHasObjectsOnPlate(self, objects_on_plate):
|
||||
self._objects_on_plate = objects_on_plate
|
||||
self.objectsOnPlateChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariantList", notify = materialLabelsChanged)
|
||||
def materialLabels(self):
|
||||
return self._material_labels
|
||||
|
||||
def setMaterialLabels(self, material_labels):
|
||||
self._material_labels = material_labels
|
||||
self.materialLabelsChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify = machineNameChanged)
|
||||
def machineName(self):
|
||||
return self._machine_name
|
||||
|
||||
def setMachineName(self, machine_name):
|
||||
self._machine_name = machine_name
|
||||
self.machineNameChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=qualityTypeChanged)
|
||||
def qualityType(self):
|
||||
return self._quality_type
|
||||
|
||||
def setQualityType(self, quality_type):
|
||||
self._quality_type = quality_type
|
||||
self.qualityTypeChanged.emit()
|
||||
|
||||
@pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged)
|
||||
def numSettingsOverridenByQualityChanges(self):
|
||||
return self._num_settings_overriden_by_quality_changes
|
||||
|
||||
def setNumSettingsOverridenByQualityChanges(self, num_settings_overriden_by_quality_changes):
|
||||
self._num_settings_overriden_by_quality_changes = num_settings_overriden_by_quality_changes
|
||||
self.numSettingsOverridenByQualityChangesChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=qualityNameChanged)
|
||||
def qualityName(self):
|
||||
return self._quality_name
|
||||
|
||||
def setQualityName(self, quality_name):
|
||||
self._quality_name = quality_name
|
||||
self.qualityNameChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=activeModeChanged)
|
||||
def activeMode(self):
|
||||
return self._active_mode
|
||||
|
||||
def setActiveMode(self, active_mode):
|
||||
if active_mode == 0:
|
||||
self._active_mode = i18n_catalog.i18nc("@title:tab", "Recommended")
|
||||
else:
|
||||
self._active_mode = i18n_catalog.i18nc("@title:tab", "Custom")
|
||||
self.activeModeChanged.emit()
|
||||
|
||||
@pyqtProperty(int, constant = True)
|
||||
def totalNumberOfSettings(self):
|
||||
return len(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0].getAllKeys())
|
||||
|
||||
@pyqtProperty(int, notify = numVisibleSettingsChanged)
|
||||
def numVisibleSettings(self):
|
||||
return self._num_visible_settings
|
||||
|
||||
def setNumVisibleSettings(self, num_visible_settings):
|
||||
self._num_visible_settings = num_visible_settings
|
||||
self.numVisibleSettingsChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = machineConflictChanged)
|
||||
def machineConflict(self):
|
||||
|
@ -95,8 +185,8 @@ class WorkspaceDialog(QObject):
|
|||
@pyqtSlot()
|
||||
## Used to notify the dialog so the lock can be released.
|
||||
def notifyClosed(self):
|
||||
if self._result is None:
|
||||
self._result = {}
|
||||
self._result = {}
|
||||
self._visible = False
|
||||
self._lock.release()
|
||||
|
||||
def hide(self):
|
||||
|
|
|
@ -10,16 +10,17 @@ import UM 1.1 as UM
|
|||
|
||||
UM.Dialog
|
||||
{
|
||||
title: catalog.i18nc("@title:window", "Import workspace conflict")
|
||||
title: catalog.i18nc("@title:window", "Import Project")
|
||||
|
||||
width: 350 * Screen.devicePixelRatio;
|
||||
minimumWidth: 350 * Screen.devicePixelRatio;
|
||||
maximumWidth: 350 * Screen.devicePixelRatio;
|
||||
|
||||
height: 250 * Screen.devicePixelRatio;
|
||||
minimumHeight: 250 * Screen.devicePixelRatio;
|
||||
maximumHeight: 250 * Screen.devicePixelRatio;
|
||||
width: 550
|
||||
minimumWidth: 550
|
||||
maximumWidth: 550
|
||||
|
||||
height: 350
|
||||
minimumHeight: 350
|
||||
maximumHeight: 350
|
||||
property int comboboxHeight: 15
|
||||
property int spacerHeight: 10
|
||||
onClosing: manager.notifyClosed()
|
||||
onVisibleChanged:
|
||||
{
|
||||
|
@ -48,7 +49,7 @@ UM.Dialog
|
|||
// See http://stackoverflow.com/questions/7659442/listelement-fields-as-properties
|
||||
Component.onCompleted:
|
||||
{
|
||||
append({"key": "override", "label": catalog.i18nc("@action:ComboBox option", "Override existing")});
|
||||
append({"key": "override", "label": catalog.i18nc("@action:ComboBox option", "Update existing")});
|
||||
append({"key": "new", "label": catalog.i18nc("@action:ComboBox option", "Create new")});
|
||||
}
|
||||
}
|
||||
|
@ -56,36 +57,60 @@ UM.Dialog
|
|||
Column
|
||||
{
|
||||
anchors.fill: parent
|
||||
spacing: 2
|
||||
Label
|
||||
{
|
||||
id: infoLabel
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@action:label", "Cura detected a number of conflicts while importing the workspace. How would you like to resolve these?")
|
||||
wrapMode: Text.Wrap
|
||||
height: 50
|
||||
id: titleLabel
|
||||
text: catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font.pixelSize: 22
|
||||
}
|
||||
UM.TooltipArea
|
||||
Rectangle
|
||||
{
|
||||
id: machineResolveTooltip
|
||||
id: separator
|
||||
color: "black"
|
||||
width: parent.width
|
||||
height: visible ? 25 : 0
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
|
||||
visible: manager.machineConflict
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label","Machine")
|
||||
width: 150
|
||||
}
|
||||
height: 1
|
||||
}
|
||||
Item // Spacer
|
||||
{
|
||||
height: spacerHeight
|
||||
width: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Printer settings")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.machineName
|
||||
width: parent.width / 3
|
||||
}
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: machineResolveTooltip
|
||||
width: parent.width / 3
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.machineConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
|
||||
ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: machineResolveComboBox
|
||||
width: parent.width
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("machine", resolveStrategiesModel.get(index).key)
|
||||
|
@ -93,28 +118,46 @@ UM.Dialog
|
|||
}
|
||||
}
|
||||
}
|
||||
UM.TooltipArea
|
||||
Item // Spacer
|
||||
{
|
||||
id: qualityChangesResolveTooltip
|
||||
width: parent.width
|
||||
height: visible ? 25 : 0
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
|
||||
visible: manager.qualityChangesConflict
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label","Profile")
|
||||
width: 150
|
||||
}
|
||||
height: spacerHeight
|
||||
width: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Profile settings")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.qualityName
|
||||
width: parent.width / 3
|
||||
}
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: qualityChangesResolveTooltip
|
||||
width: parent.width / 3
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.qualityChangesConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
|
||||
ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: qualityChangesResolveComboBox
|
||||
width: parent.width
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key)
|
||||
|
@ -122,28 +165,78 @@ UM.Dialog
|
|||
}
|
||||
}
|
||||
}
|
||||
UM.TooltipArea
|
||||
Row
|
||||
{
|
||||
id: materialResolveTooltip
|
||||
width: parent.width
|
||||
height: visible ? 25 : 0
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the material(s) be resolved?")
|
||||
visible: manager.materialConflict
|
||||
Row
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Derivative from")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "%1, %2 override(s)" ).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges)
|
||||
width: parent.width / 3
|
||||
}
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
}
|
||||
Item // Spacer
|
||||
{
|
||||
height: spacerHeight
|
||||
width: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Material settings")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.materialLabels
|
||||
delegate: Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label","Material")
|
||||
width: 150
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: modelData
|
||||
width: parent.width / 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
visible: manager.materialConflict
|
||||
Item
|
||||
{
|
||||
width: parent.width / 3 * 2
|
||||
height: comboboxHeight
|
||||
}
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: materialResolveTooltip
|
||||
width: parent.width / 3
|
||||
height: visible ? comboboxHeight : 0
|
||||
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the material be resolved?")
|
||||
ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: materialResolveComboBox
|
||||
width: parent.width
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("material", resolveStrategiesModel.get(index).key)
|
||||
|
@ -151,6 +244,60 @@ UM.Dialog
|
|||
}
|
||||
}
|
||||
}
|
||||
Item // Spacer
|
||||
{
|
||||
height: spacerHeight
|
||||
width: height
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Setting visibility")
|
||||
font.bold: true
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Mode")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.activeMode
|
||||
width: parent.width / 3
|
||||
}
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Visible settings:")
|
||||
width: parent.width / 3
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings)
|
||||
width: parent.width / 3
|
||||
}
|
||||
}
|
||||
Item // Spacer
|
||||
{
|
||||
height: spacerHeight
|
||||
width: height
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@action:warning", "Loading a project will clear all models on the buildplate")
|
||||
visible: manager.hasObjectsOnPlate
|
||||
color: "red"
|
||||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
rightButtons: [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue