Move PerObjectSettings from Uranium to Cura

This tool is deemed Cura-specific, so it is moved to Cura. This prevents it from being included in other Uranium applications.
This commit is contained in:
Ghostkeeper 2015-10-01 15:41:36 +02:00
parent 6fd9eba2dd
commit 1bda2ff220
5 changed files with 569 additions and 0 deletions

View file

@ -0,0 +1,109 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
from UM.Application import Application
from UM.Qt.ListModel import ListModel
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from UM.Settings.ProfileOverrideDecorator import ProfileOverrideDecorator
from . import SettingOverrideModel
class PerObjectSettingsModel(ListModel):
IdRole = Qt.UserRole + 1
XRole = Qt.UserRole + 2
YRole = Qt.UserRole + 3
MaterialRole = Qt.UserRole + 4
ProfileRole = Qt.UserRole + 5
SettingsRole = Qt.UserRole + 6
def __init__(self, parent = None):
super().__init__(parent)
self._scene = Application.getInstance().getController().getScene()
self._root = self._scene.getRoot()
self._root.transformationChanged.connect(self._updatePositions)
self._root.childrenChanged.connect(self._updateNodes)
self._updateNodes(None)
self.addRoleName(self.IdRole,"id")
self.addRoleName(self.XRole,"x")
self.addRoleName(self.YRole,"y")
self.addRoleName(self.MaterialRole, "material")
self.addRoleName(self.ProfileRole, "profile")
self.addRoleName(self.SettingsRole, "settings")
@pyqtSlot("quint64", str)
def setObjectProfile(self, object_id, profile_name):
self.setProperty(self.find("id", object_id), "profile", profile_name)
profile = None
if profile_name != "global":
profile = Application.getInstance().getMachineManager().findProfile(profile_name)
node = self._scene.findObject(object_id)
if profile:
if not node.getDecorator(ProfileOverrideDecorator):
node.addDecorator(ProfileOverrideDecorator())
node.callDecoration("setProfile", profile)
else:
if node.getDecorator(ProfileOverrideDecorator):
node.removeDecorator(ProfileOverrideDecorator)
@pyqtSlot("quint64", str)
def addSettingOverride(self, object_id, key):
machine = Application.getInstance().getMachineManager().getActiveMachineInstance()
if not machine:
return
node = self._scene.findObject(object_id)
if not node.getDecorator(SettingOverrideDecorator):
node.addDecorator(SettingOverrideDecorator())
node.callDecoration("addSetting", key)
@pyqtSlot("quint64", str)
def removeSettingOverride(self, object_id, key):
node = self._scene.findObject(object_id)
node.callDecoration("removeSetting", key)
if len(node.callDecoration("getAllSettings")) == 0:
node.removeDecorator(SettingOverrideDecorator)
def _updatePositions(self, source):
camera = Application.getInstance().getController().getScene().getActiveCamera()
for node in BreadthFirstIterator(self._root):
if type(node) is not SceneNode or not node.getMeshData():
continue
projected_position = camera.project(node.getWorldPosition())
index = self.find("id", id(node))
self.setProperty(index, "x", float(projected_position[0]))
self.setProperty(index, "y", float(projected_position[1]))
def _updateNodes(self, source):
self.clear()
camera = Application.getInstance().getController().getScene().getActiveCamera()
for node in BreadthFirstIterator(self._root):
if type(node) is not SceneNode or not node.getMeshData() or not node.isSelectable():
continue
projected_position = camera.project(node.getWorldPosition())
node_profile = node.callDecoration("getProfile")
if not node_profile:
node_profile = "global"
else:
node_profile = node_profile.getName()
self.appendItem({
"id": id(node),
"x": float(projected_position[0]),
"y": float(projected_position[1]),
"material": "",
"profile": node_profile,
"settings": SettingOverrideModel.SettingOverrideModel(node)
})

View file

@ -0,0 +1,339 @@
// Copyright (c) 2015 Ultimaker B.V.
// Uranium is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Window 2.2
import UM 1.1 as UM
Item {
id: base;
width: 0;
height: 0;
property variant position: mapToItem(null, 0, 0)
property real viewportWidth: UM.Application.mainWindow.width * UM.Application.mainWindow.viewportRect.width;
property real viewportHeight: UM.Application.mainWindow.height * UM.Application.mainWindow.viewportRect.height;
property int currentIndex;
Rectangle {
id: settingsPanel;
z: 3;
width: UM.Theme.sizes.per_object_settings_panel.width;
height: items.height + UM.Theme.sizes.default_margin.height * 2;
opacity: 0;
Behavior on opacity { NumberAnimation { } }
border.width: UM.Theme.sizes.per_object_settings_panel_border.width;
border.color: UM.Theme.colors.per_object_settings_panel_border;
color: UM.Theme.colors.per_object_settings_panel_background;
DropArea {
anchors.fill: parent;
}
Column {
id: items
anchors.top: parent.top;
anchors.topMargin: UM.Theme.sizes.default_margin.height;
spacing: UM.Theme.sizes.default_lining.height;
UM.SettingItem {
id: profileSelection
x: UM.Theme.sizes.per_object_settings_panel_border.width + 1
width: UM.Theme.sizes.setting.width;
height: UM.Theme.sizes.setting.height;
name: catalog.i18nc("@label", "Profile")
type: "enum"
perObjectSetting: true
style: UM.Theme.styles.setting_item;
options: UM.ProfilesModel { addUseGlobal: true }
value: UM.ActiveTool.properties.Model.getItem(base.currentIndex).profile
onItemValueChanged: {
var item = UM.ActiveTool.properties.Model.getItem(base.currentIndex);
UM.ActiveTool.properties.Model.setObjectProfile(item.id, value)
}
}
Repeater {
id: settings;
model: UM.ActiveTool.properties.Model.getItem(base.currentIndex).settings
UM.SettingItem {
width: UM.Theme.sizes.setting.width;
height: UM.Theme.sizes.setting.height;
x: UM.Theme.sizes.per_object_settings_panel_border.width + 1
name: model.label;
type: model.type;
value: model.value;
description: model.description;
unit: model.unit;
valid: model.valid;
perObjectSetting: true
dismissable: true
style: UM.Theme.styles.setting_item;
onItemValueChanged: {
settings.model.setSettingValue(model.key, value)
}
// Button {
// anchors.left: parent.right;
// text: "x";
//
// width: UM.Theme.sizes.setting.height;
// height: UM.Theme.sizes.setting.height;
//
// opacity: parent.hovered || hovered ? 1 : 0;
// onClicked: UM.ActiveTool.properties.Model.removeSettingOverride(UM.ActiveTool.properties.Model.getItem(base.currentIndex).id, model.key)
//
// style: ButtonStyle { }
// }
}
}
Item
{
height: UM.Theme.sizes.default_margin.height / 2
width: parent.width
}
Button
{
anchors.right: profileSelection.right;
text: catalog.i18nc("@action:button", "Customize Settings");
style: ButtonStyle
{
background: Rectangle
{
width: control.width;
height: control.height;
color: control.hovered ? UM.Theme.colors.load_save_button_hover : UM.Theme.colors.load_save_button;
}
label: Label
{
text: control.text;
color: UM.Theme.colors.load_save_button_text;
}
}
onClicked: settingPickDialog.visible = true;
}
}
UM.I18nCatalog { id: catalog; name: "uranium"; }
}
Repeater {
model: UM.ActiveTool.properties.Model;
delegate: Button {
x: ((model.x + 1.0) / 2.0) * base.viewportWidth - base.position.x - width / 2
y: -((model.y + 1.0) / 2.0) * base.viewportHeight + (base.viewportHeight - base.position.y) + height / 2
width: UM.Theme.sizes.per_object_settings_button.width
height: UM.Theme.sizes.per_object_settings_button.height
tooltip: catalog.i18nc("@info:tooltip", "Customise settings for this object");
checkable: true;
onClicked: {
base.currentIndex = index;
settingsPanel.anchors.left = right;
settingsPanel.anchors.top = top;
settingsPanel.opacity = 1;
}
style: ButtonStyle
{
background: Rectangle
{
width: control.width;
height: control.height;
color: control.hovered ? UM.Theme.colors.button_active : UM.Theme.colors.button_hover;
}
label: Image {
width: control.width;
height: control.height;
sourceSize.width: width;
sourceSize.height: height;
source: UM.Theme.icons.plus;
}
}
}
}
UM.Dialog {
id: settingPickDialog
title: catalog.i18nc("@title:window", "Pick a Setting to Customize")
TextField {
id: filter;
anchors {
top: parent.top;
left: parent.left;
right: parent.right;
}
placeholderText: catalog.i18nc("@label:textbox", "Filter...");
onTextChanged: settingCategoriesModel.filter(text);
}
ScrollView {
id: view;
anchors {
top: filter.bottom;
left: parent.left;
right: parent.right;
bottom: parent.bottom;
}
Column {
width: view.width - UM.Theme.sizes.default_margin.width * 2;
height: childrenRect.height;
Repeater {
id: settingList;
model: UM.SettingCategoriesModel { id: settingCategoriesModel; }
delegate: Item {
id: delegateItem;
width: parent.width;
height: childrenRect.height;
ToolButton {
id: categoryHeader;
text: model.name;
checkable: true;
width: parent.width;
onCheckedChanged: settingsColumn.state != "" ? settingsColumn.state = "" : settingsColumn.state = "collapsed";
style: ButtonStyle {
background: Rectangle
{
width: control.width;
height: control.height;
color: control.hovered ? palette.highlight : "transparent";
}
label: Row
{
spacing: UM.Theme.sizes.default_margin.width;
Image
{
anchors.verticalCenter: parent.verticalCenter;
source: control.checked ? UM.Theme.icons.arrow_right : UM.Theme.icons.arrow_bottom;
}
Label
{
text: control.text;
font.bold: true;
color: control.hovered ? palette.highlightedText : palette.text;
}
}
}
}
property variant settingsModel: model.settings;
visible: model.visible;
Column {
id: settingsColumn;
anchors.top: categoryHeader.bottom;
property real childrenHeight:
{
var h = 0.0;
for(var i in children)
{
var item = children[i];
h += children[i].height;
if(item.settingVisible)
{
if(i > 0)
{
h += spacing;
}
}
}
return h;
}
width: childrenRect.width;
height: childrenHeight;
Repeater {
model: delegateItem.settingsModel;
delegate: ToolButton {
id: button;
x: model.depth * UM.Theme.sizes.default_margin.width;
text: model.name;
tooltip: model.description;
onClicked: {
var object_id = UM.ActiveTool.properties.Model.getItem(base.currentIndex).id;
UM.ActiveTool.properties.Model.addSettingOverride(object_id, model.key);
settingPickDialog.visible = false;
}
states: State {
name: "filtered"
when: model.filtered || !model.visible || !model.enabled
PropertyChanges { target: button; height: 0; opacity: 0; }
}
}
}
states: State {
name: "collapsed";
PropertyChanges { target: settingsColumn; opacity: 0; height: 0; }
}
}
}
}
}
}
rightButtons: [
Button {
text: catalog.i18nc("@action:button", "Cancel");
onClicked: {
settingPickDialog.visible = false;
}
}
]
}
SystemPalette { id: palette; }
}

View file

@ -0,0 +1,18 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from UM.Tool import Tool
from . import PerObjectSettingsModel
class PerObjectSettingsTool(Tool):
def __init__(self):
super().__init__()
self.setExposedProperties("Model")
def event(self, event):
return False
def getModel(self):
return PerObjectSettingsModel.PerObjectSettingsModel()

View file

@ -0,0 +1,76 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSlot, QUrl
from UM.Application import Application
from UM.Qt.ListModel import ListModel
from UM.Settings.SettingOverrideDecorator import SettingOverrideDecorator
class SettingOverrideModel(ListModel):
KeyRole = Qt.UserRole + 1
LabelRole = Qt.UserRole + 2
DescriptionRole = Qt.UserRole + 3
ValueRole = Qt.UserRole + 4
TypeRole = Qt.UserRole + 5
UnitRole = Qt.UserRole + 6
ValidRole = Qt.UserRole + 7
def __init__(self, node, parent = None):
super().__init__(parent)
self._ignore_setting_change = None
self._node = node
self._node.decoratorsChanged.connect(self._onDecoratorsChanged)
self._onDecoratorsChanged(None)
self.addRoleName(self.KeyRole, "key")
self.addRoleName(self.LabelRole, "label")
self.addRoleName(self.DescriptionRole, "description")
self.addRoleName(self.ValueRole,"value")
self.addRoleName(self.TypeRole, "type")
self.addRoleName(self.UnitRole, "unit")
self.addRoleName(self.ValidRole, "valid")
@pyqtSlot(str, "QVariant")
def setSettingValue(self, key, value):
if not self._decorator:
return
self._ignore_setting_change = key
self._decorator.setSettingValue(key, value)
self._ignore_setting_change = None
def _onDecoratorsChanged(self, node):
if not self._node.getDecorator(SettingOverrideDecorator):
self.clear()
return
self._decorator = self._node.getDecorator(SettingOverrideDecorator)
self._decorator.settingAdded.connect(self._onSettingsChanged)
self._decorator.settingRemoved.connect(self._onSettingsChanged)
self._decorator.settingValueChanged.connect(self._onSettingValueChanged)
self._onSettingsChanged()
def _onSettingsChanged(self):
self.clear()
for key, setting in self._decorator.getAllSettings().items():
value = self._decorator.getSettingValue(key)
self.appendItem({
"key": key,
"label": setting.getLabel(),
"description": setting.getDescription(),
"value": str(value),
"type": setting.getType(),
"unit": setting.getUnit(),
"valid": setting.validate(value)
})
def _onSettingValueChanged(self, setting):
index = self.find("key", setting.getKey())
if index != -1 and self._ignore_setting_change != setting.getKey():
value = self._decorator.getSettingValue(setting.getKey())
self.setProperty(index, "value", str(value))
self.setProperty(index, "valid", setting.validate(value))

View file

@ -0,0 +1,27 @@
# Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher.
from . import PerObjectSettingsTool
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("uranium")
def getMetaData():
return {
"plugin": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"),
"author": "Ultimaker",
"version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."),
"api": 2
},
"tool": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings"),
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"),
"icon": "setting_per_object",
"tool_panel": "PerObjectSettingsPanel.qml"
},
}
def register(app):
return { "tool": PerObjectSettingsTool.PerObjectSettingsTool() }