mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-25 15:44:04 -06:00
Merge branch 'master' of github.com:Ultimaker/Cura
This commit is contained in:
commit
eb836bfc3f
6 changed files with 193 additions and 7 deletions
167
cura/CuraContainerRegistry.py
Normal file
167
cura/CuraContainerRegistry.py
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
# Copyright (c) 2016 Ultimaker B.V.
|
||||||
|
# Uranium is released under the terms of the AGPLv3 or higher.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
|
|
||||||
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
from UM.Application import Application
|
||||||
|
from UM.Logger import Logger
|
||||||
|
from UM.Message import Message
|
||||||
|
from UM.Platform import Platform
|
||||||
|
from UM.PluginRegistry import PluginRegistry #For getting the possible profile writers to write with.
|
||||||
|
from UM.Util import parseBool
|
||||||
|
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
catalog = i18nCatalog("uranium")
|
||||||
|
|
||||||
|
class CuraContainerRegistry(ContainerRegistry):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
## Exports an profile to a file
|
||||||
|
#
|
||||||
|
# \param instance_id \type{str} the ID of the profile to export.
|
||||||
|
# \param file_name \type{str} the full path and filename to export to.
|
||||||
|
# \param file_type \type{str} the file type with the format "<description> (*.<extension>)"
|
||||||
|
def exportProfile(self, instance_id, file_name, file_type):
|
||||||
|
Logger.log('d', 'exportProfile instance_id: '+str(instance_id))
|
||||||
|
|
||||||
|
# Parse the fileType to deduce what plugin can save the file format.
|
||||||
|
# fileType has the format "<description> (*.<extension>)"
|
||||||
|
split = file_type.rfind(" (*.") # Find where the description ends and the extension starts.
|
||||||
|
if split < 0: # Not found. Invalid format.
|
||||||
|
Logger.log("e", "Invalid file format identifier %s", file_type)
|
||||||
|
return
|
||||||
|
description = file_type[:split]
|
||||||
|
extension = file_type[split + 4:-1] # Leave out the " (*." and ")".
|
||||||
|
if not file_name.endswith("." + extension): # Auto-fill the extension if the user did not provide any.
|
||||||
|
file_name += "." + extension
|
||||||
|
|
||||||
|
# On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
|
||||||
|
if not Platform.isWindows():
|
||||||
|
if os.path.exists(file_name):
|
||||||
|
result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
|
||||||
|
catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
|
||||||
|
if result == QMessageBox.No:
|
||||||
|
return
|
||||||
|
|
||||||
|
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
|
||||||
|
if not containers:
|
||||||
|
return
|
||||||
|
container = containers[0]
|
||||||
|
|
||||||
|
profile_writer = self._findProfileWriter(extension, description)
|
||||||
|
|
||||||
|
try:
|
||||||
|
success = profile_writer.write(file_name, container)
|
||||||
|
except Exception as e:
|
||||||
|
Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
|
||||||
|
m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)), lifetime = 0)
|
||||||
|
m.show()
|
||||||
|
return
|
||||||
|
if not success:
|
||||||
|
Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
|
||||||
|
m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name), lifetime = 0)
|
||||||
|
m.show()
|
||||||
|
return
|
||||||
|
m = Message(catalog.i18nc("@info:status", "Exported profile to <filename>{0}</filename>", file_name))
|
||||||
|
m.show()
|
||||||
|
|
||||||
|
## Gets the plugin object matching the criteria
|
||||||
|
# \param extension
|
||||||
|
# \param description
|
||||||
|
# \return The plugin object matching the given extension and description.
|
||||||
|
def _findProfileWriter(self, extension, description):
|
||||||
|
pr = PluginRegistry.getInstance()
|
||||||
|
for plugin_id, meta_data in self._getIOPlugins("profile_writer"):
|
||||||
|
for supported_type in meta_data["profile_writer"]: # All file types this plugin can supposedly write.
|
||||||
|
supported_extension = supported_type.get("extension", None)
|
||||||
|
if supported_extension == extension: # This plugin supports a file type with the same extension.
|
||||||
|
supported_description = supported_type.get("description", None)
|
||||||
|
if supported_description == description: # The description is also identical. Assume it's the same file type.
|
||||||
|
return pr.getPluginObject(plugin_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
## Imports a profile from a file
|
||||||
|
#
|
||||||
|
# \param file_name \type{str} the full path and filename of the profile to import
|
||||||
|
# \return \type{Dict} dict with a 'status' key containing the string 'ok' or 'error', and a 'message' key
|
||||||
|
# containing a message for the user
|
||||||
|
def importProfile(self, file_name):
|
||||||
|
if not file_name:
|
||||||
|
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}
|
||||||
|
|
||||||
|
pr = PluginRegistry.getInstance()
|
||||||
|
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
|
||||||
|
profile_reader = pr.getPluginObject(plugin_id)
|
||||||
|
try:
|
||||||
|
profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
|
||||||
|
except Exception as e:
|
||||||
|
#Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
|
||||||
|
Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
|
||||||
|
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
|
||||||
|
if profile: #Success!
|
||||||
|
profile.setReadOnly(False)
|
||||||
|
|
||||||
|
if self._machineHasOwnQualities():
|
||||||
|
profile.setDefinition(self._activeDefinition())
|
||||||
|
if self._machineHasOwnMaterials():
|
||||||
|
profile.addMetaDataEntry("material", self._activeMaterialId())
|
||||||
|
else:
|
||||||
|
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
|
||||||
|
ContainerRegistry.getInstance().addContainer(profile)
|
||||||
|
|
||||||
|
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
|
||||||
|
|
||||||
|
#If it hasn't returned by now, none of the plugins loaded the profile successfully.
|
||||||
|
return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
|
||||||
|
|
||||||
|
## Gets a list of profile writer plugins
|
||||||
|
# \return List of tuples of (plugin_id, meta_data).
|
||||||
|
def _getIOPlugins(self, io_type):
|
||||||
|
pr = PluginRegistry.getInstance()
|
||||||
|
active_plugin_ids = pr.getActivePlugins()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for plugin_id in active_plugin_ids:
|
||||||
|
meta_data = pr.getMetaData(plugin_id)
|
||||||
|
if io_type in meta_data:
|
||||||
|
result.append( (plugin_id, meta_data) )
|
||||||
|
return result
|
||||||
|
|
||||||
|
## Gets the active definition
|
||||||
|
# \return the active definition object or None if there is no definition
|
||||||
|
def _activeDefinition(self):
|
||||||
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if global_container_stack:
|
||||||
|
definition = global_container_stack.getBottom()
|
||||||
|
if definition:
|
||||||
|
return definition
|
||||||
|
return None
|
||||||
|
|
||||||
|
## Returns true if the current machine requires its own materials
|
||||||
|
# \return True if the current machine requires its own materials
|
||||||
|
def _machineHasOwnMaterials(self):
|
||||||
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if global_container_stack:
|
||||||
|
return global_container_stack.getMetaDataEntry("has_materials", False)
|
||||||
|
return False
|
||||||
|
|
||||||
|
## Gets the ID of the active material
|
||||||
|
# \return the ID of the active material or the empty string
|
||||||
|
def _activeMaterialId(self):
|
||||||
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if global_container_stack:
|
||||||
|
material = global_container_stack.findContainer({"type": "material"})
|
||||||
|
if material:
|
||||||
|
return material.getId()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
## Returns true if the current machien requires its own quality profiles
|
||||||
|
# \return true if the current machien requires its own quality profiles
|
||||||
|
def _machineHasOwnQualities(self):
|
||||||
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if global_container_stack:
|
||||||
|
return parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", False))
|
||||||
|
return False
|
|
@ -28,9 +28,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||||
# containers.
|
# containers.
|
||||||
IndexRole = Qt.UserRole + 4
|
IndexRole = Qt.UserRole + 4
|
||||||
|
|
||||||
## Colour to display if there is no material or the material has no known
|
## List of colours to display if there is no material or the material has no known
|
||||||
# colour.
|
# colour.
|
||||||
defaultColour = "#FFFF00"
|
defaultColours = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"]
|
||||||
|
|
||||||
## Initialises the extruders model, defining the roles and listening for
|
## Initialises the extruders model, defining the roles and listening for
|
||||||
# changes in the data.
|
# changes in the data.
|
||||||
|
@ -75,7 +75,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||||
|
|
||||||
if self._add_global:
|
if self._add_global:
|
||||||
material = global_container_stack.findContainer({ "type": "material" })
|
material = global_container_stack.findContainer({ "type": "material" })
|
||||||
colour = material.getMetaDataEntry("color_code", default = self.defaultColour) if material else self.defaultColour
|
colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0]
|
||||||
item = {
|
item = {
|
||||||
"id": global_container_stack.getId(),
|
"id": global_container_stack.getId(),
|
||||||
"name": "Global",
|
"name": "Global",
|
||||||
|
@ -86,12 +86,13 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
||||||
|
|
||||||
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
||||||
material = extruder.findContainer({ "type": "material" })
|
material = extruder.findContainer({ "type": "material" })
|
||||||
colour = material.getMetaDataEntry("color_code", default = self.defaultColour) if material else self.defaultColour
|
|
||||||
position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition.
|
position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition.
|
||||||
try:
|
try:
|
||||||
position = int(position)
|
position = int(position)
|
||||||
except ValueError: #Not a proper int.
|
except ValueError: #Not a proper int.
|
||||||
position = -1
|
position = -1
|
||||||
|
default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else defaultColours[0]
|
||||||
|
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour
|
||||||
item = { #Construct an item with only the relevant information.
|
item = { #Construct an item with only the relevant information.
|
||||||
"id": extruder.getId(),
|
"id": extruder.getId(),
|
||||||
"name": extruder.getName(),
|
"name": extruder.getName(),
|
||||||
|
|
|
@ -34,6 +34,7 @@ sys.excepthook = exceptHook
|
||||||
# tries to create PyQt objects on a non-main thread.
|
# tries to create PyQt objects on a non-main thread.
|
||||||
import Arcus #@UnusedImport
|
import Arcus #@UnusedImport
|
||||||
import cura.CuraApplication
|
import cura.CuraApplication
|
||||||
|
import cura.CuraContainerRegistry
|
||||||
|
|
||||||
if sys.platform == "win32" and hasattr(sys, "frozen"):
|
if sys.platform == "win32" and hasattr(sys, "frozen"):
|
||||||
dirpath = os.path.expanduser("~/AppData/Local/cura/")
|
dirpath = os.path.expanduser("~/AppData/Local/cura/")
|
||||||
|
@ -41,5 +42,8 @@ if sys.platform == "win32" and hasattr(sys, "frozen"):
|
||||||
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
|
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
|
||||||
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
|
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
|
||||||
|
|
||||||
|
# Force an instance of CuraContainerRegistry to be created and reused later.
|
||||||
|
cura.CuraContainerRegistry.CuraContainerRegistry.getInstance()
|
||||||
|
|
||||||
app = cura.CuraApplication.CuraApplication.getInstance()
|
app = cura.CuraApplication.CuraApplication.getInstance()
|
||||||
app.run()
|
app.run()
|
||||||
|
|
|
@ -39,7 +39,6 @@ UM.ManagementPage
|
||||||
section.property: "readOnly"
|
section.property: "readOnly"
|
||||||
section.delegate: Rectangle
|
section.delegate: Rectangle
|
||||||
{
|
{
|
||||||
width: objectListContainer.viewport.width;
|
|
||||||
height: childrenRect.height;
|
height: childrenRect.height;
|
||||||
|
|
||||||
Label
|
Label
|
||||||
|
@ -265,7 +264,7 @@ UM.ManagementPage
|
||||||
id: importDialog;
|
id: importDialog;
|
||||||
title: catalog.i18nc("@title:window", "Import Profile");
|
title: catalog.i18nc("@title:window", "Import Profile");
|
||||||
selectExisting: true;
|
selectExisting: true;
|
||||||
nameFilters: base.model.getFileNameFiltersRead()
|
nameFilters: base.model.getFileNameFilters("profile_reader")
|
||||||
folder: base.model.getDefaultPath()
|
folder: base.model.getDefaultPath()
|
||||||
onAccepted:
|
onAccepted:
|
||||||
{
|
{
|
||||||
|
@ -292,7 +291,7 @@ UM.ManagementPage
|
||||||
id: exportDialog;
|
id: exportDialog;
|
||||||
title: catalog.i18nc("@title:window", "Export Profile");
|
title: catalog.i18nc("@title:window", "Export Profile");
|
||||||
selectExisting: false;
|
selectExisting: false;
|
||||||
nameFilters: base.model.getFileNameFiltersWrite()
|
nameFilters: base.model.getFileNameFilters("profile_writer")
|
||||||
folder: base.model.getDefaultPath()
|
folder: base.model.getDefaultPath()
|
||||||
onAccepted:
|
onAccepted:
|
||||||
{
|
{
|
||||||
|
|
|
@ -141,6 +141,20 @@ Column
|
||||||
control.hovered ? UM.Theme.getColor("toggle_hovered") : UM.Theme.getColor("toggle_unchecked")
|
control.hovered ? UM.Theme.getColor("toggle_hovered") : UM.Theme.getColor("toggle_unchecked")
|
||||||
Behavior on color { ColorAnimation { duration: 50; } }
|
Behavior on color { ColorAnimation { duration: 50; } }
|
||||||
|
|
||||||
|
Rectangle
|
||||||
|
{
|
||||||
|
id: swatch
|
||||||
|
height: UM.Theme.getSize("setting_control").height / 2
|
||||||
|
width: height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: (parent.height - height) / 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
color: model.colour
|
||||||
|
border.width: UM.Theme.getSize("default_lining").width
|
||||||
|
border.color: UM.Theme.getColor("toggle_checked")
|
||||||
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
|
@ -74,6 +74,7 @@ u_viewProjectionMatrix = view_projection_matrix
|
||||||
u_normalMatrix = normal_matrix
|
u_normalMatrix = normal_matrix
|
||||||
u_viewPosition = view_position
|
u_viewPosition = view_position
|
||||||
u_lightPosition = light_0_position
|
u_lightPosition = light_0_position
|
||||||
|
u_diffuseColor = diffuse_color
|
||||||
|
|
||||||
[attributes]
|
[attributes]
|
||||||
a_vertex = vertex
|
a_vertex = vertex
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue