Merge branch 'master' of https://github.com/Ultimaker/Cura into master-using-platform

This commit is contained in:
Thomas Karl Pietrowski 2016-06-15 14:59:34 +02:00
commit 536e0f489a
33 changed files with 531 additions and 255 deletions

View File

@ -199,7 +199,6 @@ class BuildVolume(SceneNode):
disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value") disallowed_areas = self._active_container_stack.getProperty("machine_disallowed_areas", "value")
areas = [] areas = []
skirt_size = 0.0
skirt_size = self._getSkirtSize(self._active_container_stack) skirt_size = self._getSkirtSize(self._active_container_stack)
if disallowed_areas: if disallowed_areas:

View File

@ -5,17 +5,33 @@ import webbrowser
from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit
from UM.Logger import Logger
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
# List of exceptions that should be considered "fatal" and abort the program.
# These are primarily some exception types that we simply cannot really recover from
# (MemoryError and SystemError) and exceptions that indicate grave errors in the
# code that cause the Python interpreter to fail (SyntaxError, ImportError).
fatal_exception_types = [
MemoryError,
SyntaxError,
ImportError,
SystemError,
]
def show(exception_type, value, tb): def show(exception_type, value, tb):
debug_mode = False debug_mode = False
if QCoreApplication.instance(): if QCoreApplication.instance():
debug_mode = QCoreApplication.instance().getCommandLineOption("debug-mode", False) debug_mode = QCoreApplication.instance().getCommandLineOption("debug-mode", False)
traceback.print_exception(exception_type, value, tb) Logger.log("c", "An uncaught exception has occurred!")
for line in traceback.format_exception(exception_type, value, tb):
for part in line.rstrip("\n").split("\n"):
Logger.log("c", part)
if not debug_mode: if not debug_mode and exception_type not in fatal_exception_types:
return return
application = QCoreApplication.instance() application = QCoreApplication.instance()
@ -29,7 +45,7 @@ def show(exception_type, value, tb):
label = QLabel(dialog) label = QLabel(dialog)
layout.addWidget(label) layout.addWidget(label)
label.setText(catalog.i18nc("@label", "<p>An uncaught exception has occurred!</p><p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>")) label.setText(catalog.i18nc("@label", "<p>A fatal exception has occurred that we could not recover from!</p><p>Please use the information below to post a bug report at <a href=\"http://github.com/Ultimaker/Cura/issues\">http://github.com/Ultimaker/Cura/issues</a></p>"))
textarea = QTextEdit(dialog) textarea = QTextEdit(dialog)
layout.addWidget(textarea) layout.addWidget(textarea)

View File

@ -0,0 +1,208 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import os
import os.path
import re
from PyQt5.QtWidgets import QMessageBox
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer
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("cura")
class CuraContainerRegistry(ContainerRegistry):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
## Create a name that is not empty and unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param current_name \type{} Current name of the container, which may be an acceptable option
# \param new_name \type{string} Base name, which may not be unique
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty
# \return \type{string} Name that is unique for the specified type and name/id
def createUniqueName(self, container_type, current_name, new_name, fallback_name):
new_name = new_name.strip()
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
if num_check:
new_name = num_check.group(1)
if new_name == "":
new_name = fallback_name
unique_name = new_name
i = 1
# In case we are renaming, the current name of the container is also a valid end-result
while self._containerExists(container_type, unique_name) and unique_name != current_name:
i += 1
unique_name = "%s #%d" % (new_name, i)
return unique_name
## Check if a container with of a certain type and a certain name or id exists
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param container_name \type{string} Name to check
def _containerExists(self, container_type, container_name):
container_class = ContainerStack if container_type == "machine" else InstanceContainer
return self.findContainers(container_class, id = container_name, type = container_type) or \
self.findContainers(container_class, name = container_name, type = container_type)
## 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)
new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
catalog.i18nc("@label", "Custom profile"))
profile.setName(new_name)
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

View File

@ -18,7 +18,7 @@ class ExtruderManager(QObject):
## Notify when the user switches the currently active extruder. ## Notify when the user switches the currently active extruder.
activeExtruderChanged = pyqtSignal() activeExtruderChanged = pyqtSignal()
## Registers listeners and such to listen to chafnges to the extruders. ## Registers listeners and such to listen to changes to the extruders.
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs.
@ -67,12 +67,14 @@ class ExtruderManager(QObject):
self.activeExtruderChanged.emit() self.activeExtruderChanged.emit()
def getActiveExtruderStack(self): def getActiveExtruderStack(self):
try: global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getBottom().getId()][str(self._active_extruder_index)] if global_container_stack:
except AttributeError: global_definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom()
return None if global_definition_container:
except KeyError: if global_definition_container.getId() in self._extruder_trains:
return None if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]:
return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)]
## Adds all extruders of a specific machine definition to the extruder ## Adds all extruders of a specific machine definition to the extruder
# manager. # manager.
@ -100,7 +102,7 @@ class ExtruderManager(QObject):
for extruder_train in extruder_trains: for extruder_train in extruder_trains:
self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
## Ensure that the extruder train stacks are linked to global stack. #Ensure that the extruder train stacks are linked to global stack.
extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack())
if extruder_trains: if extruder_trains:
@ -180,7 +182,7 @@ class ExtruderManager(QObject):
quality = qualities[0] quality = qualities[0]
preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality") preferred_quality_id = machine_definition.getMetaDataEntry("preferred_quality")
if preferred_quality_id: if preferred_quality_id:
preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id.lower(), type = "quality") preferred_quality = container_registry.findInstanceContainers(id = preferred_quality_id, type = "quality")
if len(preferred_quality) >= 1: if len(preferred_quality) >= 1:
quality = preferred_quality[0] quality = preferred_quality[0]
else: else:

View File

@ -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(),

View File

@ -1,5 +1,5 @@
# Copyright (c) 2016 Ultimaker B.V.
import re # Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
from UM.Application import Application from UM.Application import Application
@ -8,6 +8,8 @@ from UM.Preferences import Preferences
import UM.Settings import UM.Settings
from UM.Settings.Validator import ValidatorState from UM.Settings.Validator import ValidatorState
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from cura.PrinterOutputDevice import PrinterOutputDevice
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from . import ExtruderManager from . import ExtruderManager
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
@ -49,6 +51,8 @@ class MachineManagerModel(QObject):
active_machine_id = Preferences.getInstance().getValue("cura/active_machine") active_machine_id = Preferences.getInstance().getValue("cura/active_machine")
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
if active_machine_id != "": if active_machine_id != "":
# An active machine was saved, so restore it. # An active machine was saved, so restore it.
self.setActiveMachine(active_machine_id) self.setActiveMachine(active_machine_id)
@ -61,16 +65,14 @@ class MachineManagerModel(QObject):
activeStackChanged = pyqtSignal() activeStackChanged = pyqtSignal()
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed. globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed
@pyqtProperty("QVariantMap", notify = globalContainerChanged) blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
def extrudersIds(self):
## Find all extruders that reference the new stack outputDevicesChanged = pyqtSignal()
extruders = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(**{"machine": self._global_container_stack.getId()})
result = {} def _onOutputDevicesChanged(self):
for extruder in extruders: self.outputDevicesChanged.emit()
result[extruder.getMetaDataEntry("position")] = extruder.getId()
return result
def _onGlobalPropertyChanged(self, key, property_name): def _onGlobalPropertyChanged(self, key, property_name):
if property_name == "value": if property_name == "value":
@ -102,8 +104,15 @@ class MachineManagerModel(QObject):
self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
def _onActiveExtruderStackChanged(self): def _onActiveExtruderStackChanged(self):
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack() self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack()
if not self._active_container_stack: if self._active_container_stack:
self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
else:
self._active_container_stack = self._global_container_stack self._active_container_stack = self._global_container_stack
def _onInstanceContainersChanged(self, container): def _onInstanceContainersChanged(self, container):
@ -155,6 +164,10 @@ class MachineManagerModel(QObject):
Application.getInstance().setGlobalContainerStack(new_global_stack) Application.getInstance().setGlobalContainerStack(new_global_stack)
@pyqtProperty("QVariantList", notify = outputDevicesChanged)
def printerOutputDevices(self):
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
## Create a name that is not empty and unique ## Create a name that is not empty and unique
# \param container_type \type{string} Type of the container (machine, quality, ...) # \param container_type \type{string} Type of the container (machine, quality, ...)
# \param current_name \type{} Current name of the container, which may be an acceptable option # \param current_name \type{} Current name of the container, which may be an acceptable option
@ -162,31 +175,7 @@ class MachineManagerModel(QObject):
# \param fallback_name \type{string} Name to use when (stripped) new_name is empty # \param fallback_name \type{string} Name to use when (stripped) new_name is empty
# \return \type{string} Name that is unique for the specified type and name/id # \return \type{string} Name that is unique for the specified type and name/id
def _createUniqueName(self, container_type, current_name, new_name, fallback_name): def _createUniqueName(self, container_type, current_name, new_name, fallback_name):
new_name = new_name.strip() return UM.Settings.ContainerRegistry.getInstance().createUniqueName(container_type, current_name, new_name, fallback_name)
num_check = re.compile("(.*?)\s*#\d+$").match(new_name)
if num_check:
new_name = num_check.group(1)
if new_name == "":
new_name = fallback_name
unique_name = new_name
i = 1
# In case we are renaming, the current name of the container is also a valid end-result
while self._containerExists(container_type, unique_name) and unique_name != current_name:
i += 1
unique_name = "%s #%d" % (new_name, i)
return unique_name
## Check if a container with of a certain type and a certain name or id exists
# Both the id and the name are checked, because they may not be the same and it is better if they are both unique
# \param container_type \type{string} Type of the container (machine, quality, ...)
# \param container_name \type{string} Name to check
def _containerExists(self, container_type, container_name):
container_class = ContainerStack if container_type == "machine" else InstanceContainer
return UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, id = container_name, type = container_type) or \
UM.Settings.ContainerRegistry.getInstance().findContainers(container_class, name = container_name, type = container_type)
## Convenience function to check if a stack has errors. ## Convenience function to check if a stack has errors.
def _checkStackForErrors(self, stack): def _checkStackForErrors(self, stack):
@ -205,6 +194,7 @@ class MachineManagerModel(QObject):
if not self._active_container_stack: if not self._active_container_stack:
return return
self.blurSettings.emit()
user_settings = self._active_container_stack.getTop() user_settings = self._active_container_stack.getTop()
user_settings.clear() user_settings.clear()
@ -292,6 +282,7 @@ class MachineManagerModel(QObject):
new_container_id = self.duplicateContainer(self.activeQualityId) new_container_id = self.duplicateContainer(self.activeQualityId)
if new_container_id == "": if new_container_id == "":
return return
self.blurSettings.emit()
self.setActiveQuality(new_container_id) self.setActiveQuality(new_container_id)
self.updateQualityContainerFromUserContainer() self.updateQualityContainerFromUserContainer()
@ -435,7 +426,7 @@ class MachineManagerModel(QObject):
@pyqtProperty(str, notify = globalContainerChanged) @pyqtProperty(str, notify = globalContainerChanged)
def activeDefinitionId(self): def activeDefinitionId(self):
if self._active_container_stack: if self._global_container_stack:
definition = self._global_container_stack.getBottom() definition = self._global_container_stack.getBottom()
if definition: if definition:
return definition.id return definition.id
@ -469,15 +460,15 @@ class MachineManagerModel(QObject):
@pyqtProperty(bool, notify = globalContainerChanged) @pyqtProperty(bool, notify = globalContainerChanged)
def hasMaterials(self): def hasMaterials(self):
if self._active_container_stack: if self._global_container_stack:
return bool(self._active_container_stack.getMetaDataEntry("has_materials", False)) return bool(self._global_container_stack.getMetaDataEntry("has_materials", False))
return False return False
@pyqtProperty(bool, notify = globalContainerChanged) @pyqtProperty(bool, notify = globalContainerChanged)
def hasVariants(self): def hasVariants(self):
if self._active_container_stack: if self._global_container_stack:
return bool(self._active_container_stack.getMetaDataEntry("has_variants", False)) return bool(self._global_container_stack.getMetaDataEntry("has_variants", False))
return False return False

View File

@ -3,6 +3,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from enum import IntEnum # For the connection state tracking. from enum import IntEnum # For the connection state tracking.
from UM.Logger import Logger from UM.Logger import Logger
from UM.Signal import signalemitter
## Printer output device adds extra interface options on top of output device. ## Printer output device adds extra interface options on top of output device.
# #
@ -13,7 +14,8 @@ from UM.Logger import Logger
# functions to actually have the implementation. # functions to actually have the implementation.
# #
# For all other uses it should be used in the same way as a "regular" OutputDevice. # For all other uses it should be used in the same way as a "regular" OutputDevice.
class PrinterOutputDevice(OutputDevice, QObject): @signalemitter
class PrinterOutputDevice(QObject, OutputDevice):
def __init__(self, device_id, parent = None): def __init__(self, device_id, parent = None):
super().__init__(device_id = device_id, parent = parent) super().__init__(device_id = device_id, parent = parent)

View File

@ -51,8 +51,8 @@ class SettingOverrideDecorator(SceneNodeDecorator):
def getActiveExtruder(self): def getActiveExtruder(self):
return self._extruder_stack return self._extruder_stack
def _onSettingChanged(self, instance, property): def _onSettingChanged(self, instance, property_name): # Reminder: 'property' is a built-in function
if property == "value": # Only reslice if the value has changed. if property_name == "value": # Only reslice if the value has changed.
Application.getInstance().getBackend().forceSlice() Application.getInstance().getBackend().forceSlice()
## Makes sure that the stack upon which the container stack is placed is ## Makes sure that the stack upon which the container stack is placed is

View File

@ -35,6 +35,7 @@ sys.excepthook = exceptHook
import Arcus #@UnusedImport import Arcus #@UnusedImport
from UM.Platform import Platform from UM.Platform import Platform
import cura.CuraApplication import cura.CuraApplication
import cura.CuraContainerRegistry
if Platform.isWindows() and hasattr(sys, "frozen"): if Platform.isWindows() and hasattr(sys, "frozen"):
dirpath = os.path.expanduser("~/AppData/Local/cura/") dirpath = os.path.expanduser("~/AppData/Local/cura/")
@ -42,5 +43,8 @@ if Platform.isWindows() 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()

View File

@ -113,9 +113,6 @@ class CuraEngineBackend(Backend):
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-vv"] return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-vv"]
def close(self):
self._terminate() # Forcefully shutdown the backend.
## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished. ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished.
# \param time The amount of time the print will take. # \param time The amount of time the print will take.
# \param material_amount The amount of material the print will use. # \param material_amount The amount of material the print will use.

View File

@ -61,7 +61,7 @@ Item {
} }
else else
{ {
return extruders_model.getItem(extruderSelector.currentIndex).colour; return UM.Theme.getColor("setting_control");
} }
} }
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
@ -69,9 +69,22 @@ Item {
} }
label: Item label: Item
{ {
Rectangle
{
id: swatch
height: UM.Theme.getSize("setting_control").height / 2
width: height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.verticalCenter: parent.verticalCenter
color: extruders_model.getItem(extruderSelector.currentIndex).colour
border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
}
Label Label
{ {
anchors.left: parent.left anchors.left: swatch.right
anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.right: downArrow.left anchors.right: downArrow.left
anchors.rightMargin: UM.Theme.getSize("default_lining").width anchors.rightMargin: UM.Theme.getSize("default_lining").width
@ -79,7 +92,7 @@ Item {
text: extruderSelector.currentText text: extruderSelector.currentText
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: UM.Theme.getColor("setting_control_disabled_text") color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
elide: Text.ElideRight elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter

View File

@ -24,19 +24,12 @@ class PerObjectSettingsTool(Tool):
return False return False
def getSelectedObjectId(self): def getSelectedObjectId(self):
try:
selected_object = Selection.getSelectedObject(0) selected_object = Selection.getSelectedObject(0)
if selected_object.getParent().callDecoration("isGroup"):
selected_object = selected_object.getParent()
except:
selected_object = None
selected_object_id = id(selected_object) selected_object_id = id(selected_object)
return selected_object_id return selected_object_id
def getContainerID(self): def getContainerID(self):
selected_object = Selection.getSelectedObject(0) selected_object = Selection.getSelectedObject(0)
if selected_object.getParent().callDecoration("isGroup"):
selected_object = selected_object.getParent()
try: try:
return selected_object.callDecoration("getStack").getId() return selected_object.callDecoration("getStack").getId()
except AttributeError: except AttributeError:

View File

@ -256,7 +256,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if not self.setBaudRate(baud_rate): if not self.setBaudRate(baud_rate):
continue # Could not set the baud rate, go to the next continue # Could not set the baud rate, go to the next
time.sleep(1.5) # Ensure that we are not talking to the bootloader. 1.5 sec seems to be the magic number time.sleep(1.5) # Ensure that we are not talking to the bootloader. 1.5 seconds seems to be the magic number
sucesfull_responses = 0 sucesfull_responses = 0
timeout_time = time.time() + 5 timeout_time = time.time() + 5
self._serial.write(b"\n") self._serial.write(b"\n")

View File

@ -10,18 +10,12 @@
"category": "Other", "category": "Other",
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"platform": "bq_hephestos_platform.stl", "platform": "bq_hephestos_platform.stl",
"platform_offset": { "platform_offset": [ 0, -82, 0]
"value": [
0,
-82,
0
]
}
}, },
"overrides": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --" "default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --"

View File

@ -9,7 +9,7 @@
"manufacturer": "BQ", "manufacturer": "BQ",
"category": "Other", "category": "Other",
"platform": "bq_hephestos_2_platform.stl", "platform": "bq_hephestos_2_platform.stl",
"platform_offset": { "value": [6, 1320, 0 ] }, "platform_offset": [6, 1320, 0 ],
"file_formats": "text/x-gcode" "file_formats": "text/x-gcode"
}, },

View File

@ -10,18 +10,12 @@
"category": "Other", "category": "Other",
"file_formats": "text/x-code", "file_formats": "text/x-code",
"platform": "bq_hephestos_platform.stl", "platform": "bq_hephestos_platform.stl",
"platform_offset": { "platform_offset": [ 0, -82, 0]
"value": [
0,
-82,
0
]
}
}, },
"overrides": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --" "default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG1 Z10 ;move extruder up 10 mm\nG90 ;set to absolute positioning\nG1 X0 Y180 F1200 ;expose the platform\nM84 ;turn off steppers\n; -- end of END GCODE --"

View File

@ -10,18 +10,12 @@
"category": "Other", "category": "Other",
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"platform": "bq_witbox_platform.stl", "platform": "bq_witbox_platform.stl",
"platform_offset": { "platform_offset": [ 0, -145, -38]
"value": [
0,
-145,
-38
]
}
}, },
"overrides": { "overrides": {
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/sec\n; -- end of START GCODE --" "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F1200 ;move Z to position 15.0 mm\nG92 E0 ;zero the extruded length\nG1 E20 F200 ;extrude 20mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F7200 ;set feedrate to 120 mm/s\n; -- end of START GCODE --"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG90 ;set to absolute positioning\nG1 Z200 ;move the platform to the bottom\nG28 X0 Y0 ;move to the X/Y origin (Home)\nM84 ;turn off steppers\n; -- end of END GCODE --" "default_value": "; -- END GCODE --\nM104 S0 ;set extruder temperature to zero (turned off)\nG91 ;set to relative positioning\nG1 E-20 F300 ;retract the filament a bit to release some of the pressure\nG90 ;set to absolute positioning\nG1 Z200 ;move the platform to the bottom\nG28 X0 Y0 ;move to the X/Y origin (Home)\nM84 ;turn off steppers\n; -- end of END GCODE --"

View File

@ -23,8 +23,7 @@
"label": "Extruder", "label": "Extruder",
"description": "The extruder train used for printing. This is used in multi-extrusion.", "description": "The extruder train used for printing. This is used in multi-extrusion.",
"type": "extruder", "type": "extruder",
"default_value": 0, "default_value": "0",
"minimum_value": "0",
"settable_per_mesh": true, "settable_per_mesh": true,
"settable_per_extruder": false, "settable_per_extruder": false,
"settable_per_meshgroup": false, "settable_per_meshgroup": false,

View File

@ -1802,7 +1802,7 @@
{ {
"label": "Regular/Maximum Fan Speed Threshold", "label": "Regular/Maximum Fan Speed Threshold",
"description": "The layer time which sets the threshold between regular fan speed and maximum fan speed. Layers that print slower than this time use regular fan speed. For faster layers the fan speed gradually increases towards the maximum fan speed.", "description": "The layer time which sets the threshold between regular fan speed and maximum fan speed. Layers that print slower than this time use regular fan speed. For faster layers the fan speed gradually increases towards the maximum fan speed.",
"unit": "sec", "unit": "s",
"type": "float", "type": "float",
"default_value": 10, "default_value": 10,
"minimum_value": "cool_min_layer_time", "minimum_value": "cool_min_layer_time",
@ -1842,7 +1842,7 @@
{ {
"label": "Minimum Layer Time", "label": "Minimum Layer Time",
"description": "The minimum time spent in a layer. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer.", "description": "The minimum time spent in a layer. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer.",
"unit": "sec", "unit": "s",
"type": "float", "type": "float",
"default_value": 5, "default_value": 5,
"minimum_value": "0", "minimum_value": "0",
@ -2751,6 +2751,7 @@
"value": "support_extruder_nr", "value": "support_extruder_nr",
"minimum_value": "0", "minimum_value": "0",
"maximum_value": "machine_extruder_count - 1", "maximum_value": "machine_extruder_count - 1",
"enabled": "support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false "settable_per_extruder": false
}, },
@ -2763,6 +2764,7 @@
"value": "support_extruder_nr", "value": "support_extruder_nr",
"minimum_value": "0", "minimum_value": "0",
"maximum_value": "machine_extruder_count - 1", "maximum_value": "machine_extruder_count - 1",
"enabled": "support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false "settable_per_extruder": false
}, },
@ -2775,6 +2777,7 @@
"value": "support_extruder_nr", "value": "support_extruder_nr",
"minimum_value": "0", "minimum_value": "0",
"maximum_value": "machine_extruder_count - 1", "maximum_value": "machine_extruder_count - 1",
"enabled": "support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false "settable_per_extruder": false
} }
@ -3392,7 +3395,7 @@
{ {
"label": "WP Top Delay", "label": "WP Top Delay",
"description": "Delay time after an upward move, so that the upward line can harden. Only applies to Wire Printing.", "description": "Delay time after an upward move, so that the upward line can harden. Only applies to Wire Printing.",
"unit": "sec", "unit": "s",
"type": "float", "type": "float",
"default_value": 0, "default_value": 0,
"minimum_value": "0", "minimum_value": "0",
@ -3406,7 +3409,7 @@
{ {
"label": "WP Bottom Delay", "label": "WP Bottom Delay",
"description": "Delay time after a downward move. Only applies to Wire Printing.", "description": "Delay time after a downward move. Only applies to Wire Printing.",
"unit": "sec", "unit": "s",
"type": "float", "type": "float",
"default_value": 0, "default_value": 0,
"minimum_value": "0", "minimum_value": "0",
@ -3420,7 +3423,7 @@
{ {
"label": "WP Flat Delay", "label": "WP Flat Delay",
"description": "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too long delays cause sagging. Only applies to Wire Printing.", "description": "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too long delays cause sagging. Only applies to Wire Printing.",
"unit": "sec", "unit": "s",
"type": "float", "type": "float",
"default_value": 0.1, "default_value": 0.1,
"minimum_value": "0", "minimum_value": "0",
@ -3550,7 +3553,7 @@
"label": "WP Roof Outer Delay", "label": "WP Roof Outer Delay",
"description": "Time spent at the outer perimeters of hole which is to become a roof. Longer times can ensure a better connection. Only applies to Wire Printing.", "description": "Time spent at the outer perimeters of hole which is to become a roof. Longer times can ensure a better connection. Only applies to Wire Printing.",
"type": "float", "type": "float",
"unit": "sec", "unit": "s",
"default_value": 0.2, "default_value": 0.2,
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "2.0", "maximum_value_warning": "2.0",

View File

@ -154,7 +154,7 @@ UM.Dialog
Button Button
{ {
text:"save" text: catalog.i18nc("@action:button", "Add Printer")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
onClicked: onClicked:

View File

@ -545,16 +545,13 @@ UM.MainWindow
{ {
//; Remove & re-add the general page as we want to use our own instead of uranium standard. //; Remove & re-add the general page as we want to use our own instead of uranium standard.
removePage(0); removePage(0);
insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("GeneralPage.qml")); insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml"));
//: View preferences page title insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml"));
insertPage(1, catalog.i18nc("@title:tab","View"), Qt.resolvedUrl("ViewPage.qml"));
insertPage(3, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("MachinesPage.qml")); insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/MaterialsPage.qml"));
insertPage(4, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/MaterialsPage.qml")); insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml"));
insertPage(5, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml"));
//Force refresh //Force refresh
setPage(0); setPage(0);
@ -583,7 +580,7 @@ UM.MainWindow
onTriggered: onTriggered:
{ {
Cura.MachineManager.newQualityContainerFromQualityAndUser(); Cura.MachineManager.newQualityContainerFromQualityAndUser();
preferences.setPage(5); preferences.setPage(4);
preferences.show(); preferences.show();
// Show the renameDialog after a very short delay so the preference page has time to initiate // Show the renameDialog after a very short delay so the preference page has time to initiate
@ -597,7 +594,7 @@ UM.MainWindow
onTriggered: onTriggered:
{ {
preferences.visible = true; preferences.visible = true;
preferences.setPage(3); preferences.setPage(2);
} }
} }
@ -607,7 +604,7 @@ UM.MainWindow
onTriggered: onTriggered:
{ {
preferences.visible = true; preferences.visible = true;
preferences.setPage(5); preferences.setPage(4);
} }
} }
@ -617,7 +614,7 @@ UM.MainWindow
onTriggered: onTriggered:
{ {
preferences.visible = true; preferences.visible = true;
preferences.setPage(2); preferences.setPage(1);
preferences.getCurrentItem().scrollToSection(source.key); preferences.getCurrentItem().scrollToSection(source.key);
} }
} }

View File

@ -39,6 +39,12 @@ UM.PreferencesPage
scaleTinyCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) scaleTinyCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes"))
UM.Preferences.resetPreference("cura/jobname_prefix") UM.Preferences.resetPreference("cura/jobname_prefix")
prefixJobNameCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) prefixJobNameCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/jobname_prefix"))
UM.Preferences.resetPreference("view/show_overhang");
showOverhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang"))
UM.Preferences.resetPreference("view/center_on_select");
centerOnSelectCheckbox.checked = boolCheck(UM.Preferences.getValue("view/center_on_select"))
UM.Preferences.resetPreference("view/top_layer_count");
topLayerCountCheckbox.checked = boolCheck(UM.Preferences.getValue("view/top_layer_count"))
if (plugins.model.find("id", "SliceInfoPlugin") > -1) { if (plugins.model.find("id", "SliceInfoPlugin") > -1) {
UM.Preferences.resetPreference("info/send_slice_info") UM.Preferences.resetPreference("info/send_slice_info")
@ -50,17 +56,24 @@ UM.PreferencesPage
} }
} }
ColumnLayout Column
{ {
//: Language selection label //: Language selection label
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name:"cura"}
RowLayout Label
{
font.bold: true
text: catalog.i18nc("@label","Interface")
}
Row
{ {
Label Label
{ {
id: languageLabel id: languageLabel
text: catalog.i18nc("@label","Language:") text: catalog.i18nc("@label","Language:")
anchors.verticalCenter: languageComboBox.verticalCenter
} }
ComboBox ComboBox
@ -120,6 +133,51 @@ UM.PreferencesPage
font.italic: true font.italic: true
} }
Item
{
//: Spacer
height: UM.Theme.getSize("default_margin").height
width: UM.Theme.getSize("default_margin").width
}
Label
{
font.bold: true
text: catalog.i18nc("@label","Viewport behavior")
}
UM.TooltipArea
{
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Highlight unsupported areas of the model in red. Without support these areas will not print properly.")
CheckBox
{
id: showOverhangCheckbox
checked: boolCheck(UM.Preferences.getValue("view/show_overhang"))
onClicked: UM.Preferences.setValue("view/show_overhang", checked)
text: catalog.i18nc("@option:check","Display overhang");
}
}
UM.TooltipArea {
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Moves the camera so the object is in the center of the view when an object is selected")
CheckBox
{
id: centerOnSelectCheckbox
text: catalog.i18nc("@action:button","Center camera when item is selected");
checked: boolCheck(UM.Preferences.getValue("view/center_on_select"))
onClicked: UM.Preferences.setValue("view/center_on_select", checked)
}
}
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
@ -134,15 +192,52 @@ UM.PreferencesPage
} }
} }
UM.TooltipArea {
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Display 5 top layers in layer view or only the top-most layer. Rendering 5 layers takes longer, but may show more information.")
CheckBox
{
id: topLayerCountCheckbox
text: catalog.i18nc("@action:button","Display five top layers in layer view");
checked: UM.Preferences.getValue("view/top_layer_count") == 5
onClicked:
{
if(UM.Preferences.getValue("view/top_layer_count") == 5)
{
UM.Preferences.setValue("view/top_layer_count", 1)
}
else
{
UM.Preferences.setValue("view/top_layer_count", 5)
}
}
}
}
Item
{
//: Spacer
height: UM.Theme.getSize("default_margin").height
width: UM.Theme.getSize("default_margin").height
}
Label
{
font.bold: true
text: catalog.i18nc("@label","Opening files")
}
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
text: catalog.i18nc("@info:tooltip","Should opened files be scaled to the build volume if they are too large?") text: catalog.i18nc("@info:tooltip","Should objects be scaled to the build volume if they are too large?")
CheckBox CheckBox
{ {
id: scaleToFitCheckbox id: scaleToFitCheckbox
text: catalog.i18nc("@option:check","Scale large files") text: catalog.i18nc("@option:check","Scale large objects")
checked: boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) checked: boolCheck(UM.Preferences.getValue("mesh/scale_to_fit"))
onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked) onCheckedChanged: UM.Preferences.setValue("mesh/scale_to_fit", checked)
} }
@ -151,17 +246,45 @@ UM.PreferencesPage
UM.TooltipArea { UM.TooltipArea {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
text: catalog.i18nc("@info:tooltip","Should opened files be scaled up if they are extremely small?") text: catalog.i18nc("@info:tooltip","Should objects be scaled up if they are extremely small?")
CheckBox CheckBox
{ {
id: scaleTinyCheckbox id: scaleTinyCheckbox
text: catalog.i18nc("@option:check","Scale extremely small files") text: catalog.i18nc("@option:check","Scale extremely small objects")
checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) checked: boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes"))
onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked) onCheckedChanged: UM.Preferences.setValue("mesh/scale_tiny_meshes", checked)
} }
} }
UM.TooltipArea {
width: childrenRect.width
height: childrenRect.height
text: catalog.i18nc("@info:tooltip", "Should a prefix based on the printer name be added to the print job name automatically?")
CheckBox
{
id: prefixJobNameCheckbox
text: catalog.i18nc("@option:check", "Add machine prefix to job name")
checked: boolCheck(UM.Preferences.getValue("cura/jobname_prefix"))
onCheckedChanged: UM.Preferences.setValue("cura/jobname_prefix", checked)
}
}
Item
{
//: Spacer
height: UM.Theme.getSize("default_margin").height
width: UM.Theme.getSize("default_margin").height
}
Label
{
font.bold: true
visible: checkUpdatesCheckbox.visible || sendDataCheckbox.visible
text: catalog.i18nc("@label","Privacy")
}
UM.TooltipArea { UM.TooltipArea {
visible: plugins.model.find("id", "UpdateChecker") > -1 visible: plugins.model.find("id", "UpdateChecker") > -1
width: childrenRect.width width: childrenRect.width
@ -192,20 +315,7 @@ UM.PreferencesPage
} }
} }
UM.TooltipArea { //: Invisible list used to check if a plugin exists
width: childrenRect.width
height: childrenRect.height
text: catalog.i18nc("@info:tooltip", "Should a prefix based on the printer name be added to the print job name automatically?")
CheckBox
{
id: prefixJobNameCheckbox
text: catalog.i18nc("@option:check", "Add machine prefix to job name")
checked: boolCheck(UM.Preferences.getValue("cura/jobname_prefix"))
onCheckedChanged: UM.Preferences.setValue("cura/jobname_prefix", checked)
}
}
ListView ListView
{ {
id: plugins id: plugins

View File

@ -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:
{ {

View File

@ -24,9 +24,10 @@ Button {
checkable: true checkable: true
checked: definition.expanded checked: definition.expanded
onClicked: definition.expanded ? settingDefinitionsModel.collapse(definition.key) : settingDefinitionsModel.expandAll(definition.key) onClicked: { forceActiveFocus(); definition.expanded ? settingDefinitionsModel.collapse(definition.key) : settingDefinitionsModel.expandAll(definition.key) }
UM.SimpleButton { UM.SimpleButton
{
id: settingsButton id: settingsButton
visible: base.hovered || settingsButton.hovered visible: base.hovered || settingsButton.hovered
@ -60,7 +61,8 @@ Button {
height: parent.height / 2 height: parent.height / 2
width: height width: height
onClicked: { onClicked:
{
base.showAllHiddenInheritedSettings() base.showAllHiddenInheritedSettings()
} }
@ -68,11 +70,13 @@ Button {
hoverColor: UM.Theme.getColor("setting_control_button_hover") hoverColor: UM.Theme.getColor("setting_control_button_hover")
iconSource: UM.Theme.getIcon("notice") iconSource: UM.Theme.getIcon("notice")
onEntered: { onEntered:
{
base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible.")) base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible."))
} }
onExited: { onExited:
{
base.hideTooltip(); base.hideTooltip();
} }

View File

@ -31,7 +31,7 @@ SettingItem
} }
} }
onClicked: propertyProvider.setPropertyValue("value", !checked) onClicked: { forceActiveFocus(); propertyProvider.setPropertyValue("value", !checked) }
Rectangle Rectangle
{ {

View File

@ -86,7 +86,7 @@ SettingItem
} }
} }
onActivated: provider.setPropertyValue("value", definition.options[index].key) onActivated: { forceActiveFocus(); provider.setPropertyValue("value", definition.options[index].key) }
onModelChanged: updateCurrentIndex(); onModelChanged: updateCurrentIndex();
Connections Connections

View File

@ -47,7 +47,7 @@ SettingItem
} }
else else
{ {
return extruders_model.getItem(index).colour; return UM.Theme.getColor("setting_control");
} }
} }
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
@ -55,9 +55,22 @@ SettingItem
} }
label: Item label: Item
{ {
Rectangle
{
id: swatch
height: UM.Theme.getSize("setting_control").height / 2
width: height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.verticalCenter: parent.verticalCenter
color: extruders_model.getItem(control.currentIndex).colour
border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border")
}
Label Label
{ {
anchors.left: parent.left anchors.left: swatch.right
anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.right: downArrow.left anchors.right: downArrow.left
anchors.rightMargin: UM.Theme.getSize("default_lining").width anchors.rightMargin: UM.Theme.getSize("default_lining").width
@ -65,7 +78,7 @@ SettingItem
text: control.currentText text: control.currentText
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : extruders_model.getItem(index).colour color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
elide: Text.ElideRight elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@ -89,7 +102,11 @@ SettingItem
} }
} }
onActivated: provider.setPropertyValue("value", extruders_model.getItem(index).index); onActivated:
{
forceActiveFocus();
provider.setPropertyValue("value", extruders_model.getItem(index).index)
}
onModelChanged: updateCurrentIndex(); onModelChanged: updateCurrentIndex();
Connections Connections

View File

@ -7,6 +7,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.0 as Cura
import "." import "."
@ -245,5 +246,14 @@ Item {
} }
} }
Connections
{
target: Cura.MachineManager
onBlurSettings:
{
revertButton.focus = true
}
}
UM.I18nCatalog { id: catalog; name: "cura" } UM.I18nCatalog { id: catalog; name: "cura" }
} }

View File

@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import UM 1.1 as UM import UM 1.1 as UM
import Cura 1.0 as Cura
Rectangle Rectangle
{ {
@ -14,6 +15,9 @@ Rectangle
property int currentModeIndex; property int currentModeIndex;
// Is there an output device for this printer?
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
color: UM.Theme.getColor("sidebar"); color: UM.Theme.getColor("sidebar");
UM.I18nCatalog { id: catalog; name:"cura"} UM.I18nCatalog { id: catalog; name:"cura"}

View File

@ -141,15 +141,35 @@ 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.verticalCenter: parent.verticalCenter
anchors.left: swatch.right
anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2
color: control.checked ? UM.Theme.getColor("toggle_checked_text") : color: control.checked ? UM.Theme.getColor("toggle_checked_text") :
control.pressed ? UM.Theme.getColor("toggle_active_text") : control.pressed ? UM.Theme.getColor("toggle_active_text") :
control.hovered ? UM.Theme.getColor("toggle_hovered_text") : UM.Theme.getColor("toggle_unchecked_text") control.hovered ? UM.Theme.getColor("toggle_hovered_text") : UM.Theme.getColor("toggle_unchecked_text")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
text: control.text; text: control.text
elide: Text.ElideRight
} }
} }
label: Item { } label: Item { }

View File

@ -1,96 +0,0 @@
// Copyright (c) 2015 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Controls.Styles 1.1
import UM 1.1 as UM
UM.PreferencesPage
{
id: preferencesPage
//: View configuration page title
title: catalog.i18nc("@title:window","View");
function reset()
{
UM.Preferences.resetPreference("view/show_overhang");
UM.Preferences.resetPreference("view/center_on_select");
UM.Preferences.resetPreference("view/top_layer_count");
}
Column
{
UM.I18nCatalog { id: catalog; name:"cura"}
UM.TooltipArea
{
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Highlight unsupported areas of the model in red. Without support these areas will not print properly.")
CheckBox
{
id: overhangCheckbox
checked: boolCheck(UM.Preferences.getValue("view/show_overhang"))
onClicked: UM.Preferences.setValue("view/show_overhang", checked)
text: catalog.i18nc("@option:check","Display overhang");
}
}
UM.TooltipArea {
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Moves the camera so the object is in the center of the view when an object is selected")
CheckBox
{
id: centerCheckbox
text: catalog.i18nc("@action:button","Center camera when item is selected");
checked: boolCheck(UM.Preferences.getValue("view/center_on_select"))
onClicked: UM.Preferences.setValue("view/center_on_select", checked)
}
}
UM.TooltipArea {
width: childrenRect.width;
height: childrenRect.height;
text: catalog.i18nc("@info:tooltip","Display 5 top layers in layer view or only the top-most layer. Rendering 5 layers takes longer, but may show more information.")
CheckBox
{
id: topLayerCheckbox
text: catalog.i18nc("@action:button","Display five top layers in layer view.");
checked: UM.Preferences.getValue("view/top_layer_count") == 5
onClicked:
{
if(UM.Preferences.getValue("view/top_layer_count") == 5)
{
UM.Preferences.setValue("view/top_layer_count", 1)
}
else
{
UM.Preferences.setValue("view/top_layer_count", 5)
}
}
}
}
Connections {
target: UM.Preferences
onPreferenceChanged:
{
overhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang"))
centerCheckbox.checked = boolCheck(UM.Preferences.getValue("view/center_on_select"))
topLayerCheckbox = UM.Preferences.getValue("view/top_layer_count") == 5
}
}
}
}

View File

@ -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