WIP: Rework profile management page

This commit is contained in:
Lipu Fei 2018-02-16 16:40:09 +01:00
parent bed2106fc6
commit 96f1264364
9 changed files with 561 additions and 36 deletions

View file

@ -61,6 +61,7 @@ from cura.Settings.UserProfilesModel import UserProfilesModel
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
from cura.Machines.VariantManager import VariantManager
from cura.Machines.Models.QualityManagementModel import QualityManagementModel
from . import PlatformPhysics
from . import BuildVolume
@ -939,6 +940,9 @@ class CuraApplication(QtApplication):
qmlRegisterType(BrandMaterialsModel, "Cura", 1, 0, "BrandMaterialsModel")
qmlRegisterType(NewMaterialsModel, "Cura", 1, 0, "NewMaterialsModel")
# TODO: make this singleton?
qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel")
qmlRegisterSingletonType(NewQualityProfilesModel, "Cura", 1, 0, "NewQualityProfilesModel", self.getNewQualityProfileModel)
qmlRegisterSingletonType(NewCustomQualityProfilesModel, "Cura", 1, 0, "NewCustomQualityProfilesModel", self.getNewCustomQualityProfilesModel)
qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")

View file

@ -59,7 +59,7 @@ class MaterialManager(QObject):
self._update_timer = QTimer(self)
self._update_timer.setInterval(300)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._updateTables)
self._update_timer.timeout.connect(self._updateMaps)
self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
@ -192,7 +192,7 @@ class MaterialManager(QObject):
self.materialsUpdated.emit()
def _updateTables(self):
def _updateMaps(self):
self.initialize()
def _onContainerMetadataChanged(self, container):
@ -203,7 +203,7 @@ class MaterialManager(QObject):
if container_type != "material":
return
# TODO: update the cache table
# update the maps
self._update_timer.start()
def getMaterialGroup(self, root_material_id: str) -> Optional[MaterialGroup]:

View file

@ -0,0 +1,75 @@
from PyQt5.QtCore import Qt
from UM.Qt.ListModel import ListModel
class QualityManagementModel(ListModel):
NameRole = Qt.UserRole + 1
IsReadOnlyRole = Qt.UserRole + 2
QualityGroupRole = Qt.UserRole + 3
QualityChangesGroupRole = Qt.UserRole + 4
def __init__(self, parent = None):
super().__init__(parent)
self.addRoleName(self.NameRole, "name")
self.addRoleName(self.IsReadOnlyRole, "is_read_only")
self.addRoleName(self.QualityGroupRole, "quality_group")
self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
from cura.CuraApplication import CuraApplication
self._container_registry = CuraApplication.getInstance().getContainerRegistry()
self._machine_manager = CuraApplication.getInstance().getMachineManager()
self._extruder_manager = CuraApplication.getInstance().getExtruderManager()
self._quality_manager = CuraApplication.getInstance()._quality_manager
self._machine_manager.globalContainerChanged.connect(self._update)
#self._quality_manager.materialsUpdated.connect(self._update) # TODO
self._update()
def _update(self):
global_stack = self._machine_manager._global_container_stack
quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
available_quality_types = set(qt for qt, qg in quality_group_dict.items() if qg.is_available)
if not available_quality_types and not quality_changes_group_dict:
# Nothing to show
self.setItems([])
return
item_list = []
# Create quality group items
for quality_group in quality_group_dict.values():
if not quality_group.is_available:
continue
item = {"name": quality_group.name,
"is_read_only": True,
"quality_group": quality_group,
"quality_changes_group": None}
item_list.append(item)
# Sort by quality names
item_list = sorted(item_list, key = lambda x: x["name"])
# Create quality_changes group items
quality_changes_item_list = []
for quality_changes_group in quality_changes_group_dict.values():
if quality_changes_group.quality_type not in available_quality_types:
continue
quality_group = quality_group_dict[quality_changes_group.quality_type]
item = {"name": quality_changes_group.name,
"is_read_only": False,
"quality_group": quality_group,
"quality_changes_group": quality_changes_group}
quality_changes_item_list.append(item)
# Sort quality_changes items by names and append to the item list
quality_changes_item_list = sorted(quality_changes_item_list, key = lambda x: x["name"])
item_list += quality_changes_item_list
self.setItems(item_list)

View file

@ -1,10 +1,9 @@
from typing import Optional
from PyQt5.Qt import QObject
from PyQt5.QtCore import QObject, QTimer
from UM.Application import Application
from UM.Logger import Logger
from UM.Util import parseBool
from cura.Machines.ContainerGroup import ContainerGroup
from cura.Machines.ContainerNode import ContainerNode
@ -126,6 +125,15 @@ class QualityManager(QObject):
self._default_machine_definition_id = "fdmprinter"
self._container_registry.containerMetaDataChanged.connect(self._onContainerMetadataChanged)
self._container_registry.containerAdded.connect(self._onContainerMetadataChanged)
self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged)
self._update_timer = QTimer(self)
self._update_timer.setInterval(300)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._updateMaps)
def initialize(self):
# Initialize the lookup tree for quality profiles with following structure:
# <machine> -> <variant> -> <material>
@ -186,10 +194,6 @@ class QualityManager(QObject):
material_node.addQualityMetadata(quality_type, metadata)
# Initialize quality
self._initializeQualityChangesTables()
def _initializeQualityChangesTables(self):
# Initialize the lookup tree for quality_changes profiles with following structure:
# <machine> -> <quality_type> -> <name>
quality_changes_metadata_list = self._container_registry.findContainersMetadata(type = "quality_changes")
@ -206,6 +210,20 @@ class QualityManager(QObject):
machine_node.addQualityChangesMetadata(quality_type, metadata)
def _updateMaps(self):
self.initialize()
def _onContainerMetadataChanged(self, container):
self._onContainerChanged(container)
def _onContainerChanged(self, container):
container_type = container.getMetaDataEntry("type")
if container_type not in ("quality", "quality_changes"):
return
# update the cache table
self._update_timer.start()
# Updates the given quality groups' availabilities according to which extruders are being used/ enabled.
def _updateQualityGroupsAvailability(self, machine: "GlobalStack", quality_group_list):
used_extruders = set()
@ -234,8 +252,8 @@ class QualityManager(QObject):
machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id)
if not machine_node:
Logger.log("e", "Cannot find node for machine def [%s] in QualityChanges lookup table", machine_definition_id)
return {}
Logger.log("i", "Cannot find node for machine def [%s] in QualityChanges lookup table", machine_definition_id)
return dict()
# Update availability for each QualityChangesGroup:
# A custom profile is always available as long as the quality_type it's based on is available

View file

@ -12,7 +12,6 @@ from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
import UM.FlameProfiler
from UM.FlameProfiler import pyqtSlot
from PyQt5.QtWidgets import QMessageBox
from UM import Util
from UM.Application import Application
@ -1371,27 +1370,6 @@ class MachineManager(QObject):
self._current_root_material_id[position] = self._global_container_stack.extruders[position].material.getMetaDataEntry("base_file")
return self._current_root_material_id
def _setQualityChangesGroup(self, quality_changes_group):
self._current_quality_changes_group = quality_changes_group
# TODO: quality_changes groups depend on a quality_type. Here it's fetching the quality_types every time.
# Can we do this better, like caching the quality group a quality_changes group depends on?
quality_manager = Application.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroups(self._global_container_stack)
quality_type = quality_changes_group.quality_type
container = self._empty_quality_changes_container
if quality_changes_group.node_for_global is not None:
container = quality_changes_group.node_for_global.getContainer()
self._global_container_stack.qualityChanges = container
self._global_container_stack.quality = quality_group_dict[quality_type]
for position, extruder in self._global_container_stack.extruders.items():
container = quality_changes_group.nodes_for_extruders.get(position,
self._empty_quality_changes_container)
extruder.qualityChanges = container
def _setEmptyQuality(self):
self._current_quality_group = None
self._global_container_stack.quality = self._empty_quality_container
@ -1405,8 +1383,6 @@ class MachineManager(QObject):
def _setQualityGroup(self, quality_group, empty_quality_changes = True):
self._current_quality_group = quality_group
#TODO: check quality_changes
# Set quality and quality_changes for the GlobalStack
self._global_container_stack.quality = quality_group.node_for_global.getContainer()
if empty_quality_changes:
@ -1419,6 +1395,44 @@ class MachineManager(QObject):
self._global_container_stack.extruders[position].qualityChanges = self._empty_quality_changes_container
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setQualityChangesGroup(self, quality_changes_group):
# TODO: quality_changes groups depend on a quality_type. Here it's fetching the quality_types every time.
# Can we do this better, like caching the quality group a quality_changes group depends on?
quality_type = quality_changes_group.quality_type
quality_manager = Application.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroups(self._global_container_stack)
quality_group = quality_group_dict[quality_type]
quality_changes_container = self._empty_quality_changes_container
quality_container = self._empty_quality_changes_container
if quality_changes_group.node_for_global:
quality_changes_container = quality_changes_group.node_for_global.getContainer()
if quality_group.node_for_global:
quality_container = quality_group.node_for_global.getContainer()
self._global_container_stack.quality = quality_container
self._global_container_stack.qualityChanges = quality_changes_container
for position, extruder in self._global_container_stack.extruders.items():
quality_changes_node = quality_changes_group.nodes_for_extruders.get(position)
quality_node = quality_group.nodes_for_extruders.get(position)
quality_changes_container = self._empty_quality_changes_container
quality_container = self._empty_quality_changes_container
if quality_changes_node:
quality_changes_container = quality_changes_node.getContainer()
if quality_node:
quality_container = quality_node.getContainer()
extruder.quality = quality_container
extruder.qualityChanges = quality_changes_container
self._current_quality_group = quality_group
self._current_quality_changes_group = quality_changes_group
self.activeQualityGroupChanged.emit()
self.activeQualityChangesGroupChanged.emit()
def _setVariantGroup(self, position, container_node):
self._global_container_stack.extruders[position].variant = container_node.getContainer()
@ -1515,3 +1529,23 @@ class MachineManager(QObject):
def activeQualityGroup(self):
return self._current_quality_group
@pyqtSlot("QVariant")
def setQualityChangesGroup(self, quality_changes_group):
Logger.log("d", "---------------- qcg = [%s]", quality_changes_group.name)
self.blurSettings.emit()
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
self._setQualityChangesGroup(quality_changes_group)
Logger.log("d", "Quality changes set!")
@pyqtProperty("QVariant", fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
def activeQualityChangesGroup(self):
return self._current_quality_changes_group
@pyqtProperty(str, notify = activeQualityGroupChanged)
def activeQualityOrQualityChangesName(self):
name = ""
if self._current_quality_changes_group:
name = self._current_quality_changes_group.name
elif self._current_quality_group:
name = self._current_quality_group.name
return name

View file

@ -2,7 +2,7 @@
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, List, Optional
from PyQt5.QtCore import Qt, QObject
from PyQt5.QtCore import Qt
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel

View file

@ -27,6 +27,7 @@ class NewQualityProfilesModel(ListModel):
LayerHeightRole = Qt.UserRole + 4
AvailableRole = Qt.UserRole + 5
QualityGroupRole = Qt.UserRole + 6
QualityChangesGroupRole = Qt.UserRole + 7
def __init__(self, parent = None):
super().__init__(parent)
@ -37,6 +38,7 @@ class NewQualityProfilesModel(ListModel):
self.addRoleName(self.LayerHeightRole, "layer_height")
self.addRoleName(self.AvailableRole, "available")
self.addRoleName(self.QualityGroupRole, "quality_group")
self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group")
# connect signals
Application.getInstance().globalContainerStackChanged.connect(self._update)

View file

@ -0,0 +1,98 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.8
import QtQuick.Controls 1.4
import UM 1.2 as UM
import Cura 1.0 as Cura
Tab
{
id: base
property string extruderId: "";
property string extruderDefinition: "";
property string quality: "";
property string material: "";
TableView
{
anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width
Component
{
id: itemDelegate
UM.TooltipArea
{
property var setting: qualitySettings.getItem(styleData.row)
height: childrenRect.height
width: (parent != null) ? parent.width : 0
text: (styleData.value.substr(0,1) == "=") ? styleData.value : ""
Label
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
text: (styleData.value.substr(0,1) == "=") ? catalog.i18nc("@info:status", "Calculated") : styleData.value
font.strikeout: styleData.column == 1 && quality == Cura.MachineManager.globalQualityId && setting.user_value != ""
font.italic: setting.profile_value_source == "quality_changes" || (quality == Cura.MachineManager.globalQualityId && setting.user_value != "")
opacity: font.strikeout ? 0.5 : 1
color: styleData.textColor
elide: Text.ElideRight
}
}
}
TableViewColumn
{
role: "label"
title: catalog.i18nc("@title:column", "Setting")
width: (parent.width * 0.4) | 0
delegate: itemDelegate
}
TableViewColumn
{
role: "profile_value"
title: catalog.i18nc("@title:column", "Profile")
width: (parent.width * 0.18) | 0
delegate: itemDelegate
}
TableViewColumn
{
role: "user_value"
title: catalog.i18nc("@title:column", "Current");
visible: quality == Cura.MachineManager.globalQualityId
width: (parent.width * 0.18) | 0
delegate: itemDelegate
}
TableViewColumn
{
role: "unit"
title: catalog.i18nc("@title:column", "Unit")
width: (parent.width * 0.14) | 0
delegate: itemDelegate
}
section.property: "category"
section.delegate: Label
{
text: section
font.bold: true
}
model: Cura.QualitySettingsModel
{
id: qualitySettings
extruderId: base.extruderId
extruderDefinition: base.extruderDefinition
quality: base.quality != null ? base.quality : ""
material: base.material != null ? base.material : ""
}
SystemPalette { id: palette }
}
}

View file

@ -0,0 +1,294 @@
// Copyright (c) 2018 Ultimaker B.V.
// Uranium is released under the terms of the LGPLv3 or higher.
import QtQuick 2.8
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.3
import UM 1.2 as UM
import Cura 1.0 as Cura
Item
{
id: base
property var resetEnabled: false // Keep PreferencesDialog happy
UM.I18nCatalog { id: catalog; name: "cura"; }
Cura.QualityManagementModel {
id: qualitiesModel
}
Label {
id: titleLabel
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 5 * screenScaleFactor
}
font.pointSize: 18
text: catalog.i18nc("@title:tab", "Profiles")
}
Row // Button Row
{
id: buttonRow
anchors {
left: parent.left
right: parent.right
top: titleLabel.bottom
}
height: childrenRect.height
// Activate button
Button
{
text: catalog.i18nc("@action:button", "Activate")
iconName: "list-activate"
//enabled: base.currentItem != null ? base.currentItem.id != Cura.MachineManager.activeQualityId : false;
enabled: true // TODO
onClicked: {
// TODO
}
}
// Create button
Button
{
text: catalog.i18nc("@label", "Create")
iconName: "list-add"
//enabled: base.canCreateProfile() && !Cura.MachineManager.stacksHaveErrors
enabled: true // TODO
//visible: base.canCreateProfile()
visible: true // TODO
onClicked: {
// TODO
}
}
// Duplicate button
Button
{
text: catalog.i18nc("@label", "Duplicate")
iconName: "list-add"
//enabled: ! base.canCreateProfile()
enabled: true // TODO
//visible: ! base.canCreateProfile()
visible: true // TODO
onClicked: {
// TODO
}
}
// Remove button
Button
{
text: catalog.i18nc("@action:button", "Remove")
iconName: "list-remove"
//enabled: base.currentItem != null ? !base.currentItem.readOnly && !Cura.ContainerManager.isContainerUsed(base.currentItem.id) : false;
enabled: true // TODO
onClicked: {
// TODO
}
}
// Rename button
Button
{
text: catalog.i18nc("@action:button", "Rename")
iconName: "edit-rename"
//enabled: base.currentItem != null ? !base.currentItem.readOnly : false;
enabled: true // TODO
onClicked: {
// TODO
}
}
// Import button
Button
{
text: catalog.i18nc("@action:button", "Import")
iconName: "document-import"
onClicked: {
// TODO
}
}
// Export button
Button
{
text: catalog.i18nc("@action:button", "Export")
iconName: "document-export"
//enabled: currentItem != null && !base.currentItem.readOnly
enabled: true // TODO
onClicked: {
// TODO
}
}
}
Item {
id: contentsItem
anchors {
top: titleLabel.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
margins: 5 * screenScaleFactor
bottomMargin: 0
}
clip: true
}
Item
{
anchors {
top: buttonRow.bottom
topMargin: UM.Theme.getSize("default_margin").height
left: parent.left
right: parent.right
bottom: parent.bottom
}
SystemPalette { id: palette }
Label
{
id: captionLabel
anchors {
top: parent.top
left: parent.left
}
visible: text != ""
text: {
// OLD STUFF
return catalog.i18nc("@label %1 is printer name", "Printer: %1").arg(Cura.MachineManager.activeMachineName);
}
width: profileScrollView.width
elide: Text.ElideRight
}
ScrollView
{
id: profileScrollView
anchors {
top: captionLabel.visible ? captionLabel.bottom : parent.top
topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0
bottom: parent.bottom
left: parent.left
}
Rectangle {
parent: viewport
anchors.fill: parent
color: palette.light
}
width: true ? (parent.width * 0.4) | 0 : parent.width
ListView
{
id: qualityListView
model: qualitiesModel
section.property: "is_read_only"
section.delegate: Rectangle
{
height: childrenRect.height
Label
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width
text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles")
font.bold: true
}
}
delegate: Rectangle
{
width: profileScrollView.width
height: childrenRect.height
color: ListView.isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase
Row
{
spacing: (UM.Theme.getSize("default_margin").width / 2) | 0
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.right: parent.right
Label
{
width: Math.floor((parent.width * 0.3))
text: model.name
elide: Text.ElideRight
font.italic: { // TODO: make it easier
return model.name == Cura.MachineManager.activeQualityOrQualityChangesName;
}
color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text;
}
}
MouseArea
{
anchors.fill: parent
onClicked: {
parent.ListView.view.currentIndex = model.index;
}
}
}
onCurrentIndexChanged:
{
var model = qualitiesModel.getItem(currentIndex);
// TODO
}
}
}
Item
{
id: detailsPanel
anchors {
left: profileScrollView.right
leftMargin: UM.Theme.getSize("default_margin").width
top: parent.top
bottom: parent.bottom
right: parent.right
}
Item
{
anchors.fill: parent
Item // Profile title Label
{
id: profileName
width: parent.width
height: childrenRect.height
Label {
text: "TODO" // TODO
font: UM.Theme.getFont("large")
}
}
}
}
}
}