Merge branch 'master' of github.com:Ultimaker/Cura into network_rewrite

This commit is contained in:
Jaime van Kessel 2017-12-19 10:40:32 +01:00
commit bc8741ef08
29 changed files with 581 additions and 223 deletions

32
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,32 @@
<!--
The following template is useful for filing new issues. Processing an issue will go much faster when this is filled out.
Before filing, please check if the issue already exists (either open or closed).
Thank you for using Cura!
-->
Application Version:
<!-- The version of the application this issue occurs with -->
Platform:
<!-- Information about the platform the issue occurs on -->
Qt:
<!-- The version of Qt used (not necessary if you're using the version from Ultimaker's website) -->
PyQt:
<!-- The version of PyQt used (not necessary if you're using the version from Ultimaker's website) -->
Display Driver:
<!-- Video driver name and version -->
Steps to Reproduce:
<!-- Add the steps needed that lead up to the issue (replace this text) -->
Actual Results:
<!-- What happens after the above steps have been followed (replace this text) -->
Expected results:
<!-- What should happen after the above steps have been followed (replace this text) -->
Additional Information:
<!-- Extra information relevant to the issue, like screenshots (replace this text) -->

1
.gitignore vendored
View file

@ -47,6 +47,7 @@ plugins/CuraVariSlicePlugin
plugins/CuraLiveScriptingPlugin plugins/CuraLiveScriptingPlugin
plugins/CuraPrintProfileCreator plugins/CuraPrintProfileCreator
plugins/OctoPrintPlugin plugins/OctoPrintPlugin
plugins/CuraCloudPlugin
#Build stuff #Build stuff
CMakeCache.txt CMakeCache.txt

View file

@ -1,22 +1,16 @@
Cura Cura
==== ====
This is the new, shiny frontend for Cura. Check [daid/LegacyCura](https://github.com/daid/LegacyCura) for the legacy Cura that everyone knows and loves/hates. We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable.
This is the new, shiny frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates.
We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable.
Logging Issues Logging Issues
------------ ------------
Use [this](https://github.com/Ultimaker/Uranium/wiki/Bug-Reporting-Template) template to report issues. New issues that do not adhere to this template will take us a lot longer to handle and will therefore have a lower pirority.
For crashes and similar issues, please attach the following information: For crashes and similar issues, please attach the following information:
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output) * (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output)
* The Cura GUI log file, located at * The Cura GUI log file, located at
* %APPDATA%\cura\\`<Cura version>`\cura.log (Windows), or usually C:\Users\\`<your username>`\AppData\Roaming\cura\\`<Cura version>`\cura.log * `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
* $User/Library/Application Support/cura/`<Cura version>`/cura.log (OSX) * `$USER/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
* $USER/.local/share/cura/`<Cura version>`/cura.log (Ubuntu/Linux) * `$USER/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder
@ -24,53 +18,26 @@ For additional support, you could also ask in the #cura channel on FreeNode IRC.
Dependencies Dependencies
------------ ------------
* [Uranium](https://github.com/Ultimaker/Uranium) Cura is built on top of the Uranium framework.
* [Uranium](https://github.com/Ultimaker/Uranium) * [CuraEngine](https://github.com/Ultimaker/CuraEngine) This will be needed at runtime to perform the actual slicing.
Cura is built on top of the Uranium framework. * [PySerial](https://github.com/pyserial/pyserial) Only required for USB printing support.
* [CuraEngine](https://github.com/Ultimaker/CuraEngine) * [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers
This will be needed at runtime to perform the actual slicing.
* [PySerial](https://github.com/pyserial/pyserial)
Only required for USB printing support.
* [python-zeroconf](https://github.com/jstasiak/python-zeroconf)
Only required to detect mDNS-enabled printers
Configuring Cura
----------------
Link your CuraEngine backend by inserting the following lines in `$HOME/.config/cura/config.cfg` :
```
[backend]
location = /[path_to_the..]/CuraEngine/build/CuraEngine
```
Build scripts Build scripts
------------- -------------
Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.
Please checkout [cura-build](https://github.com/Ultimaker/cura-build) Plugins
Third party plugins
------------- -------------
* [Post Processing Plugin](https://github.com/nallath/PostProcessingPlugin): Allows for post-processing scripts to run on g-code. Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins.
* [Barbarian Plugin](https://github.com/nallath/BarbarianPlugin): Simple scale tool for imperial to metric.
* [X3G Writer](https://github.com/Ghostkeeper/X3GWriter): Adds support for exporting X3G files.
* [Auto orientation](https://github.com/nallath/CuraOrientationPlugin): Calculate the optimal orientation for a model.
* [OctoPrint Plugin](https://github.com/fieldofview/OctoPrintPlugin): Send printjobs directly to OctoPrint and monitor their progress in Cura.
* [Electric Print Cost Calculator Plugin](https://github.com/zoff99/ElectricPrintCostCalculator): Calculate the electric costs of a print.
Making profiles for other printers Supported printers
---------------------------------- -------------
If your make of printer is not in the list of supported printers, and using the "Custom FDM Printer" does not offer enough flexibility, you can use [this](https://github.com/Ultimaker/Cura/blob/master/resources/definitions/ultimaker_original.def.json) as a template. Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura) for guidelines about adding support for new machines.
* Change the machine ID to something unique Configuring Cura
* Change the machine_name to your printer's name ----------------
* If you have a 3D model of your platform you can put it in resources/meshes and put its name under platform Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Cura-Settings) about configuration options for developers.
* Set your machine's dimensions with machine_width, machine_depth, and machine_height
* If your printer's origin is in the center of the bed, set machine_center_is_zero to true.
* Set your print head dimensions with the machine_head_shape parameters
* Set the start and end gcode in machine_start_gcode and machine_end_gcode
Once you are done, put the profile you have made into resources/definitions, or in definitions in your cura profile folder.
If you want to make a definition for a multi-extrusion printer, have a look at [this](https://github.com/Ultimaker/Cura/blob/master/resources/definitions/ultimaker_original_dual.def.json) as a template, along with the two extruder definitions it references [here](https://github.com/Ultimaker/Cura/blob/master/resources/extruders/ultimaker_original_dual_1st.def.json) and [here](https://github.com/Ultimaker/Cura/blob/master/resources/extruders/ultimaker_original_dual_2nd.def.json)
Translating Cura Translating Cura
---------------- ----------------
@ -93,3 +60,7 @@ To submit your translation, ideally you would make two pull requests where all `
After the translation is submitted, the Cura maintainers will check for its completeness and check whether it is consistent. We will take special care to look for common mistakes, such as translating mark-up `<message>` code and such. We are often not fluent in every language, so we expect the translator and the international users to make corrections where necessary. Of course, there will always be some mistakes in every translation. After the translation is submitted, the Cura maintainers will check for its completeness and check whether it is consistent. We will take special care to look for common mistakes, such as translating mark-up `<message>` code and such. We are often not fluent in every language, so we expect the translator and the international users to make corrections where necessary. Of course, there will always be some mistakes in every translation.
When the next Cura release comes around, some of the texts will have changed and some new texts will have been added. Around the time when the beta is released we will invoke a string freeze, meaning that no developer is allowed to make changes to the texts. Then we will update the translation template `.pot` files and ask all our translators to update their translations. If you are unable to update the translation in time for the actual release, we will remove the language from the drop-down menu in the Preferences window. The translation stays in Cura however, so that someone might pick it up again later and update it with the newest texts. Also, users who had previously selected the language can still continue Cura in their language but English text will appear among the original text. When the next Cura release comes around, some of the texts will have changed and some new texts will have been added. Around the time when the beta is released we will invoke a string freeze, meaning that no developer is allowed to make changes to the texts. Then we will update the translation template `.pot` files and ask all our translators to update their translations. If you are unable to update the translation in time for the actual release, we will remove the language from the drop-down menu in the Preferences window. The translation stays in Cura however, so that someone might pick it up again later and update it with the newest texts. Also, users who had previously selected the language can still continue Cura in their language but English text will appear among the original text.
License
----------------
Cura is released under the terms of the LGPLv3 or higher. A copy of this license should be included with the software.

View file

@ -37,7 +37,7 @@ else:
# List of exceptions that should be considered "fatal" and abort the program. # 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 # These are primarily some exception types that we simply cannot really recover from
# (MemoryError and SystemError) and exceptions that indicate grave errors in the # (MemoryError and SystemError) and exceptions that indicate grave errors in the
# code that cause the Python interpreter to fail (SyntaxError, ImportError). # code that cause the Python interpreter to fail (SyntaxError, ImportError).
fatal_exception_types = [ fatal_exception_types = [
MemoryError, MemoryError,
SyntaxError, SyntaxError,
@ -53,7 +53,7 @@ class CrashHandler:
self.exception_type = exception_type self.exception_type = exception_type
self.value = value self.value = value
self.traceback = tb self.traceback = tb
self.dialog = QDialog() self.dialog = None # Don't create a QDialog before there is a QApplication
# While we create the GUI, the information will be stored for sending afterwards # While we create the GUI, the information will be stored for sending afterwards
self.data = dict() self.data = dict()
@ -71,6 +71,7 @@ class CrashHandler:
if not application: if not application:
sys.exit(1) sys.exit(1)
self.dialog = QDialog()
self._createDialog() self._createDialog()
## Creates a modal dialog. ## Creates a modal dialog.
@ -288,4 +289,4 @@ class CrashHandler:
# When the exception is not in the fatal_exception_types list, the dialog is not created, so we don't need to show it # When the exception is not in the fatal_exception_types list, the dialog is not created, so we don't need to show it
if self.dialog: if self.dialog:
self.dialog.exec_() self.dialog.exec_()
os._exit(1) os._exit(1)

View file

@ -127,7 +127,7 @@ class CuraApplication(QtApplication):
# Cura will always show the Add Machine Dialog upon start. # Cura will always show the Add Machine Dialog upon start.
stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished
def __init__(self): def __init__(self, **kwargs):
# this list of dir names will be used by UM to detect an old cura directory # this list of dir names will be used by UM to detect an old cura directory
for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]: for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]:
@ -208,9 +208,12 @@ class CuraApplication(QtApplication):
self._additional_components = {} # Components to add to certain areas in the interface self._additional_components = {} # Components to add to certain areas in the interface
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType, super().__init__(name = "cura",
version = CuraVersion,
buildtype = CuraBuildType,
is_debug_mode = CuraDebugMode, is_debug_mode = CuraDebugMode,
tray_icon_name = "cura-icon-32.png") tray_icon_name = "cura-icon-32.png",
**kwargs)
self.default_theme = "cura-light" self.default_theme = "cura-light"
@ -400,7 +403,11 @@ class CuraApplication(QtApplication):
@pyqtSlot() @pyqtSlot()
def closeApplication(self): def closeApplication(self):
Logger.log("i", "Close application") Logger.log("i", "Close application")
self._main_window.close() main_window = self.getMainWindow()
if main_window is not None:
main_window.close()
else:
self.exit(0)
## A reusable dialogbox ## A reusable dialogbox
# #
@ -508,11 +515,10 @@ class CuraApplication(QtApplication):
self._plugins_loaded = True self._plugins_loaded = True
@classmethod @classmethod
def addCommandLineOptions(self, parser): def addCommandLineOptions(self, parser, parsed_command_line = {}):
super().addCommandLineOptions(parser) super().addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
parser.add_argument("file", nargs="*", help="Files to load after starting the application.") parser.add_argument("file", nargs="*", help="Files to load after starting the application.")
parser.add_argument("--single-instance", action="store_true", default=False) parser.add_argument("--single-instance", action="store_true", default=False)
parser.add_argument("--headless", action = "store_true", default=False)
# Set up a local socket server which listener which coordinates single instances Curas and accepts commands. # Set up a local socket server which listener which coordinates single instances Curas and accepts commands.
def _setUpSingleInstanceServer(self): def _setUpSingleInstanceServer(self):
@ -566,13 +572,16 @@ class CuraApplication(QtApplication):
# This should be called directly before creating an instance of CuraApplication. # This should be called directly before creating an instance of CuraApplication.
# \returns \type{bool} True if the whole Cura app should continue running. # \returns \type{bool} True if the whole Cura app should continue running.
@classmethod @classmethod
def preStartUp(cls): def preStartUp(cls, parser = None, parsed_command_line = {}):
# Peek the arguments and look for the 'single-instance' flag. # Peek the arguments and look for the 'single-instance' flag.
parser = argparse.ArgumentParser(prog="cura") # pylint: disable=bad-whitespace if not parser:
CuraApplication.addCommandLineOptions(parser) parser = argparse.ArgumentParser(prog = "cura", add_help = False) # pylint: disable=bad-whitespace
parsed_command_line = vars(parser.parse_args()) CuraApplication.addCommandLineOptions(parser, parsed_command_line = parsed_command_line)
# Important: It is important to keep this line here!
# In Uranium we allow to pass unknown arguments to the final executable or script.
parsed_command_line.update(vars(parser.parse_known_args()[0]))
if "single_instance" in parsed_command_line and parsed_command_line["single_instance"]: if parsed_command_line["single_instance"]:
Logger.log("i", "Checking for the presence of an ready running Cura instance.") Logger.log("i", "Checking for the presence of an ready running Cura instance.")
single_instance_socket = QLocalSocket() single_instance_socket = QLocalSocket()
Logger.log("d", "preStartUp(): full server name: " + single_instance_socket.fullServerName()) Logger.log("d", "preStartUp(): full server name: " + single_instance_socket.fullServerName())
@ -604,7 +613,22 @@ class CuraApplication(QtApplication):
return False return False
return True return True
def preRun(self):
# Last check for unknown commandline arguments
parser = self.getCommandlineParser()
parser.add_argument("--help", "-h",
action='store_true',
default = False,
help = "Show this help message and exit."
)
parsed_args = vars(parser.parse_args()) # This won't allow unknown arguments
if parsed_args["help"]:
parser.print_help()
sys.exit(0)
def run(self): def run(self):
self.preRun()
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene...")) self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
self._setUpSingleInstanceServer() self._setUpSingleInstanceServer()
@ -659,12 +683,12 @@ class CuraApplication(QtApplication):
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml")) self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles)) self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))
run_headless = self.getCommandLineOption("headless", False) run_without_gui = self.getCommandLineOption("headless", False) or self.getCommandLineOption("invisible", False)
if not run_headless: if not run_without_gui:
self.initializeEngine() self.initializeEngine()
controller.setActiveStage("PrepareStage") controller.setActiveStage("PrepareStage")
if run_headless or self._engine.rootObjects: if run_without_gui or self._engine.rootObjects:
self.closeSplash() self.closeSplash()
for file_name in self.getCommandLineOption("file", []): for file_name in self.getCommandLineOption("file", []):
@ -1362,7 +1386,8 @@ class CuraApplication(QtApplication):
# If a model is to small then it will not contain any points # If a model is to small then it will not contain any points
if offset_shape_arr is None and hull_shape_arr is None: if offset_shape_arr is None and hull_shape_arr is None:
Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."), Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
title=self._i18n_catalog.i18nc("@info:title", "Warning")).show() title=self._i18n_catalog.i18nc("@info:title", "Warning")
).show()
return return
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher # Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher

View file

@ -16,6 +16,7 @@ import math
import os.path import os.path
import unicodedata import unicodedata
import json import json
import re #To create abbreviations for printer names.
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -316,15 +317,14 @@ class PrintInformation(QObject):
return return
global_stack_name = global_container_stack.getName() global_stack_name = global_container_stack.getName()
split_name = global_stack_name.split(" ")
abbr_machine = "" abbr_machine = ""
for word in split_name: for word in re.findall(r"[\w']+", global_stack_name):
if word.lower() == "ultimaker": if word.lower() == "ultimaker":
abbr_machine += "UM" abbr_machine += "UM"
elif word.isdigit(): elif word.isdigit():
abbr_machine += word abbr_machine += word
else: else:
stripped_word = self._stripAccents(word.strip("()[]{}#").upper()) stripped_word = self._stripAccents(word.upper())
# - use only the first character if the word is too long (> 3 characters) # - use only the first character if the word is too long (> 3 characters)
# - use the whole word if it's not too long (<= 3 characters) # - use the whole word if it's not too long (<= 3 characters)
if len(stripped_word) > 3: if len(stripped_word) > 3:

View file

@ -514,7 +514,7 @@ class ContainerManager(QObject):
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
# Find the quality_changes container for this stack and merge the contents of the top container into it. # Find the quality_changes container for this stack and merge the contents of the top container into it.
quality_changes = stack.qualityChanges quality_changes = stack.qualityChanges
if not quality_changes or quality_changes.isReadOnly(): if not quality_changes or self._container_registry.isReadOnly(quality_changes.getId()):
Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId()) Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId())
continue continue
@ -687,7 +687,7 @@ class ContainerManager(QObject):
global_stack = Application.getInstance().getGlobalContainerStack() global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack or not quality_name: if not global_stack or not quality_name:
return "" return ""
machine_definition = global_stack.getBottom() machine_definition = global_stack.definition
active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
if active_stacks is None: if active_stacks is None:

View file

@ -36,6 +36,11 @@ class CuraContainerRegistry(ContainerRegistry):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# We don't have all the machines loaded in the beginning, so in order to add the missing extruder stack
# for single extrusion machines, we subscribe to the containerAdded signal, and whenever a global stack
# is added, we check to see if an extruder stack needs to be added.
self.containerAdded.connect(self._onContainerAdded)
## Overridden from ContainerRegistry ## Overridden from ContainerRegistry
# #
# Adds a container to the registry. # Adds a container to the registry.
@ -410,6 +415,17 @@ class CuraContainerRegistry(ContainerRegistry):
if not extruder_stacks: if not extruder_stacks:
self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder") self.addExtruderStackForSingleExtrusionMachine(machine, "fdmextruder")
def _onContainerAdded(self, container):
# We don't have all the machines loaded in the beginning, so in order to add the missing extruder stack
# for single extrusion machines, we subscribe to the containerAdded signal, and whenever a global stack
# is added, we check to see if an extruder stack needs to be added.
if not isinstance(container, ContainerStack) or container.getMetaDataEntry("type") != "machine":
return
extruder_stacks = self.findContainerStacks(type = "extruder_train", machine = container.getId())
if not extruder_stacks:
self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder")
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id): def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
new_extruder_id = extruder_id new_extruder_id = extruder_id
@ -425,7 +441,6 @@ class CuraContainerRegistry(ContainerRegistry):
extruder_stack.setName(extruder_definition.getName()) extruder_stack.setName(extruder_definition.getName())
extruder_stack.setDefinition(extruder_definition) extruder_stack.setDefinition(extruder_definition)
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
extruder_stack.setNextStack(machine)
# create empty user changes container otherwise # create empty user changes container otherwise
user_container = InstanceContainer(extruder_stack.id + "_user") user_container = InstanceContainer(extruder_stack.id + "_user")
@ -433,7 +448,7 @@ class CuraContainerRegistry(ContainerRegistry):
user_container.addMetaDataEntry("machine", extruder_stack.getId()) user_container.addMetaDataEntry("machine", extruder_stack.getId())
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_container.setDefinition(machine.definition) user_container.setDefinition(machine.definition.getId())
if machine.userChanges: if machine.userChanges:
# for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
@ -444,8 +459,8 @@ class CuraContainerRegistry(ContainerRegistry):
user_container.addInstance(machine.userChanges.getInstance(user_setting_key)) user_container.addInstance(machine.userChanges.getInstance(user_setting_key))
machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) machine.userChanges.removeInstance(user_setting_key, postpone_emit = True)
extruder_stack.setUserChanges(user_container)
self.addContainer(user_container) self.addContainer(user_container)
extruder_stack.setUserChanges(user_container)
variant_id = "default" variant_id = "default"
if machine.variant.getId() not in ("empty", "empty_variant"): if machine.variant.getId() not in ("empty", "empty_variant"):
@ -491,6 +506,9 @@ class CuraContainerRegistry(ContainerRegistry):
self.addContainer(extruder_stack) self.addContainer(extruder_stack)
# Set next stack at the end
extruder_stack.setNextStack(machine)
return extruder_stack return extruder_stack
def _findQualityChangesContainerInCuraFolder(self, name): def _findQualityChangesContainerInCuraFolder(self, name):

View file

@ -377,7 +377,7 @@ class CuraContainerStack(ContainerStack):
if not container or not isinstance(container, DefinitionContainer): if not container or not isinstance(container, DefinitionContainer):
definition = self.findContainer(container_type = DefinitionContainer) definition = self.findContainer(container_type = DefinitionContainer)
if not definition: if not definition:
raise InvalidContainerStackError("Stack {id} does not have a definition!".format(id = self._id)) raise InvalidContainerStackError("Stack {id} does not have a definition!".format(id = self.getId()))
new_containers[index] = definition new_containers[index] = definition
continue continue

View file

@ -368,7 +368,9 @@ class MachineManager(QObject):
self.blurSettings.emit() # Ensure no-one has focus. self.blurSettings.emit() # Ensure no-one has focus.
self._cancelDelayedActiveContainerStackChanges() self._cancelDelayedActiveContainerStackChanges()
containers = ContainerRegistry.getInstance().findContainerStacks(id = stack_id) container_registry = ContainerRegistry.getInstance()
containers = container_registry.findContainerStacks(id = stack_id)
if containers: if containers:
Application.getInstance().setGlobalContainerStack(containers[0]) Application.getInstance().setGlobalContainerStack(containers[0])

View file

@ -2,13 +2,40 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import argparse
import os import os
import sys import sys
import platform
import faulthandler
from UM.Platform import Platform from UM.Platform import Platform
parser = argparse.ArgumentParser(prog = "cura",
add_help = False)
parser.add_argument('--debug',
action='store_true',
default = False,
help = "Turn on the debug mode by setting this option."
)
known_args = vars(parser.parse_known_args()[0])
if not known_args["debug"]:
def get_cura_dir_path():
if Platform.isWindows():
return os.path.expanduser("~/AppData/Roaming/cura/")
elif Platform.isLinux():
return os.path.expanduser("~/.local/share/cura")
elif Platform.isOSX():
return os.path.expanduser("~/Library/Logs/cura")
if hasattr(sys, "frozen"):
dirpath = get_cura_dir_path()
os.makedirs(dirpath, exist_ok = True)
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
import platform
import faulthandler
#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 #WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
# For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
@ -47,8 +74,8 @@ def exceptHook(hook_type, value, traceback):
_crash_handler = CrashHandler(hook_type, value, traceback) _crash_handler = CrashHandler(hook_type, value, traceback)
_crash_handler.show() _crash_handler.show()
if not known_args["debug"]:
sys.excepthook = exceptHook sys.excepthook = exceptHook
# Workaround for a race condition on certain systems where there # Workaround for a race condition on certain systems where there
# is a race condition between Arcus and PyQt. Importing Arcus # is a race condition between Arcus and PyQt. Importing Arcus
@ -58,29 +85,14 @@ import Arcus #@UnusedImport
import cura.CuraApplication import cura.CuraApplication
import cura.Settings.CuraContainerRegistry import cura.Settings.CuraContainerRegistry
def get_cura_dir_path():
if Platform.isWindows():
return os.path.expanduser("~/AppData/Local/cura/")
elif Platform.isLinux():
return os.path.expanduser("~/.local/share/cura")
elif Platform.isOSX():
return os.path.expanduser("~/Library/Logs/cura")
if hasattr(sys, "frozen"):
dirpath = get_cura_dir_path()
os.makedirs(dirpath, exist_ok = True)
sys.stdout = open(os.path.join(dirpath, "stdout.log"), "w")
sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w")
faulthandler.enable() faulthandler.enable()
# Force an instance of CuraContainerRegistry to be created and reused later. # Force an instance of CuraContainerRegistry to be created and reused later.
cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance() cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance()
# This pre-start up check is needed to determine if we should start the application at all. # This pre-start up check is needed to determine if we should start the application at all.
if not cura.CuraApplication.CuraApplication.preStartUp(): if not cura.CuraApplication.CuraApplication.preStartUp(parser = parser, parsed_command_line = known_args):
sys.exit(0) sys.exit(0)
app = cura.CuraApplication.CuraApplication.getInstance() app = cura.CuraApplication.CuraApplication.getInstance(parser = parser, parsed_command_line = known_args)
app.run() app.run()

View file

@ -218,7 +218,7 @@ class StartSliceJob(Job):
result[key] = stack.getProperty(key, "value") result[key] = stack.getProperty(key, "value")
Job.yieldThread() Job.yieldThread()
result["print_bed_temperature"] = result["material_bed_temperature"] #Renamed settings. result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings.
result["print_temperature"] = result["material_print_temperature"] result["print_temperature"] = result["material_print_temperature"]
result["time"] = time.strftime("%H:%M:%S") #Some extra settings. result["time"] = time.strftime("%H:%M:%S") #Some extra settings.
result["date"] = time.strftime("%d-%m-%Y") result["date"] = time.strftime("%d-%m-%Y")
@ -246,10 +246,10 @@ class StartSliceJob(Job):
settings = self._buildReplacementTokens(stack) settings = self._buildReplacementTokens(stack)
#Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it. # Also send the material GUID. This is a setting in fdmprinter, but we have no interface for it.
settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "") settings["material_guid"] = stack.material.getMetaDataEntry("GUID", "")
#Replace the setting tokens in start and end g-code. # Replace the setting tokens in start and end g-code.
settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], settings) settings["machine_extruder_start_code"] = self._expandGcodeTokens(settings["machine_extruder_start_code"], settings)
settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], settings) settings["machine_extruder_end_code"] = self._expandGcodeTokens(settings["machine_extruder_end_code"], settings)
@ -269,18 +269,23 @@ class StartSliceJob(Job):
def _buildGlobalSettingsMessage(self, stack): def _buildGlobalSettingsMessage(self, stack):
settings = self._buildReplacementTokens(stack) settings = self._buildReplacementTokens(stack)
# Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
start_gcode = settings["machine_start_gcode"] start_gcode = settings["machine_start_gcode"]
#Pre-compute material material_bed_temp_prepend and material_print_temp_prepend
bed_temperature_settings = {"material_bed_temperature", "material_bed_temperature_layer_0"} bed_temperature_settings = {"material_bed_temperature", "material_bed_temperature_layer_0"}
settings["material_bed_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in bed_temperature_settings)) settings["material_bed_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in bed_temperature_settings))
print_temperature_settings = {"material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"} print_temperature_settings = {"material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"}
settings["material_print_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in print_temperature_settings)) settings["material_print_temp_prepend"] = all(("{" + setting + "}" not in start_gcode for setting in print_temperature_settings))
#Replace the setting tokens in start and end g-code. # Find the correct temperatures from the first used extruder
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], settings) extruder_stack = Application.getInstance().getExtruderManager().getUsedExtruderStacks()[0]
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], settings) extruder_0_settings = self._buildReplacementTokens(extruder_stack)
for key, value in settings.items(): #Add all submessages for each individual setting. # Replace the setting tokens in start and end g-code.
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], extruder_0_settings)
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], extruder_0_settings)
# Add all sub-messages for each individual setting.
for key, value in settings.items():
setting_message = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings") setting_message = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings")
setting_message.name = key setting_message.name = key
setting_message.value = str(value).encode("utf-8") setting_message.value = str(value).encode("utf-8")

View file

@ -145,6 +145,7 @@ Item {
property var settingDefinitionsModel: addedSettingsModel property var settingDefinitionsModel: addedSettingsModel
property var propertyProvider: provider property var propertyProvider: provider
property var globalPropertyProvider: inheritStackProvider property var globalPropertyProvider: inheritStackProvider
property var externalResetHandler: false
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,

View file

@ -92,7 +92,7 @@ class SimulationPass(RenderPass):
self.bind() self.bind()
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay) tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay, backface_cull = True)
head_position = None # Indicates the current position of the print head head_position = None # Indicates the current position of the print head
nozzle_node = None nozzle_node = None
@ -149,7 +149,7 @@ class SimulationPass(RenderPass):
self._current_shader = self._layer_shader self._current_shader = self._layer_shader
self._switching_layers = True self._switching_layers = True
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end)) layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end), backface_cull = True)
layers_batch.addItem(node.getWorldTransformation(), layer_data) layers_batch.addItem(node.getWorldTransformation(), layer_data)
layers_batch.render(self._scene.getActiveCamera()) layers_batch.render(self._scene.getActiveCamera())

View file

@ -187,20 +187,27 @@ geometry41core =
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert)); myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert)); myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert)); myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head + g_vertex_offset_vert));
//And reverse so that the line is also visible from the back side.
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert));
EndPrimitive(); EndPrimitive();
} else { } else {
// All normal lines are rendered as 3d tubes. // All normal lines are rendered as 3d tubes.
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz)); myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz)); myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert)); myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert)); myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz)); myEmitVertex(v_vertex[0], v_color[0], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz));
myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz)); myEmitVertex(v_vertex[1], v_color[1], g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz));
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert));
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_vert, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert));
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz));
myEmitVertex(v_vertex[1], v_color[1], -g_vertex_normal_horz, u_viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz));
EndPrimitive(); EndPrimitive();

View file

@ -99,7 +99,9 @@ class SliceInfo(Extension):
"type": extruder.material.getMetaData().get("material", ""), "type": extruder.material.getMetaData().get("material", ""),
"brand": extruder.material.getMetaData().get("brand", "") "brand": extruder.material.getMetaData().get("brand", "")
} }
extruder_dict["material_used"] = print_information.materialLengths[int(extruder.getMetaDataEntry("position", "0"))] extruder_position = int(extruder.getMetaDataEntry("position", "0"))
if extruder_position in print_information.materialLengths:
extruder_dict["material_used"] = print_information.materialLengths[extruder_position]
extruder_dict["variant"] = extruder.variant.getName() extruder_dict["variant"] = extruder.variant.getName()
extruder_dict["nozzle_size"] = extruder.getProperty("machine_nozzle_size", "value") extruder_dict["nozzle_size"] = extruder.getProperty("machine_nozzle_size", "value")

View file

@ -122,6 +122,21 @@ class VersionUpgrade30to31(VersionUpgrade):
if len(all_quality_changes) <= 1 and not parser.has_option("metadata", "extruder"): if len(all_quality_changes) <= 1 and not parser.has_option("metadata", "extruder"):
self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser) self._createExtruderQualityChangesForSingleExtrusionMachine(filename, parser)
if parser["metadata"]["type"] == "definition_changes":
if parser["general"]["definition"] == "custom":
# We are only interested in machine_nozzle_size
if parser.has_option("values", "machine_nozzle_size"):
machine_nozzle_size = parser["values"]["machine_nozzle_size"]
definition_name = parser["general"]["name"]
machine_extruders = self._getSingleExtrusionMachineExtruders(definition_name)
#For single extuder machine we nee only first extruder
if len(machine_extruders) !=0:
if self._updateSingleExtuderDefinitionFile(machine_extruders, machine_nozzle_size):
parser.remove_option("values", "machine_nozzle_size")
# Update version numbers # Update version numbers
parser["general"]["version"] = "2" parser["general"]["version"] = "2"
parser["metadata"]["setting_version"] = "4" parser["metadata"]["setting_version"] = "4"
@ -200,6 +215,133 @@ class VersionUpgrade30to31(VersionUpgrade):
return quality_changes_containers return quality_changes_containers
def _getSingleExtrusionMachineExtruders(self, definition_name):
machine_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.MachineStack)
machine_instances = []
#Find all machine instances
for item in os.listdir(machine_instances_dir):
file_path = os.path.join(machine_instances_dir, item)
if not os.path.isfile(file_path):
continue
parser = configparser.ConfigParser(interpolation=None)
try:
parser.read([file_path])
except:
# skip, it is not a valid stack file
continue
if not parser.has_option("metadata", "type"):
continue
if "machine" != parser["metadata"]["type"]:
continue
if not parser.has_option("general", "id"):
continue
machine_instances.append(parser)
#Find for extruders
extruders_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.ExtruderStack)
#"machine",[extruders]
extruder_instances_per_machine = {}
#Find all custom extruders for founded machines
for item in os.listdir(extruders_instances_dir):
file_path = os.path.join(extruders_instances_dir, item)
if not os.path.isfile(file_path):
continue
parser = configparser.ConfigParser(interpolation=None)
try:
parser.read([file_path])
except:
# skip, it is not a valid stack file
continue
if not parser.has_option("metadata", "type"):
continue
if "extruder_train" != parser["metadata"]["type"]:
continue
if not parser.has_option("metadata", "machine"):
continue
if not parser.has_option("metadata", "position"):
continue
for machine_instace in machine_instances:
machine_id = machine_instace["general"]["id"]
if machine_id != parser["metadata"]["machine"]:
continue
if machine_id + "_settings" != definition_name:
continue
if extruder_instances_per_machine.get(machine_id) is None:
extruder_instances_per_machine.update({machine_id:[]})
extruder_instances_per_machine.get(machine_id).append(parser)
#the extruder can be related only to one machine
break
return extruder_instances_per_machine
#Find extruder defition at index 0 and update its values
def _updateSingleExtuderDefinitionFile(self, extruder_instances_per_machine, machine_nozzle_size):
defintion_instances_dir = Resources.getPath(CuraApplication.ResourceTypes.DefinitionChangesContainer)
for item in os.listdir(defintion_instances_dir):
file_path = os.path.join(defintion_instances_dir, item)
if not os.path.isfile(file_path):
continue
parser = configparser.ConfigParser(interpolation=None)
try:
parser.read([file_path])
except:
# skip, it is not a valid stack file
continue
if not parser.has_option("general", "name"):
continue
name = parser["general"]["name"]
custom_extruder_at_0_position = None
for machine_extruders in extruder_instances_per_machine:
for extruder_instance in extruder_instances_per_machine[machine_extruders]:
if extruder_instance["general"]["id"] + "_settings" == name:
defition_position = extruder_instance["metadata"]["position"]
if defition_position == "0":
custom_extruder_at_0_position = extruder_instance
break
if custom_extruder_at_0_position is not None:
break
#If not null, then parsed file is for first extuder and then can be updated. I need to update only
# first, because this update for single extuder machine
if custom_extruder_at_0_position is not None:
#Add new value
parser["values"]["machine_nozzle_size"] = machine_nozzle_size
definition_output = io.StringIO()
parser.write(definition_output)
with open(file_path, "w") as f:
f.write(definition_output.getvalue())
return True
return False
def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes): def _createExtruderQualityChangesForSingleExtrusionMachine(self, filename, global_quality_changes):
suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower()) suffix = "_" + quote_plus(global_quality_changes["general"]["name"].lower())
machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "") machine_name = os.path.os.path.basename(filename).replace(".inst.cfg", "").replace(suffix, "")

View file

@ -737,7 +737,6 @@ class XmlMaterialProfile(InstanceContainer):
Logger.log("w", "No definition found for machine ID %s", machine_id) Logger.log("w", "No definition found for machine ID %s", machine_id)
continue continue
Logger.log("d", "Found def for machine [%s]", machine_id)
definition_metadata = definition_metadata[0] definition_metadata = definition_metadata[0]
machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition. machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition.

View file

@ -46,7 +46,7 @@
"default_value": "RepRap (Marlin/Sprinter)" "default_value": "RepRap (Marlin/Sprinter)"
}, },
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\nM190 S{material_bed_temperature}\nM104 T0 S{material_print_temperature}\nM109 T0 S{material_print_temperature}\nM104 T1 S{material_print_temperature}\nM109 T1 S{material_print_temperature}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position" "default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\nM190 S{material_bed_temperature}\nM104 T0 S{material_print_temperature}\nM109 T0 S{material_print_temperature}\nM104 T1 S{material_print_temperature}\nM109 T1 S{material_print_temperature}\n;G32 S3 ; auto level\nG92 E0 ; Reset extruder position"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "M104 S0 ; turn off extruders\nM140 S0 ; heated bed heater off\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors" "default_value": "M104 S0 ; turn off extruders\nM140 S0 ; heated bed heater off\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"

View file

@ -0,0 +1,71 @@
// Copyright (c) 2017 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import UM 1.2 as UM
import Cura 1.0 as Cura
import "Menus"
ToolButton
{
text: Cura.MachineManager.activeMachineName
tooltip: Cura.MachineManager.activeMachineName
style: ButtonStyle
{
background: Rectangle
{
color:
{
if(control.pressed)
{
return UM.Theme.getColor("sidebar_header_active");
}
else if(control.hovered)
{
return UM.Theme.getColor("sidebar_header_hover");
}
else
{
return UM.Theme.getColor("sidebar_header_bar");
}
}
Behavior on color { ColorAnimation { duration: 50; } }
UM.RecolorImage
{
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.getColor("text_emphasis")
source: UM.Theme.getIcon("arrow_bottom")
}
Label
{
id: sidebarComboBoxLabel
color: UM.Theme.getColor("sidebar_header_text_active")
text: control.text;
elide: Text.ElideRight;
anchors.left: parent.left;
anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2
anchors.right: downArrow.left;
anchors.rightMargin: control.rightMargin;
anchors.verticalCenter: parent.verticalCenter;
font: UM.Theme.getFont("large")
}
}
label: Label {}
}
menu: PrinterMenu { }
}

View file

@ -141,4 +141,4 @@ Column
} }
width: base.width width: base.width
} }
} }

View file

@ -14,7 +14,6 @@ Item {
property real progress: UM.Backend.progress property real progress: UM.Backend.progress
property int backendState: UM.Backend.state property int backendState: UM.Backend.state
property var backend: CuraApplication.getBackend()
property bool activity: CuraApplication.platformActivity property bool activity: CuraApplication.platformActivity
property alias buttonRowWidth: saveRow.width property alias buttonRowWidth: saveRow.width
@ -49,12 +48,14 @@ Item {
} }
function sliceOrStopSlicing() { function sliceOrStopSlicing() {
if (base.backendState != "undefined" && backend !== "undefined") { try {
if ([1, 5].indexOf(base.backendState) != -1) { if ([1, 5].indexOf(base.backendState) != -1) {
backend.forceSlice(); CuraApplication.backend.forceSlice();
} else { } else {
backend.stopSlicing(); CuraApplication.backend.stopSlicing();
} }
} catch (e) {
console.log('Could not start or stop slicing', e)
} }
} }

View file

@ -14,9 +14,9 @@ import "."
Item { Item {
id: base; id: base;
height: UM.Theme.getSize("section").height; height: UM.Theme.getSize("section").height
property alias contents: controlContainer.children; property alias contents: controlContainer.children
property alias hovered: mouse.containsMouse property alias hovered: mouse.containsMouse
property var showRevertButton: true property var showRevertButton: true
@ -179,8 +179,13 @@ Item {
iconSource: UM.Theme.getIcon("reset") iconSource: UM.Theme.getIcon("reset")
onClicked: { onClicked: {
revertButton.focus = true; revertButton.focus = true
Cura.MachineManager.clearUserSettingAllCurrentStacks(propertyProvider.key);
if (externalResetHandler) {
externalResetHandler(propertyProvider.key)
} else {
Cura.MachineManager.clearUserSettingAllCurrentStacks(propertyProvider.key)
}
} }
onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting has a value that is different from the profile.\n\nClick to restore the value of the profile.")) } onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting has a value that is different from the profile.\n\nClick to restore the value of the profile.")) }

View file

@ -287,6 +287,7 @@ Item
property var settingDefinitionsModel: definitionsModel property var settingDefinitionsModel: definitionsModel
property var propertyProvider: provider property var propertyProvider: provider
property var globalPropertyProvider: inheritStackProvider property var globalPropertyProvider: inheritStackProvider
property var externalResetHandler: false
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,

View file

@ -22,7 +22,6 @@ Rectangle
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
property int backendState: UM.Backend.state
property bool monitoringPrint: UM.Controller.activeStage.stageId == "MonitorStage" property bool monitoringPrint: UM.Controller.activeStage.stageId == "MonitorStage"
@ -87,70 +86,12 @@ Rectangle
} }
} }
ToolButton MachineSelection {
{
id: machineSelection id: machineSelection
text: Cura.MachineManager.activeMachineName
width: base.width width: base.width
height: UM.Theme.getSize("sidebar_header").height height: UM.Theme.getSize("sidebar_header").height
tooltip: Cura.MachineManager.activeMachineName
anchors.top: base.top anchors.top: base.top
//anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right anchors.right: parent.right
style: ButtonStyle
{
background: Rectangle
{
color:
{
if(control.pressed)
{
return UM.Theme.getColor("sidebar_header_active");
}
else if(control.hovered)
{
return UM.Theme.getColor("sidebar_header_hover");
}
else
{
return UM.Theme.getColor("sidebar_header_bar");
}
}
Behavior on color { ColorAnimation { duration: 50; } }
UM.RecolorImage
{
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_margin").width
width: UM.Theme.getSize("standard_arrow").width
height: UM.Theme.getSize("standard_arrow").height
sourceSize.width: width
sourceSize.height: width
color: UM.Theme.getColor("text_emphasis")
source: UM.Theme.getIcon("arrow_bottom")
}
Label
{
id: sidebarComboBoxLabel
color: UM.Theme.getColor("sidebar_header_text_active")
text: control.text;
elide: Text.ElideRight;
anchors.left: parent.left;
anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2
anchors.right: downArrow.left;
anchors.rightMargin: control.rightMargin;
anchors.verticalCenter: parent.verticalCenter;
font: UM.Theme.getFont("large")
}
}
label: Label {}
}
menu: PrinterMenu { }
} }
SidebarHeader { SidebarHeader {
@ -330,7 +271,7 @@ Rectangle
{ {
id: controlItem id: controlItem
anchors.bottom: footerSeparator.top anchors.bottom: footerSeparator.top
anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.top: monitoringPrint ? machineSelection.bottom : headerSeparator.bottom
anchors.left: base.left anchors.left: base.left
anchors.right: base.right anchors.right: base.right
sourceComponent: sourceComponent:
@ -349,7 +290,7 @@ Rectangle
Loader Loader
{ {
anchors.bottom: footerSeparator.top anchors.bottom: footerSeparator.top
anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.top: monitoringPrint ? machineSelection.bottom : headerSeparator.bottom
anchors.left: base.left anchors.left: base.left
anchors.right: base.right anchors.right: base.right
source: source:

View file

@ -0,0 +1,34 @@
[general]
version = 2
name = Fine
definition = ultimaker3
[metadata]
type = quality
quality_type = normal
material = generic_bam_ultimaker3_AA_0.4
weight = 0
setting_version = 4
[values]
cool_fan_full_at_height = =layer_height_0 + 2 * layer_height
cool_fan_speed_max = =cool_fan_speed
cool_min_speed = 7
machine_nozzle_cool_down_speed = 0.75
machine_nozzle_heat_up_speed = 1.6
material_print_temperature = =default_material_print_temperature - 10
prime_tower_enable = =min(extruderValues('material_surface_energy')) < 100
skin_overlap = 10
speed_layer_0 = 20
support_interface_enable = True
support_interface_density = =min(extruderValues('material_surface_energy'))
support_interface_pattern = ='lines' if support_interface_density < 100 else 'concentric'
support_top_distance = =math.ceil(min(extruderValues('material_adhesion_tendency')) / 1) * layer_height
support_bottom_distance = =math.ceil(min(extruderValues('material_adhesion_tendency')) / 2) * layer_height
support_angle = 45
support_join_distance = 5
support_offset = 2
support_pattern = triangles
support_infill_rate = 10
top_bottom_thickness = 1
wall_thickness = 1

View file

@ -6,7 +6,9 @@ import pytest #This module contains unit tests.
import shutil #To copy files to make a temporary file. import shutil #To copy files to make a temporary file.
import unittest.mock #To mock and monkeypatch stuff. import unittest.mock #To mock and monkeypatch stuff.
import urllib.parse import urllib.parse
import copy
import cura.CuraApplication
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing. from cura.Settings.CuraContainerRegistry import CuraContainerRegistry #The class we're testing.
from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks. from cura.Settings.ExtruderStack import ExtruderStack #Testing for returning the correct types of stacks.
from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks. from cura.Settings.GlobalStack import GlobalStack #Testing for returning the correct types of stacks.
@ -15,6 +17,32 @@ import UM.Settings.InstanceContainer #Creating instance containers to register.
import UM.Settings.ContainerRegistry #Making empty container stacks. import UM.Settings.ContainerRegistry #Making empty container stacks.
import UM.Settings.ContainerStack #Setting the container registry here properly. import UM.Settings.ContainerStack #Setting the container registry here properly.
from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.addMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
empty_quality_container.addMetaDataEntry("type", "quality")
empty_quality_container.addMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
## Gives a fresh CuraContainerRegistry instance. ## Gives a fresh CuraContainerRegistry instance.
@pytest.fixture() @pytest.fixture()
@ -37,6 +65,7 @@ def teardown():
## Tests whether addContainer properly converts to ExtruderStack. ## Tests whether addContainer properly converts to ExtruderStack.
def test_addContainerExtruderStack(container_registry, definition_container): def test_addContainerExtruderStack(container_registry, definition_container):
creteEmptyContainers()
container_registry.addContainer(definition_container) container_registry.addContainer(definition_container)
container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert. container_stack = UM.Settings.ContainerStack.ContainerStack(stack_id = "Test Container Stack") #A container we're going to convert.
@ -113,36 +142,36 @@ def test_addContainerBadSettingVersion(container_registry, definition_container)
mock_super_add_container.assert_not_called() #Should not get passed on to UM.Settings.ContainerRegistry.addContainer, because the setting_version doesn't match its definition! mock_super_add_container.assert_not_called() #Should not get passed on to UM.Settings.ContainerRegistry.addContainer, because the setting_version doesn't match its definition!
## Tests whether loading gives objects of the correct type. ## Tests whether loading gives objects of the correct type.
@pytest.mark.parametrize("filename, output_class", [ # @pytest.mark.parametrize("filename, output_class", [
("ExtruderLegacy.stack.cfg", ExtruderStack), # ("ExtruderLegacy.stack.cfg", ExtruderStack),
("MachineLegacy.stack.cfg", GlobalStack), # ("MachineLegacy.stack.cfg", GlobalStack),
("Left.extruder.cfg", ExtruderStack), # ("Left.extruder.cfg", ExtruderStack),
("Global.global.cfg", GlobalStack), # ("Global.global.cfg", GlobalStack),
("Global.stack.cfg", GlobalStack) # ("Global.stack.cfg", GlobalStack)
]) # ])
def test_loadTypes(filename, output_class, container_registry): # def test_loadTypes(filename, output_class, container_registry):
#Mock some dependencies. # #Mock some dependencies.
Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file. # Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks", filename)]) #Return just this tested file.
#
def findContainers(container_type = 0, id = None): # def findContainers(container_type = 0, id = None):
if id == "some_instance": # if id == "some_instance":
return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)] # return [UM.Settings.ContainerRegistry._EmptyInstanceContainer(id)]
elif id == "some_definition": # elif id == "some_definition":
return [DefinitionContainer(container_id = id)] # return [DefinitionContainer(container_id = id)]
else: # else:
return [] # return []
#
container_registry.findContainers = findContainers # container_registry.findContainers = findContainers
#
with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"): # with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"):
with unittest.mock.patch("os.remove"): # with unittest.mock.patch("os.remove"):
container_registry.load() # container_registry.load()
#
#Check whether the resulting type was correct. # #Check whether the resulting type was correct.
stack_id = filename.split(".")[0] # stack_id = filename.split(".")[0]
for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates. # for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates.
if container_id == stack_id: #This is the one we're testing. # if container_id == stack_id: #This is the one we're testing.
assert type(container) == output_class # assert type(container) == output_class
break # break
else: # else:
assert False #Container stack with specified ID was not loaded. # assert False #Container stack with specified ID was not loaded.

View file

@ -3,7 +3,9 @@
import pytest #This module contains automated tests. import pytest #This module contains automated tests.
import unittest.mock #For the mocking and monkeypatching functionality. import unittest.mock #For the mocking and monkeypatching functionality.
import copy
import cura.CuraApplication
import UM.Settings.ContainerRegistry #To create empty instance containers. import UM.Settings.ContainerRegistry #To create empty instance containers.
import UM.Settings.ContainerStack #To set the container registry the container stacks use. import UM.Settings.ContainerStack #To set the container registry the container stacks use.
from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer. from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer.
@ -12,6 +14,7 @@ import cura.Settings.ExtruderStack #The module we're testing.
from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised. from cura.Settings.Exceptions import InvalidContainerError, InvalidOperationError #To check whether the correct exceptions are raised.
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
## Fake container registry that always provides all containers you ask of. ## Fake container registry that always provides all containers you ask of.
@pytest.yield_fixture() @pytest.yield_fixture()
@ -32,6 +35,7 @@ def container_registry():
## An empty extruder stack to test with. ## An empty extruder stack to test with.
@pytest.fixture() @pytest.fixture()
def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack: def extruder_stack() -> cura.Settings.ExtruderStack.ExtruderStack:
creteEmptyContainers()
return cura.Settings.ExtruderStack.ExtruderStack("TestStack") return cura.Settings.ExtruderStack.ExtruderStack("TestStack")
## Gets an instance container with a specified container type. ## Gets an instance container with a specified container type.
@ -43,6 +47,31 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.addMetaDataEntry("type", container_type) container.addMetaDataEntry("type", container_type)
return container return container
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.addMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
empty_quality_container.addMetaDataEntry("type", "quality")
empty_quality_container.addMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
class DefinitionContainerSubClass(DefinitionContainer): class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self): def __init__(self):
super().__init__(container_id = "SubDefinitionContainer") super().__init__(container_id = "SubDefinitionContainer")

View file

@ -3,7 +3,9 @@
import pytest #This module contains unit tests. import pytest #This module contains unit tests.
import unittest.mock #To monkeypatch some mocks in place of dependencies. import unittest.mock #To monkeypatch some mocks in place of dependencies.
import copy
import cura.CuraApplication
import cura.Settings.GlobalStack #The module we're testing. import cura.Settings.GlobalStack #The module we're testing.
import cura.Settings.CuraContainerStack #To get the list of container types. import cura.Settings.CuraContainerStack #To get the list of container types.
from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors. from cura.Settings.Exceptions import TooManyExtrudersError, InvalidContainerError, InvalidOperationError #To test raising these errors.
@ -13,6 +15,7 @@ from UM.Settings.SettingInstance import InstanceState
import UM.Settings.ContainerRegistry import UM.Settings.ContainerRegistry
import UM.Settings.ContainerStack import UM.Settings.ContainerStack
import UM.Settings.SettingDefinition #To add settings to the definition. import UM.Settings.SettingDefinition #To add settings to the definition.
from UM.Settings.ContainerRegistry import ContainerRegistry
## Fake container registry that always provides all containers you ask of. ## Fake container registry that always provides all containers you ask of.
@pytest.yield_fixture() @pytest.yield_fixture()
@ -33,6 +36,7 @@ def container_registry():
#An empty global stack to test with. #An empty global stack to test with.
@pytest.fixture() @pytest.fixture()
def global_stack() -> cura.Settings.GlobalStack.GlobalStack: def global_stack() -> cura.Settings.GlobalStack.GlobalStack:
creteEmptyContainers()
return cura.Settings.GlobalStack.GlobalStack("TestStack") return cura.Settings.GlobalStack.GlobalStack("TestStack")
## Gets an instance container with a specified container type. ## Gets an instance container with a specified container type.
@ -44,6 +48,31 @@ def getInstanceContainer(container_type) -> InstanceContainer:
container.addMetaDataEntry("type", container_type) container.addMetaDataEntry("type", container_type)
return container return container
def creteEmptyContainers():
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant")
ContainerRegistry.getInstance().addContainer(empty_variant_container)
empty_material_container = copy.deepcopy(empty_container)
empty_material_container.setMetaDataEntry("id", "empty_material")
empty_material_container.addMetaDataEntry("type", "material")
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container.setMetaDataEntry("id", "empty_quality")
empty_quality_container.setName("Not Supported")
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
empty_quality_container.addMetaDataEntry("type", "quality")
empty_quality_container.addMetaDataEntry("supported", False)
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
class DefinitionContainerSubClass(DefinitionContainer): class DefinitionContainerSubClass(DefinitionContainer):
def __init__(self): def __init__(self):
super().__init__(container_id = "SubDefinitionContainer") super().__init__(container_id = "SubDefinitionContainer")