Merge branch 'master' into WIP_improve_initialization

This commit is contained in:
Diego Prado Gesto 2018-05-14 15:15:02 +02:00 committed by GitHub
commit 8ad409ff55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 1810 additions and 778 deletions

1
.gitignore vendored
View file

@ -14,6 +14,7 @@ CuraEngine.exe
LC_MESSAGES LC_MESSAGES
.cache .cache
*.qmlc *.qmlc
.mypy_cache
#MacOS #MacOS
.DS_Store .DS_Store

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 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.
enable_testing() enable_testing()
@ -53,3 +53,9 @@ foreach(_plugin ${_plugins})
cura_add_test(NAME pytest-${_plugin_name} DIRECTORY ${_plugin_directory} PYTHONPATH "${_plugin_directory}|${CMAKE_SOURCE_DIR}|${URANIUM_DIR}") cura_add_test(NAME pytest-${_plugin_name} DIRECTORY ${_plugin_directory} PYTHONPATH "${_plugin_directory}|${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
endif() endif()
endforeach() endforeach()
#Add code style test.
add_test(
NAME "code-style"
COMMAND ${PYTHON_EXECUTABLE} run_mypy.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)

View file

@ -295,7 +295,9 @@ class CuraApplication(QtApplication):
Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources")) Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
if not hasattr(sys, "frozen"): if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) resource_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")
Resources.addSearchPath(resource_path)
Resources.setBundledResourcesPath(resource_path)
# Adds custom property types, settings types, and extra operators (functions) that need to be registered in # Adds custom property types, settings types, and extra operators (functions) that need to be registered in
# SettingDefinition and SettingFunction. # SettingDefinition and SettingFunction.
@ -439,7 +441,7 @@ class CuraApplication(QtApplication):
"RotateTool", "RotateTool",
"ScaleTool", "ScaleTool",
"SelectionTool", "SelectionTool",
"TranslateTool" "TranslateTool",
]) ])
self._i18n_catalog = i18nCatalog("cura") self._i18n_catalog = i18nCatalog("cura")
@ -986,6 +988,8 @@ class CuraApplication(QtApplication):
scene_bounding_box = None scene_bounding_box = None
is_block_slicing_node = False is_block_slicing_node = False
active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
print_information = self.getPrintInformation()
for node in DepthFirstIterator(self.getController().getScene().getRoot()): for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if ( if (
not issubclass(type(node), CuraSceneNode) or not issubclass(type(node), CuraSceneNode) or
@ -997,6 +1001,11 @@ class CuraApplication(QtApplication):
is_block_slicing_node = True is_block_slicing_node = True
count += 1 count += 1
# After clicking the Undo button, if the build plate empty the project name needs to be set
if print_information.baseName == '':
print_information.setBaseName(node.getName())
if not scene_bounding_box: if not scene_bounding_box:
scene_bounding_box = node.getBoundingBox() scene_bounding_box = node.getBoundingBox()
else: else:
@ -1004,7 +1013,7 @@ class CuraApplication(QtApplication):
if other_bb is not None: if other_bb is not None:
scene_bounding_box = scene_bounding_box + node.getBoundingBox() scene_bounding_box = scene_bounding_box + node.getBoundingBox()
print_information = self.getPrintInformation()
if print_information: if print_information:
print_information.setPreSliced(is_block_slicing_node) print_information.setPreSliced(is_block_slicing_node)
@ -1121,39 +1130,6 @@ class CuraApplication(QtApplication):
Selection.add(node) Selection.add(node)
## Delete all nodes containing mesh data in the scene.
# \param only_selectable. Set this to False to delete objects from all build plates
@pyqtSlot()
def deleteAll(self, only_selectable = True):
Logger.log("i", "Clearing scene")
if not self.getController().getToolsEnabled():
return
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not isinstance(node, SceneNode):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if only_selectable and not node.isSelectable():
continue
if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
continue # Only remove nodes that are selectable.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
nodes.append(node)
if nodes:
op = GroupedOperation()
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))
# Reset the print information
self.getController().getScene().sceneChanged.emit(node)
op.push()
Selection.clear()
## Reset all translation on nodes with mesh data. ## Reset all translation on nodes with mesh data.
@pyqtSlot() @pyqtSlot()
def resetAllTranslation(self): def resetAllTranslation(self):

View file

@ -15,12 +15,10 @@ from UM.Logger import Logger
from UM.Resources import Resources from UM.Resources import Resources
from UM.Version import Version from UM.Version import Version
class CuraPackageManager(QObject): class CuraPackageManager(QObject):
Version = 1 Version = 1
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created # The prefix that's added to all files for an installed package to avoid naming conflicts with user created files.
# files.
PREFIX_PLACE_HOLDER = "-CP;" PREFIX_PLACE_HOLDER = "-CP;"
def __init__(self, parent = None): def __init__(self, parent = None):
@ -31,11 +29,19 @@ class CuraPackageManager(QObject):
self._plugin_registry = self._application.getPluginRegistry() self._plugin_registry = self._application.getPluginRegistry()
# JSON file that keeps track of all installed packages. # JSON file that keeps track of all installed packages.
self._package_management_file_path = os.path.join(os.path.abspath(Resources.getDataStoragePath()), self._bundled_package_management_file_path = os.path.join(
"packages.json") os.path.abspath(Resources.getBundledResourcesPath()),
self._installed_package_dict = {} # a dict of all installed packages "packages.json"
self._to_remove_package_set = set() # a set of packages that need to be removed at the next start )
self._to_install_package_dict = {} # a dict of packages that need to be installed at the next start self._user_package_management_file_path = os.path.join(
os.path.abspath(Resources.getDataStoragePath()),
"packages.json"
)
self._bundled_package_dict = {} # A dict of all bundled packages
self._installed_package_dict = {} # A dict of all installed packages
self._to_remove_package_set = set() # A set of packages that need to be removed at the next start
self._to_install_package_dict = {} # A dict of packages that need to be installed at the next start
installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed. installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed.
@ -46,47 +52,61 @@ class CuraPackageManager(QObject):
# (for initialize) Loads the package management file if exists # (for initialize) Loads the package management file if exists
def _loadManagementData(self) -> None: def _loadManagementData(self) -> None:
if not os.path.exists(self._package_management_file_path): # The bundles package management file should always be there
Logger.log("i", "Package management file %s doesn't exist, do nothing", self._package_management_file_path) if not os.path.exists(self._bundled_package_management_file_path):
Logger.log("w", "Bundled package management file could not be found!")
return
# Load the bundled packages:
with open(self._bundled_package_management_file_path, "r", encoding = "utf-8") as f:
self._bundled_package_dict = json.load(f, encoding = "utf-8")
Logger.log("i", "Loaded bundled packages data from %s", self._bundled_package_management_file_path)
# Load the user package management file
if not os.path.exists(self._user_package_management_file_path):
Logger.log("i", "User package management file %s doesn't exist, do nothing", self._user_package_management_file_path)
return return
# Need to use the file lock here to prevent concurrent I/O from other processes/threads # Need to use the file lock here to prevent concurrent I/O from other processes/threads
container_registry = self._application.getContainerRegistry() container_registry = self._application.getContainerRegistry()
with container_registry.lockFile(): with container_registry.lockFile():
with open(self._package_management_file_path, "r", encoding = "utf-8") as f:
management_dict = json.load(f, encoding = "utf-8")
# Load the user packages:
with open(self._user_package_management_file_path, "r", encoding="utf-8") as f:
management_dict = json.load(f, encoding="utf-8")
self._installed_package_dict = management_dict.get("installed", {}) self._installed_package_dict = management_dict.get("installed", {})
self._to_remove_package_set = set(management_dict.get("to_remove", [])) self._to_remove_package_set = set(management_dict.get("to_remove", []))
self._to_install_package_dict = management_dict.get("to_install", {}) self._to_install_package_dict = management_dict.get("to_install", {})
Logger.log("i", "Loaded user packages management file from %s", self._user_package_management_file_path)
Logger.log("i", "Package management file %s is loaded", self._package_management_file_path)
def _saveManagementData(self) -> None: def _saveManagementData(self) -> None:
# Need to use the file lock here to prevent concurrent I/O from other processes/threads # Need to use the file lock here to prevent concurrent I/O from other processes/threads
container_registry = self._application.getContainerRegistry() container_registry = self._application.getContainerRegistry()
with container_registry.lockFile(): with container_registry.lockFile():
with open(self._package_management_file_path, "w", encoding = "utf-8") as f: with open(self._user_package_management_file_path, "w", encoding = "utf-8") as f:
data_dict = {"version": CuraPackageManager.Version, data_dict = {"version": CuraPackageManager.Version,
"installed": self._installed_package_dict, "installed": self._installed_package_dict,
"to_remove": list(self._to_remove_package_set), "to_remove": list(self._to_remove_package_set),
"to_install": self._to_install_package_dict} "to_install": self._to_install_package_dict}
data_dict["to_remove"] = list(data_dict["to_remove"]) data_dict["to_remove"] = list(data_dict["to_remove"])
json.dump(data_dict, f) json.dump(data_dict, f, sort_keys = True, indent = 4)
Logger.log("i", "Package management file %s is saved", self._package_management_file_path) Logger.log("i", "Package management file %s was saved", self._user_package_management_file_path)
# (for initialize) Removes all packages that have been scheduled to be removed. # (for initialize) Removes all packages that have been scheduled to be removed.
def _removeAllScheduledPackages(self) -> None: def _removeAllScheduledPackages(self) -> None:
for package_id in self._to_remove_package_set: for package_id in self._to_remove_package_set:
self._purgePackage(package_id) self._purgePackage(package_id)
del self._installed_package_dict[package_id]
self._to_remove_package_set.clear() self._to_remove_package_set.clear()
self._saveManagementData() self._saveManagementData()
# (for initialize) Installs all packages that have been scheduled to be installed. # (for initialize) Installs all packages that have been scheduled to be installed.
def _installAllScheduledPackages(self) -> None: def _installAllScheduledPackages(self) -> None:
for package_id, installation_package_data in self._to_install_package_dict.items():
self._installPackage(installation_package_data) while self._to_install_package_dict:
self._to_install_package_dict.clear() package_id, package_info = list(self._to_install_package_dict.items())[0]
self._installPackage(package_info)
self._installed_package_dict[package_id] = self._to_install_package_dict[package_id]
del self._to_install_package_dict[package_id]
self._saveManagementData() self._saveManagementData()
# Checks the given package is installed. If so, return a dictionary that contains the package's information. # Checks the given package is installed. If so, return a dictionary that contains the package's information.
@ -99,87 +119,66 @@ class CuraPackageManager(QObject):
return package_info return package_info
if package_id in self._installed_package_dict: if package_id in self._installed_package_dict:
package_info = self._installed_package_dict.get(package_id) package_info = self._installed_package_dict[package_id]["package_info"]
return package_info return package_info
for section, packages in self.getAllInstalledPackagesInfo().items(): if package_id in self._bundled_package_dict:
for package in packages: package_info = self._bundled_package_dict[package_id]["package_info"]
if package["package_id"] == package_id: return package_info
return package
return None return None
def getAllInstalledPackagesInfo(self) -> dict: def getAllInstalledPackagesInfo(self) -> dict:
installed_package_id_set = set(self._installed_package_dict.keys()) | set(self._to_install_package_dict.keys()) # Add bundled, installed, and to-install packages to the set of installed package IDs
installed_package_id_set = installed_package_id_set.difference(self._to_remove_package_set) all_installed_ids = set()
managed_package_id_set = installed_package_id_set | self._to_remove_package_set if self._bundled_package_dict.keys():
all_installed_ids = all_installed_ids.union(set(self._bundled_package_dict.keys()))
# TODO: For absolutely no reason, this function seems to run in a loop if self._installed_package_dict.keys():
# even though no loop is ever called with it. all_installed_ids = all_installed_ids.union(set(self._installed_package_dict.keys()))
if self._to_install_package_dict.keys():
all_installed_ids = all_installed_ids.union(set(self._to_install_package_dict.keys()))
all_installed_ids = all_installed_ids.difference(self._to_remove_package_set)
# map of <package_type> -> <package_id> -> <package_info> # map of <package_type> -> <package_id> -> <package_info>
installed_packages_dict = {} installed_packages_dict = {}
for package_id in installed_package_id_set: for package_id in all_installed_ids:
# Skip required plugins as they should not be tampered with
if package_id in Application.getInstance().getRequiredPlugins(): if package_id in Application.getInstance().getRequiredPlugins():
continue continue
package_info = None
# Add bundled plugins
if package_id in self._bundled_package_dict:
package_info = self._bundled_package_dict[package_id]["package_info"]
# Add installed plugins
if package_id in self._installed_package_dict:
package_info = self._installed_package_dict[package_id]["package_info"]
# Add to install plugins
if package_id in self._to_install_package_dict: if package_id in self._to_install_package_dict:
package_info = self._to_install_package_dict[package_id]["package_info"] package_info = self._to_install_package_dict[package_id]["package_info"]
else:
package_info = self._installed_package_dict[package_id]
package_info["is_bundled"] = False
package_type = package_info["package_type"] if package_info is None:
if package_type not in installed_packages_dict: continue
installed_packages_dict[package_type] = []
installed_packages_dict[package_type].append( package_info )
# We also need to get information from the plugin registry such as if a plugin is active # We also need to get information from the plugin registry such as if a plugin is active
package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id) package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
# Also get all bundled plugins # If the package ID is in bundled, label it as such
all_metadata = self._plugin_registry.getAllMetaData() package_info["is_bundled"] = package_info["package_id"] in self._bundled_package_dict.keys()
for item in all_metadata:
if item == {}:
continue
plugin_package_info = self.__convertPluginMetadataToPackageMetadata(item) # If there is not a section in the dict for this type, add it
# Only gather the bundled plugins here. if package_info["package_type"] not in installed_packages_dict:
package_id = plugin_package_info["package_id"] installed_packages_dict[package_info["package_type"]] = []
if package_id in managed_package_id_set:
continue
if package_id in Application.getInstance().getRequiredPlugins():
continue
plugin_package_info["is_bundled"] = True if plugin_package_info["author"]["display_name"] == "Ultimaker B.V." else False # Finally, add the data
plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id) installed_packages_dict[package_info["package_type"]].append(package_info)
package_type = "plugin"
if package_type not in installed_packages_dict:
installed_packages_dict[package_type] = []
installed_packages_dict[package_type].append( plugin_package_info )
return installed_packages_dict return installed_packages_dict
def __convertPluginMetadataToPackageMetadata(self, plugin_metadata: dict) -> dict:
package_metadata = {
"package_id": plugin_metadata["id"],
"package_type": "plugin",
"display_name": plugin_metadata["plugin"]["name"],
"description": plugin_metadata["plugin"].get("description"),
"package_version": plugin_metadata["plugin"]["version"],
"cura_version": int(plugin_metadata["plugin"]["api"]),
"website": "",
"author_id": plugin_metadata["plugin"].get("author", "UnknownID"),
"author": {
"author_id": plugin_metadata["plugin"].get("author", "UnknownID"),
"display_name": plugin_metadata["plugin"].get("author", ""),
"email": "",
"website": "",
},
"tags": ["plugin"],
}
return package_metadata
# Checks if the given package is installed. # Checks if the given package is installed.
def isPackageInstalled(self, package_id: str) -> bool: def isPackageInstalled(self, package_id: str) -> bool:
return self.getInstalledPackageInfo(package_id) is not None return self.getInstalledPackageInfo(package_id) is not None
@ -293,7 +292,7 @@ class CuraPackageManager(QObject):
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
installation_dirs_dict = { installation_dirs_dict = {
"materials": Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer), "materials": Resources.getStoragePath(CuraApplication.ResourceTypes.MaterialInstanceContainer),
"quality": Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer), "qualities": Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer),
"plugins": os.path.abspath(Resources.getStoragePath(Resources.Plugins)), "plugins": os.path.abspath(Resources.getStoragePath(Resources.Plugins)),
} }

View file

@ -14,6 +14,8 @@ from UM.i18n import i18nCatalog
from UM.Logger import Logger from UM.Logger import Logger
from UM.Qt.Duration import Duration from UM.Qt.Duration import Duration
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.i18n import i18nCatalog
from UM.MimeTypeDatabase import MimeTypeDatabase
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -321,7 +323,7 @@ class PrintInformation(QObject):
# when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its
# extension. This cuts the extension off if necessary. # extension. This cuts the extension off if necessary.
name = os.path.splitext(name)[0] check_name = os.path.splitext(name)[0]
filename_parts = os.path.basename(base_name).split(".") filename_parts = os.path.basename(base_name).split(".")
# If it's a gcode, also always update the job name # If it's a gcode, also always update the job name
@ -332,21 +334,21 @@ class PrintInformation(QObject):
# if this is a profile file, always update the job name # if this is a profile file, always update the job name
# name is "" when I first had some meshes and afterwards I deleted them so the naming should start again # name is "" when I first had some meshes and afterwards I deleted them so the naming should start again
is_empty = name == "" is_empty = check_name == ""
if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != name)): if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != check_name)):
# Only take the file name part, Note : file name might have 'dot' in name as well # Only take the file name part, Note : file name might have 'dot' in name as well
if is_project_file:
# This is for .curaproject
self._base_name = ".".join(filename_parts)
elif len(filename_parts) > 1:
if "gcode" in filename_parts:
gcode_index = filename_parts.index('gcode')
self._base_name = ".".join(filename_parts[0:gcode_index])
else:
self._base_name = name
else:
self._base_name = name
data = ''
try:
mime_type = MimeTypeDatabase.getMimeTypeForFile(name)
data = mime_type.stripExtension(name)
except:
Logger.log("w", "Unsupported Mime Type Database file extension")
if data is not None:
self._base_name = data
else:
self._base_name = ''
self._updateJobName() self._updateJobName()

View file

@ -385,7 +385,8 @@ class ExtruderManager(QObject):
# Register the extruder trains by position # Register the extruder trains by position
for extruder_train in extruder_trains: for extruder_train in extruder_trains:
self._extruder_trains[global_stack_id][extruder_train.getMetaDataEntry("position")] = extruder_train extruder_position = extruder_train.getMetaDataEntry("position")
self._extruder_trains[global_stack_id][extruder_position] = extruder_train
# regardless of what the next stack is, we have to set it again, because of signal routing. ??? # regardless of what the next stack is, we have to set it again, because of signal routing. ???
extruder_train.setParent(global_stack) extruder_train.setParent(global_stack)

View file

@ -38,7 +38,7 @@ class ExtruderStack(CuraContainerStack):
# #
# This will set the next stack and ensure that we register this stack as an extruder. # This will set the next stack and ensure that we register this stack as an extruder.
@override(ContainerStack) @override(ContainerStack)
def setNextStack(self, stack: CuraContainerStack) -> None: def setNextStack(self, stack: CuraContainerStack, connect_signals: bool = True) -> None:
super().setNextStack(stack) super().setNextStack(stack)
stack.addExtruder(self) stack.addExtruder(self)
self.addMetaDataEntry("machine", stack.id) self.addMetaDataEntry("machine", stack.id)

View file

@ -125,7 +125,7 @@ class GlobalStack(CuraContainerStack):
# #
# This will simply raise an exception since the Global stack cannot have a next stack. # This will simply raise an exception since the Global stack cannot have a next stack.
@override(ContainerStack) @override(ContainerStack)
def setNextStack(self, next_stack: ContainerStack) -> None: def setNextStack(self, stack: CuraContainerStack, connect_signals: bool = True) -> None:
raise Exceptions.InvalidOperationError("Global stack cannot have a next stack!") raise Exceptions.InvalidOperationError("Global stack cannot have a next stack!")
# protected: # protected:
@ -153,6 +153,23 @@ class GlobalStack(CuraContainerStack):
return True return True
## Perform some sanity checks on the global stack
# Sanity check for extruders; they must have positions 0 and up to machine_extruder_count - 1
def isValid(self):
container_registry = ContainerRegistry.getInstance()
extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = self.getId())
machine_extruder_count = self.getProperty("machine_extruder_count", "value")
extruder_check_position = set()
for extruder_train in extruder_trains:
extruder_position = extruder_train.getMetaDataEntry("position")
extruder_check_position.add(extruder_position)
for check_position in range(machine_extruder_count):
if str(check_position) not in extruder_check_position:
return False
return True
## private: ## private:
global_stack_mime = MimeType( global_stack_mime = MimeType(

View file

@ -6,6 +6,7 @@ import time
#Type hinting. #Type hinting.
from typing import List, Dict, TYPE_CHECKING, Optional from typing import List, Dict, TYPE_CHECKING, Optional
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.Interfaces import ContainerInterface from UM.Settings.Interfaces import ContainerInterface
@ -166,8 +167,6 @@ class MachineManager(QObject):
if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id): if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = 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)
# Make sure _active_container_stack is properly initiated
ExtruderManager.getInstance().setActiveExtruderIndex(0)
def _onOutputDevicesChanged(self) -> None: def _onOutputDevicesChanged(self) -> None:
self._printer_output_devices = [] self._printer_output_devices = []
@ -358,6 +357,10 @@ class MachineManager(QObject):
return return
global_stack = containers[0] global_stack = containers[0]
if not global_stack.isValid():
# Mark global stack as invalid
ConfigurationErrorMessage.getInstance().addFaultyContainers(global_stack.getId())
return # We're done here
ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
self._global_container_stack = global_stack self._global_container_stack = global_stack
self._application.setGlobalContainerStack(global_stack) self._application.setGlobalContainerStack(global_stack)

View file

@ -15,6 +15,7 @@ from UM.Math.Vector import Vector
from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Mesh.MeshReader import MeshReader from UM.Mesh.MeshReader import MeshReader
from UM.Scene.GroupDecorator import GroupDecorator from UM.Scene.GroupDecorator import GroupDecorator
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.CuraSceneNode import CuraSceneNode
@ -25,6 +26,15 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
MYPY = False MYPY = False
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-project-file",
comment = "Cura Project File",
suffixes = ["curaproject.3mf"]
)
)
try: try:
if not MYPY: if not MYPY:
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET

View file

@ -66,8 +66,8 @@ Generate a cube mesh to prevent support material generation in specific areas of
*Real bridging - smartavionics *Real bridging - smartavionics
New experimental feature that detects bridges, adjusting the print speed, slow and fan speed to enhance print quality on bridging parts. New experimental feature that detects bridges, adjusting the print speed, slow and fan speed to enhance print quality on bridging parts.
*Updated CuraEngine executable - thopiekar *Updated CuraEngine executable - thopiekar & Ultimaker B.V. ❤️
The CuraEngine executable now contains a dedicated icon, author information and a license. The CuraEngine executable contains a dedicated icon, author and license info on Windows now. The icon has been designed by Ultimaker B.V.
*Use RapidJSON and ClipperLib from system libraries *Use RapidJSON and ClipperLib from system libraries
Application updated to use verified copies of libraries, reducing maintenance time keeping them up to date (the operating system is now responsible), as well as reducing the amount of code shipped (as necessary code is already on the users system). Application updated to use verified copies of libraries, reducing maintenance time keeping them up to date (the operating system is now responsible), as well as reducing the amount of code shipped (as necessary code is already on the users system).

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 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.
from UM.Application import Application from UM.Application import Application
@ -22,12 +22,16 @@ from cura.Settings.ExtruderManager import ExtruderManager
import numpy import numpy
import math import math
import re import re
from typing import Dict, List, NamedTuple, Optional, Union
from collections import namedtuple from collections import namedtuple
# This parser is intented for interpret the common firmware codes among all the different flavors Position = NamedTuple("Position", [("x", float), ("y", float), ("z", float), ("f", float), ("e", float)])
## This parser is intended to interpret the common firmware codes among all the
# different flavors
class FlavorParser: class FlavorParser:
def __init__(self): def __init__(self) -> None:
Application.getInstance().hideMessageSignal.connect(self._onHideMessage) Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
self._cancelled = False self._cancelled = False
self._message = None self._message = None
@ -44,19 +48,18 @@ class FlavorParser:
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True) Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
def _clearValues(self): def _clearValues(self) -> None:
self._extruder_number = 0 self._extruder_number = 0
self._extrusion_length_offset = [0] self._extrusion_length_offset = [0]
self._layer_type = LayerPolygon.Inset0Type self._layer_type = LayerPolygon.Inset0Type
self._layer_number = 0 self._layer_number = 0
self._previous_z = 0 self._previous_z = 0
self._layer_data_builder = LayerDataBuilder.LayerDataBuilder() self._layer_data_builder = LayerDataBuilder.LayerDataBuilder()
self._center_is_zero = False
self._is_absolute_positioning = True # It can be absolute (G90) or relative (G91) self._is_absolute_positioning = True # It can be absolute (G90) or relative (G91)
self._is_absolute_extrusion = True # It can become absolute (M82, default) or relative (M83) self._is_absolute_extrusion = True # It can become absolute (M82, default) or relative (M83)
@staticmethod @staticmethod
def _getValue(line, code): def _getValue(line: str, code: str) -> Optional[Union[str, int, float]]:
n = line.find(code) n = line.find(code)
if n < 0: if n < 0:
return None return None
@ -71,29 +74,29 @@ class FlavorParser:
except: except:
return None return None
def _getInt(self, line, code): def _getInt(self, line: str, code: str) -> Optional[int]:
value = self._getValue(line, code) value = self._getValue(line, code)
try: try:
return int(value) return int(value)
except: except:
return None return None
def _getFloat(self, line, code): def _getFloat(self, line: str, code: str) -> Optional[float]:
value = self._getValue(line, code) value = self._getValue(line, code)
try: try:
return float(value) return float(value)
except: except:
return None return None
def _onHideMessage(self, message): def _onHideMessage(self, message: str) -> None:
if message == self._message: if message == self._message:
self._cancelled = True self._cancelled = True
@staticmethod @staticmethod
def _getNullBoundingBox(): def _getNullBoundingBox() -> AxisAlignedBox:
return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10)) return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
def _createPolygon(self, layer_thickness, path, extruder_offsets): def _createPolygon(self, layer_thickness: float, path: List[List[Union[float, int]]], extruder_offsets: List[float]) -> bool:
countvalid = 0 countvalid = 0
for point in path: for point in path:
if point[5] > 0: if point[5] > 0:
@ -139,12 +142,12 @@ class FlavorParser:
this_layer.polygons.append(this_poly) this_layer.polygons.append(this_poly)
return True return True
def _createEmptyLayer(self, layer_number): def _createEmptyLayer(self, layer_number: int) -> None:
self._layer_data_builder.addLayer(layer_number) self._layer_data_builder.addLayer(layer_number)
self._layer_data_builder.setLayerHeight(layer_number, 0) self._layer_data_builder.setLayerHeight(layer_number, 0)
self._layer_data_builder.setLayerThickness(layer_number, 0) self._layer_data_builder.setLayerThickness(layer_number, 0)
def _calculateLineWidth(self, current_point, previous_point, current_extrusion, previous_extrusion, layer_thickness): def _calculateLineWidth(self, current_point: Position, previous_point: Position, current_extrusion: float, previous_extrusion: float, layer_thickness: float) -> float:
# Area of the filament # Area of the filament
Af = (self._filament_diameter / 2) ** 2 * numpy.pi Af = (self._filament_diameter / 2) ** 2 * numpy.pi
# Length of the extruded filament # Length of the extruded filament
@ -166,7 +169,7 @@ class FlavorParser:
return 0.35 return 0.35
return line_width return line_width
def _gCode0(self, position, params, path): def _gCode0(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
x, y, z, f, e = position x, y, z, f, e = position
if self._is_absolute_positioning: if self._is_absolute_positioning:
@ -202,7 +205,7 @@ class FlavorParser:
_gCode1 = _gCode0 _gCode1 = _gCode0
## Home the head. ## Home the head.
def _gCode28(self, position, params, path): def _gCode28(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
return self._position( return self._position(
params.x if params.x is not None else position.x, params.x if params.x is not None else position.x,
params.y if params.y is not None else position.y, params.y if params.y is not None else position.y,
@ -211,20 +214,20 @@ class FlavorParser:
position.e) position.e)
## Set the absolute positioning ## Set the absolute positioning
def _gCode90(self, position, params, path): def _gCode90(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
self._is_absolute_positioning = True self._is_absolute_positioning = True
self._is_absolute_extrusion = True self._is_absolute_extrusion = True
return position return position
## Set the relative positioning ## Set the relative positioning
def _gCode91(self, position, params, path): def _gCode91(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
self._is_absolute_positioning = False self._is_absolute_positioning = False
self._is_absolute_extrusion = False self._is_absolute_extrusion = False
return position return position
## Reset the current position to the values specified. ## Reset the current position to the values specified.
# For example: G92 X10 will set the X to 10 without any physical motion. # For example: G92 X10 will set the X to 10 without any physical motion.
def _gCode92(self, position, params, path): def _gCode92(self, position: Position, params: Position, path: List[List[Union[float, int]]]) -> Position:
if params.e is not None: if params.e is not None:
# Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width # Sometimes a G92 E0 is introduced in the middle of the GCode so we need to keep those offsets for calculate the line_width
self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e self._extrusion_length_offset[self._extruder_number] += position.e[self._extruder_number] - params.e
@ -236,7 +239,7 @@ class FlavorParser:
params.f if params.f is not None else position.f, params.f if params.f is not None else position.f,
position.e) position.e)
def processGCode(self, G, line, position, path): def processGCode(self, G: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position:
func = getattr(self, "_gCode%s" % G, None) func = getattr(self, "_gCode%s" % G, None)
line = line.split(";", 1)[0] # Remove comments (if any) line = line.split(";", 1)[0] # Remove comments (if any)
if func is not None: if func is not None:
@ -257,27 +260,25 @@ class FlavorParser:
f = float(item[1:]) / 60 f = float(item[1:]) / 60
if item[0] == "E": if item[0] == "E":
e = float(item[1:]) e = float(item[1:])
if self._is_absolute_positioning and ((x is not None and x < 0) or (y is not None and y < 0)):
self._center_is_zero = True
params = self._position(x, y, z, f, e) params = self._position(x, y, z, f, e)
return func(position, params, path) return func(position, params, path)
return position return position
def processTCode(self, T, line, position, path): def processTCode(self, T: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position:
self._extruder_number = T self._extruder_number = T
if self._extruder_number + 1 > len(position.e): if self._extruder_number + 1 > len(position.e):
self._extrusion_length_offset.extend([0] * (self._extruder_number - len(position.e) + 1)) self._extrusion_length_offset.extend([0] * (self._extruder_number - len(position.e) + 1))
position.e.extend([0] * (self._extruder_number - len(position.e) + 1)) position.e.extend([0] * (self._extruder_number - len(position.e) + 1))
return position return position
def processMCode(self, M, line, position, path): def processMCode(self, M: int, line: str, position: Position, path: List[List[Union[float, int]]]) -> Position:
pass pass
_type_keyword = ";TYPE:" _type_keyword = ";TYPE:"
_layer_keyword = ";LAYER:" _layer_keyword = ";LAYER:"
## For showing correct x, y offsets for each extruder ## For showing correct x, y offsets for each extruder
def _extruderOffsets(self): def _extruderOffsets(self) -> Dict[int, List[float]]:
result = {} result = {}
for extruder in ExtruderManager.getInstance().getExtruderStacks(): for extruder in ExtruderManager.getInstance().getExtruderStacks():
result[int(extruder.getMetaData().get("position", "0"))] = [ result[int(extruder.getMetaData().get("position", "0"))] = [
@ -285,7 +286,7 @@ class FlavorParser:
extruder.getProperty("machine_nozzle_offset_y", "value")] extruder.getProperty("machine_nozzle_offset_y", "value")]
return result return result
def processGCodeStream(self, stream): def processGCodeStream(self, stream: str) -> Optional[CuraSceneNode]:
Logger.log("d", "Preparing to load GCode") Logger.log("d", "Preparing to load GCode")
self._cancelled = False self._cancelled = False
# We obtain the filament diameter from the selected extruder to calculate line widths # We obtain the filament diameter from the selected extruder to calculate line widths
@ -453,10 +454,9 @@ class FlavorParser:
Logger.log("w", "File doesn't contain any valid layers") Logger.log("w", "File doesn't contain any valid layers")
settings = Application.getInstance().getGlobalContainerStack() settings = Application.getInstance().getGlobalContainerStack()
if not settings.getProperty("machine_center_is_zero", "value"):
machine_width = settings.getProperty("machine_width", "value") machine_width = settings.getProperty("machine_width", "value")
machine_depth = settings.getProperty("machine_depth", "value") machine_depth = settings.getProperty("machine_depth", "value")
if not self._center_is_zero:
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2)) scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
Logger.log("d", "GCode loading finished") Logger.log("d", "GCode loading finished")

View file

@ -5,10 +5,20 @@ from UM.FileHandler.FileReader import FileReader
from UM.Mesh.MeshReader import MeshReader from UM.Mesh.MeshReader import MeshReader
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Application import Application from UM.Application import Application
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
from . import MarlinFlavorParser, RepRapFlavorParser from . import MarlinFlavorParser, RepRapFlavorParser
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-gcode-file",
comment = "Cura GCode File",
suffixes = ["gcode", "gcode.gz"]
)
)
# Class for loading and parsing G-code files # Class for loading and parsing G-code files
class GCodeReader(MeshReader): class GCodeReader(MeshReader):

View file

@ -66,9 +66,9 @@ class GCodeWriter(MeshWriter):
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
scene = Application.getInstance().getController().getScene() scene = Application.getInstance().getController().getScene()
gcode_dict = getattr(scene, "gcode_dict") if not hasattr(scene, "gcode_dict"):
if not gcode_dict:
return False return False
gcode_dict = getattr(scene, "gcode_dict")
gcode_list = gcode_dict.get(active_build_plate, None) gcode_list = gcode_dict.get(active_build_plate, None)
if gcode_list is not None: if gcode_list is not None:
has_settings = False has_settings = False

View file

@ -56,8 +56,6 @@ class MachineSettingsAction(MachineAction):
if self._isEmptyDefinitionChanges(definition_changes_id): if self._isEmptyDefinitionChanges(definition_changes_id):
return return
self._container_registry.removeContainer(definition_changes_id)
def _reset(self): def _reset(self):
if not self._global_container_stack: if not self._global_container_stack:
return return

View file

@ -2,7 +2,7 @@
"name": "Machine Settings action", "name": "Machine Settings action",
"author": "fieldOfView", "author": "fieldOfView",
"version": "1.0.0", "version": "1.0.0",
"description": "Provides a way to change machine settings (such as build volume, nozzle size, etc)", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).",
"api": 4, "api": 4,
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -12,11 +12,14 @@ Window
property var selection: null property var selection: null
title: catalog.i18nc("@title", "Toolbox") title: catalog.i18nc("@title", "Toolbox")
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
width: 720 * screenScaleFactor width: 720 * screenScaleFactor
height: 640 * screenScaleFactor height: 640 * screenScaleFactor
minimumWidth: 720 * screenScaleFactor minimumWidth: width
maximumWidth: 720 * screenScaleFactor maximumWidth: minimumWidth
minimumHeight: 350 * screenScaleFactor minimumHeight: height
maximumHeight: minimumHeight
color: UM.Theme.getColor("sidebar") color: UM.Theme.getColor("sidebar")
UM.I18nCatalog UM.I18nCatalog
{ {
@ -76,7 +79,7 @@ Window
{ {
id: footer id: footer
visible: toolbox.restartRequired visible: toolbox.restartRequired
height: toolbox.restartRequired ? UM.Theme.getSize("toolbox_footer").height : 0 height: visible ? UM.Theme.getSize("toolbox_footer").height : 0
} }
// TODO: Clean this up: // TODO: Clean this up:
Connections Connections

View file

@ -0,0 +1,29 @@
// Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM
ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: "transparent"
border
{
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
}
label: Label
{
text: control.text
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -9,7 +9,7 @@ import UM 1.1 as UM
Item Item
{ {
id: page id: page
property var details: base.selection property var details: base.selection || {}
anchors.fill: parent anchors.fill: parent
ToolboxBackColumn ToolboxBackColumn
{ {
@ -32,6 +32,7 @@ Item
height: UM.Theme.getSize("toolbox_thumbnail_medium").height height: UM.Theme.getSize("toolbox_thumbnail_medium").height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: details.icon_url || "../images/logobot.svg" source: details.icon_url || "../images/logobot.svg"
mipmap: true
anchors anchors
{ {
top: parent.top top: parent.top
@ -53,7 +54,7 @@ Item
rightMargin: UM.Theme.getSize("wide_margin").width rightMargin: UM.Theme.getSize("wide_margin").width
bottomMargin: UM.Theme.getSize("default_margin").height bottomMargin: UM.Theme.getSize("default_margin").height
} }
text: details.name text: details.name || ""
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
@ -62,7 +63,7 @@ Item
Label Label
{ {
id: description id: description
text: details.description text: details.description || ""
anchors anchors
{ {
top: title.bottom top: title.bottom
@ -114,6 +115,7 @@ Item
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
} }
} }

View file

@ -8,6 +8,7 @@ import UM 1.1 as UM
Item Item
{ {
property var packageData
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
height: visible ? childrenRect.height : 0 height: visible ? childrenRect.height : 0
visible: packageData.type == "material" && packageData.has_configs visible: packageData.type == "material" && packageData.has_configs
@ -36,8 +37,8 @@ Item
Label Label
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: styleData.elideMode elide: Text.ElideRight
text: styleData.value text: styleData.value || ""
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
} }
@ -55,8 +56,8 @@ Item
Label Label
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: styleData.elideMode elide: Text.ElideRight
text: styleData.value text: styleData.value || ""
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
} }
@ -67,8 +68,8 @@ Item
Label Label
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
elide: styleData.elideMode elide: Text.ElideRight
text: styleData.value text: styleData.value || ""
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -33,6 +33,7 @@ Item
height: UM.Theme.getSize("toolbox_thumbnail_medium").height height: UM.Theme.getSize("toolbox_thumbnail_medium").height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: details.icon_url || "../images/logobot.svg" source: details.icon_url || "../images/logobot.svg"
mipmap: true
anchors anchors
{ {
top: parent.top top: parent.top
@ -54,8 +55,9 @@ Item
rightMargin: UM.Theme.getSize("wide_margin").width rightMargin: UM.Theme.getSize("wide_margin").width
bottomMargin: UM.Theme.getSize("default_margin").height bottomMargin: UM.Theme.getSize("default_margin").height
} }
text: details.name text: details.name || ""
font: UM.Theme.getFont("large") font: UM.Theme.getFont("large")
color: UM.Theme.getColor("text")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: parent.width width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height height: UM.Theme.getSize("toolbox_property_label").height
@ -113,7 +115,7 @@ Item
text: text:
{ {
var date = new Date(details.last_updated) var date = new Date(details.last_updated)
return date.toLocaleString(Qt.locale()) return date.toLocaleString(UM.Preferences.getValue("general/language"))
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
@ -133,6 +135,7 @@ Item
} }
font: UM.Theme.getFont("very_small") font: UM.Theme.getFont("very_small")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
onLinkActivated: Qt.openUrlExternally(link) onLinkActivated: Qt.openUrlExternally(link)
} }
} }

View file

@ -10,9 +10,8 @@ Item
{ {
id: tile id: tile
property bool installed: toolbox.isInstalled(model.id) property bool installed: toolbox.isInstalled(model.id)
property var packageData: model
width: detailList.width - UM.Theme.getSize("wide_margin").width width: detailList.width - UM.Theme.getSize("wide_margin").width
height: Math.max(UM.Theme.getSize("toolbox_detail_tile").height, childrenRect.height + UM.Theme.getSize("default_margin").height) height: normalData.height + compatibilityChart.height + 4 * UM.Theme.getSize("default_margin").height
Item Item
{ {
id: normalData id: normalData
@ -46,6 +45,7 @@ Item
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
} }
} }
Item Item
{ {
id: controls id: controls
@ -76,58 +76,9 @@ Item
} }
enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) //Don't allow installing while another download is running. enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) //Don't allow installing while another download is running.
opacity: enabled ? 1.0 : 0.5 opacity: enabled ? 1.0 : 0.5
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: 96
implicitHeight: 30
color:
{
if (installed)
{
return UM.Theme.getColor("action_button_disabled")
}
else
{
if ( control.hovered )
{
return UM.Theme.getColor("primary_hover")
}
else
{
return UM.Theme.getColor("primary")
}
}
} property alias installed: tile.installed
} style: UM.Theme.styles.toolbox_action_button
label: Label
{
text: control.text
color:
{
if (installed)
{
return UM.Theme.getColor("action_button_disabled_text")
}
else
{
if ( control.hovered )
{
return UM.Theme.getColor("button_text_hover")
}
else
{
return UM.Theme.getColor("button_text")
}
}
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default_bold")
}
}
onClicked: onClicked:
{ {
if (installed) if (installed)
@ -164,6 +115,7 @@ Item
id: compatibilityChart id: compatibilityChart
anchors.top: normalData.bottom anchors.top: normalData.bottom
width: normalData.width width: normalData.width
packageData: model
} }
Rectangle Rectangle

View file

@ -9,9 +9,7 @@ import UM 1.1 as UM
Column Column
{ {
// HACK: GridLayouts don't render to the correct height with odd numbers of height: childrenRect.height
// items, so if odd, add some extra space.
height: grid.model.items.length % 2 == 0 ? childrenRect.height : childrenRect.height + UM.Theme.getSize("toolbox_thumbnail_small").height
width: parent.width width: parent.width
spacing: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height
Label Label
@ -36,6 +34,7 @@ Column
delegate: ToolboxDownloadsGridTile delegate: ToolboxDownloadsGridTile
{ {
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
Layout.preferredHeight: UM.Theme.getSize("toolbox_thumbnail_small").height
} }
} }
} }

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
@ -34,10 +34,11 @@ Item
Image Image
{ {
anchors.centerIn: parent anchors.centerIn: parent
width: UM.Theme.getSize("toolbox_thumbnail_small").width - 26 width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
height: UM.Theme.getSize("toolbox_thumbnail_small").height - 26 height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg" source: model.icon_url || "../images/logobot.svg"
mipmap: true
} }
} }
Column Column

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.3
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
@ -9,7 +9,7 @@ import UM 1.1 as UM
Item Item
{ {
width: UM.Theme.getSize("toolbox_thumbnail_large").width width: UM.Theme.getSize("toolbox_thumbnail_large").width
height: childrenRect.height height: thumbnail.height + packageName.height
Rectangle Rectangle
{ {
id: highlight id: highlight
@ -40,14 +40,16 @@ Item
height: UM.Theme.getSize("toolbox_thumbnail_large").height - 2 * UM.Theme.getSize("default_margin").height height: UM.Theme.getSize("toolbox_thumbnail_large").height - 2 * UM.Theme.getSize("default_margin").height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: model.icon_url || "../images/logobot.svg" source: model.icon_url || "../images/logobot.svg"
mipmap: true
} }
} }
Label Label
{ {
id: packageName
text: model.name text: model.name
anchors anchors
{ {
bottom: parent.bottom top: thumbnail.bottom
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
} }
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter

View file

@ -14,8 +14,7 @@ Item
height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0 height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0
Label Label
{ {
visible: toolbox.restartRequired text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.")
text: catalog.i18nc("@info", "You will need to restart Cura before changes in plugins have effect.")
height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height) height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
anchors anchors
@ -38,7 +37,6 @@ Item
right: parent.right right: parent.right
rightMargin: UM.Theme.getSize("wide_margin").width rightMargin: UM.Theme.getSize("wide_margin").width
} }
visible: toolbox.restartRequired
iconName: "dialog-restart" iconName: "dialog-restart"
onClicked: toolbox.restart() onClicked: toolbox.restart()
style: ButtonStyle style: ButtonStyle
@ -49,7 +47,7 @@ Item
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_footer_button").height) implicitHeight: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary") color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
} }
label: Text label: Label
{ {
color: UM.Theme.getColor("button_text") color: UM.Theme.getColor("button_text")
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
@ -61,7 +59,7 @@ Item
} }
ToolboxShadow ToolboxShadow
{ {
visible: toolbox.restartRequired visible: footer.visible
anchors.bottom: footer.top anchors.bottom: footer.top
reversed: true reversed: true
} }

View file

@ -24,7 +24,8 @@ Item
ToolboxTabButton ToolboxTabButton
{ {
text: catalog.i18nc("@title:tab", "Plugins") text: catalog.i18nc("@title:tab", "Plugins")
active: toolbox.viewCategory == "plugin" active: toolbox.viewCategory == "plugin" && enabled
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
onClicked: onClicked:
{ {
toolbox.filterModelByProp("packages", "type", "plugin") toolbox.filterModelByProp("packages", "type", "plugin")
@ -35,7 +36,8 @@ Item
ToolboxTabButton ToolboxTabButton
{ {
text: catalog.i18nc("@title:tab", "Materials") text: catalog.i18nc("@title:tab", "Materials")
active: toolbox.viewCategory == "material" active: toolbox.viewCategory == "material" && enabled
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
onClicked: onClicked:
{ {
toolbox.filterModelByProp("authors", "package_types", "material") toolbox.filterModelByProp("authors", "package_types", "material")

View file

@ -1,21 +1,18 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher. // Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.7
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
Item Item
{ {
height: UM.Theme.getSize("toolbox_installed_tile").height
width: parent.width
property bool canUpdate: false property bool canUpdate: false
property bool isEnabled: true property bool isEnabled: true
height: UM.Theme.getSize("toolbox_installed_tile").height
anchors
{
left: parent.left
right: parent.right
}
Rectangle Rectangle
{ {
color: UM.Theme.getColor("lining") color: UM.Theme.getColor("lining")
@ -23,52 +20,54 @@ Item
height: UM.Theme.getSize("default_lining").height height: UM.Theme.getSize("default_lining").height
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
} }
Row
{
id: tileRow
height: parent.height
width: parent.width
spacing: UM.Theme.getSize("default_margin").width
topPadding: UM.Theme.getSize("default_margin").height
CheckBox
{
id: disableButton
checked: isEnabled
visible: model.type == "plugin"
width: visible ? UM.Theme.getSize("checkbox").width : 0
enabled: !toolbox.isDownloading
style: UM.Theme.styles.checkbox
onClicked: toolbox.isEnabled(model.id) ? toolbox.disable(model.id) : toolbox.enable(model.id)
}
Column Column
{ {
id: pluginInfo id: pluginInfo
property var color: isEnabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") topPadding: UM.Theme.getSize("default_margin").height / 2
height: parent.height property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text")
anchors width: tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0))
{
left: parent.left
top: parent.top
right: authorInfo.left
topMargin: UM.Theme.getSize("default_margin").height
rightMargin: UM.Theme.getSize("default_margin").width
}
Label Label
{ {
text: model.name text: model.name
width: parent.width width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height height: UM.Theme.getSize("toolbox_property_label").height
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
color: pluginInfo.color color: pluginInfo.color
} }
Text Label
{ {
text: model.description text: model.description
maximumLineCount: 3
elide: Text.ElideRight
width: parent.width width: parent.width
height: UM.Theme.getSize("toolbox_property_label").height
clip: true
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: pluginInfo.color color: pluginInfo.color
elide: Text.ElideRight
} }
} }
Column Column
{ {
id: authorInfo id: authorInfo
height: parent.height
width: Math.floor(UM.Theme.getSize("toolbox_action_button").width * 1.25) width: Math.floor(UM.Theme.getSize("toolbox_action_button").width * 1.25)
anchors
{
top: parent.top
topMargin: UM.Theme.getSize("default_margin").height
right: pluginActions.left
rightMargin: UM.Theme.getSize("default_margin").width
}
Label Label
{ {
text: text:
@ -89,126 +88,12 @@ Item
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin") onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
linkColor: UM.Theme.getColor("text_link")
} }
} }
Column ToolboxInstalledTileActions
{ {
id: pluginActions id: pluginActions
width: childrenRect.width
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").height
anchors
{
top: parent.top
right: parent.right
topMargin: UM.Theme.getSize("default_margin").height
}
Button {
id: removeButton
text:
{
if (model.is_bundled)
{
return isEnabled ? catalog.i18nc("@action:button", "Disable") : catalog.i18nc("@action:button", "Enable")
}
else
{
return catalog.i18nc("@action:button", "Uninstall")
}
}
enabled: !toolbox.isDownloading
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: "transparent"
border
{
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
}
label: Text
{
text: control.text
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
onClicked:
{
if (model.is_bundled)
{
if (toolbox.isEnabled(model.id))
{
toolbox.disable(model.id)
}
else
{
toolbox.enable(model.id)
}
}
else
{
toolbox.uninstall(model.id)
}
}
}
Button
{
id: updateButton
text: catalog.i18nc("@action:button", "Update")
visible: canUpdate
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
}
label: Label
{
text: control.text
color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default_bold")
}
}
onClicked:
{
toolbox.update(model.id);
}
}
ProgressBar
{
id: progressbar
anchors
{
left: updateButton.left
right: updateButton.right
top: updateButton.bottom
topMargin: Math.floor(UM.Theme.getSize("default_margin") / 4)
}
value: toolbox.isDownloading ? toolbox.downloadProgress : 0
visible: toolbox.isDownloading
style: ProgressBarStyle
{
background: Rectangle
{
color: UM.Theme.getColor("lining")
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_progress_bar").height)
}
progress: Rectangle
{
color: UM.Theme.getColor("primary")
}
}
}
} }
Connections Connections
{ {
@ -217,3 +102,4 @@ Item
onMetadataChanged: canUpdate = toolbox.canUpdate(model.id) onMetadataChanged: canUpdate = toolbox.canUpdate(model.id)
} }
} }
}

View file

@ -0,0 +1,93 @@
// Copyright (c) 2018 Ultimaker B.V.
// Toolbox is released under the terms of the LGPLv3 or higher.
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM
Column
{
width: UM.Theme.getSize("toolbox_action_button").width
spacing: UM.Theme.getSize("narrow_margin").height
Item
{
width: parent.width
height: childrenRect.height
visible: canUpdate
Button
{
id: updateButton
text: catalog.i18nc("@action:button", "Update")
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
}
label: Label
{
text: control.text
color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default_bold")
}
}
onClicked: toolbox.update(model.id)
}
ProgressBar
{
id: progressbar
width: parent.width
value: toolbox.isDownloading ? toolbox.downloadProgress : 0
visible: toolbox.isDownloading
style: ProgressBarStyle
{
background: Rectangle
{
color: "transparent"
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
}
progress: Rectangle
{
// TODO Define a good color that fits the purpuse
color: "blue"
opacity: 0.5
}
}
}
}
Button
{
id: removeButton
text: catalog.i18nc("@action:button", "Uninstall")
visible: !model.is_bundled
enabled: !toolbox.isDownloading
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: "transparent"
border
{
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
}
label: Label
{
text: control.text
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
onClicked: toolbox.uninstall(model.id)
}
}

View file

@ -42,7 +42,7 @@ UM.Dialog
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
readOnly: true readOnly: true
text: licenseDialog.licenseContent text: licenseDialog.licenseContent || ""
} }
} }
rightButtons: rightButtons:

View file

@ -25,7 +25,7 @@ Button
height: UM.Theme.getSize("sidebar_header_highlight").height height: UM.Theme.getSize("sidebar_header_highlight").height
} }
} }
label: Text label: Label
{ {
text: control.text text: control.text
color: color:
@ -43,7 +43,7 @@ Button
return UM.Theme.getColor("topbar_button_text_inactive"); return UM.Theme.getColor("topbar_button_text_inactive");
} }
} }
font: control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium") font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic")
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }

View file

@ -28,8 +28,9 @@ class PackagesModel(ListModel):
self.addRoleName(Qt.UserRole + 11, "download_url") self.addRoleName(Qt.UserRole + 11, "download_url")
self.addRoleName(Qt.UserRole + 12, "last_updated") self.addRoleName(Qt.UserRole + 12, "last_updated")
self.addRoleName(Qt.UserRole + 13, "is_bundled") self.addRoleName(Qt.UserRole + 13, "is_bundled")
self.addRoleName(Qt.UserRole + 14, "has_configs") self.addRoleName(Qt.UserRole + 14, "is_enabled")
self.addRoleName(Qt.UserRole + 15, "supported_configs") self.addRoleName(Qt.UserRole + 15, "has_configs")
self.addRoleName(Qt.UserRole + 16, "supported_configs")
# List of filters for queries. The result is the union of the each list of results. # List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str, str] self._filter = {} # type: Dict[str, str]
@ -52,20 +53,26 @@ class PackagesModel(ListModel):
configs_model = ConfigsModel() configs_model = ConfigsModel()
configs_model.setConfigs(package["data"]["supported_configs"]) configs_model.setConfigs(package["data"]["supported_configs"])
if "author_id" not in package["author"] or "display_name" not in package["author"]:
package["author"]["author_id"] = ""
package["author"]["display_name"] = ""
# raise Exception("Detected a package with malformed author data.")
items.append({ items.append({
"id": package["package_id"], "id": package["package_id"],
"type": package["package_type"], "type": package["package_type"],
"name": package["display_name"], "name": package["display_name"],
"version": package["package_version"], "version": package["package_version"],
"author_id": package["author"]["author_id"] if "author_id" in package["author"] else package["author"]["name"], "author_id": package["author"]["author_id"],
"author_name": package["author"]["display_name"] if "display_name" in package["author"] else package["author"]["name"], "author_name": package["author"]["display_name"],
"author_email": package["author"]["email"] if "email" in package["author"] else "None", "author_email": package["author"]["email"] if "email" in package["author"] else None,
"description": package["description"], "description": package["description"] if "description" in package else None,
"icon_url": package["icon_url"] if "icon_url" in package else None, "icon_url": package["icon_url"] if "icon_url" in package else None,
"image_urls": package["image_urls"] if "image_urls" in package else None, "image_urls": package["image_urls"] if "image_urls" in package else None,
"download_url": package["download_url"] if "download_url" in package else None, "download_url": package["download_url"] if "download_url" in package else None,
"last_updated": package["last_updated"] if "last_updated" in package else None, "last_updated": package["last_updated"] if "last_updated" in package else None,
"is_bundled": package["is_bundled"] if "is_bundled" in package else False, "is_bundled": package["is_bundled"] if "is_bundled" in package else False,
"is_enabled": package["is_enabled"] if "is_enabled" in package else False,
"has_configs": has_configs, "has_configs": has_configs,
"supported_configs": configs_model "supported_configs": configs_model
}) })

View file

@ -13,19 +13,17 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkRepl
from UM.Application import Application from UM.Application import Application
from UM.Logger import Logger from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.Qt.Bindings.PluginsModel import PluginsModel
from UM.Extension import Extension from UM.Extension import Extension
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Version import Version from UM.Version import Version
import cura
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from .AuthorsModel import AuthorsModel from .AuthorsModel import AuthorsModel
from .PackagesModel import PackagesModel from .PackagesModel import PackagesModel
from .ConfigsModel import ConfigsModel
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
## The Toolbox class is responsible of communicating with the server through the API ## The Toolbox class is responsible of communicating with the server through the API
class Toolbox(QObject, Extension): class Toolbox(QObject, Extension):
def __init__(self, parent=None) -> None: def __init__(self, parent=None) -> None:
@ -34,7 +32,7 @@ class Toolbox(QObject, Extension):
self._application = Application.getInstance() self._application = Application.getInstance()
self._package_manager = None self._package_manager = None
self._plugin_registry = Application.getInstance().getPluginRegistry() self._plugin_registry = Application.getInstance().getPluginRegistry()
self._packages_version = self._plugin_registry.APIVersion self._packages_version = self._getPackagesVersion()
self._api_version = 1 self._api_version = 1
self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version) self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version)
@ -154,6 +152,13 @@ class Toolbox(QObject, Extension):
def _onAppInitialized(self) -> None: def _onAppInitialized(self) -> None:
self._package_manager = Application.getInstance().getCuraPackageManager() self._package_manager = Application.getInstance().getCuraPackageManager()
def _getPackagesVersion(self) -> int:
if not hasattr(cura, "CuraVersion"):
return self._plugin_registry.APIVersion
if not hasattr(cura.CuraVersion, "CuraPackagesVersion"):
return self._plugin_registry.APIVersion
return cura.CuraVersion.CuraPackagesVersion
@pyqtSlot() @pyqtSlot()
def browsePackages(self) -> None: def browsePackages(self) -> None:
# Create the network manager: # Create the network manager:
@ -244,7 +249,6 @@ class Toolbox(QObject, Extension):
@pyqtSlot() @pyqtSlot()
def restart(self): def restart(self):
self._package_manager._removeAllScheduledPackages()
CuraApplication.getInstance().windowClosed() CuraApplication.getInstance().windowClosed()
# Checks # Checks
@ -326,8 +330,8 @@ class Toolbox(QObject, Extension):
# Handlers for Network Events # Handlers for Network Events
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
def _onNetworkAccessibleChanged(self, accessible: int) -> None: def _onNetworkAccessibleChanged(self, network_accessibility: QNetworkAccessManager.NetworkAccessibility) -> None:
if accessible == 0: if network_accessibility == QNetworkAccessManager.NotAccessible:
self.resetDownload() self.resetDownload()
def _onRequestFinished(self, reply: QNetworkReply) -> None: def _onRequestFinished(self, reply: QNetworkReply) -> None:
@ -347,6 +351,7 @@ class Toolbox(QObject, Extension):
if reply.operation() == QNetworkAccessManager.GetOperation: if reply.operation() == QNetworkAccessManager.GetOperation:
for type, url in self._request_urls.items(): for type, url in self._request_urls.items():
if reply.url() == url: if reply.url() == url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
try: try:
json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
@ -391,6 +396,10 @@ class Toolbox(QObject, Extension):
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type) Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
break break
else:
self.setViewPage("errored")
self.resetDownload()
return
else: else:
# Ignore any operation that is not a get operation # Ignore any operation that is not a get operation
@ -403,7 +412,7 @@ class Toolbox(QObject, Extension):
if bytes_sent == bytes_total: if bytes_sent == bytes_total:
self.setIsDownloading(False) self.setIsDownloading(False)
self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) self._download_reply.downloadProgress.disconnect(self._onDownloadProgress)
# <ust not delete the temporary file on Windows # Must not delete the temporary file on Windows
self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False) self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False)
file_path = self._temp_plugin_file.name file_path = self._temp_plugin_file.name
# Write first and close, otherwise on Windows, it cannot read the file # Write first and close, otherwise on Windows, it cannot read the file
@ -450,7 +459,7 @@ class Toolbox(QObject, Extension):
self._active_package = package self._active_package = package
self.activePackageChanged.emit() self.activePackageChanged.emit()
@pyqtProperty("QVariantMap", fset = setActivePackage, notify = activePackageChanged) @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
def activePackage(self) -> Optional[Dict[str, Any]]: def activePackage(self) -> Optional[Dict[str, Any]]:
return self._active_package return self._active_package

View file

@ -11,6 +11,16 @@ except ImportError:
from UM.i18n import i18nCatalog #To translate the file format description. from UM.i18n import i18nCatalog #To translate the file format description.
from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag. from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
MimeTypeDatabase.addMimeType(
MimeType(
name = "application/x-cura-stl-file",
comment = "Cura UFP File",
suffixes = ["ufp"]
)
)
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")

View file

@ -188,14 +188,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
b"name": system_info["name"].encode("utf-8"), b"name": system_info["name"].encode("utf-8"),
b"address": address.encode("utf-8"), b"address": address.encode("utf-8"),
b"firmware_version": system_info["firmware"].encode("utf-8"), b"firmware_version": system_info["firmware"].encode("utf-8"),
b"manual": b"true" b"manual": b"true",
b"machine": str(system_info['hardware']["typeid"]).encode("utf-8")
} }
if "hardware" in system_info and 'typeid' in system_info["hardware"]:
properties[b"machine"] = str(system_info['hardware']["typeid"]).encode("utf-8")
else:
properties[b"machine"] = system_info["variant"].encode("utf-8")
if has_cluster_capable_firmware: if has_cluster_capable_firmware:
# Cluster needs an additional request, before it's completed. # Cluster needs an additional request, before it's completed.
properties[b"incomplete"] = b"true" properties[b"incomplete"] = b"true"

View file

@ -1,7 +1,7 @@
{ {
"name": "UM3 Network Connection", "name": "UM3 Network Connection",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"description": "Manages network connections to Ultimaker 3 printers", "description": "Manages network connections to Ultimaker 3 printers.",
"version": "1.0.0", "version": "1.0.0",
"api": 4, "api": 4,
"i18n-catalog": "cura" "i18n-catalog": "cura"

View file

@ -2,7 +2,7 @@
"name": "Ultimaker machine actions", "name": "Ultimaker machine actions",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.0",
"description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc)", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).",
"api": 4, "api": 4,
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -2,7 +2,7 @@
"name": "UserAgreement", "name": "UserAgreement",
"author": "Ultimaker B.V.", "author": "Ultimaker B.V.",
"version": "1.0.0", "version": "1.0.0",
"description": "Ask the user once if he/she agrees with our license", "description": "Ask the user once if he/she agrees with our license.",
"api": 4, "api": 4,
"i18n-catalog": "cura" "i18n-catalog": "cura"
} }

View file

@ -227,6 +227,7 @@
{ {
"label": "Outer nozzle diameter", "label": "Outer nozzle diameter",
"description": "The outer diameter of the tip of the nozzle.", "description": "The outer diameter of the tip of the nozzle.",
"unit": "mm",
"default_value": 1, "default_value": 1,
"type": "float", "type": "float",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -238,6 +239,7 @@
{ {
"label": "Nozzle length", "label": "Nozzle length",
"description": "The height difference between the tip of the nozzle and the lowest part of the print head.", "description": "The height difference between the tip of the nozzle and the lowest part of the print head.",
"unit": "mm",
"default_value": 3, "default_value": 3,
"type": "float", "type": "float",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -261,6 +263,7 @@
{ {
"label": "Heat zone length", "label": "Heat zone length",
"description": "The distance from the tip of the nozzle in which heat from the nozzle is transferred to the filament.", "description": "The distance from the tip of the nozzle in which heat from the nozzle is transferred to the filament.",
"unit": "mm",
"default_value": 16, "default_value": 16,
"type": "float", "type": "float",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -271,6 +274,7 @@
{ {
"label": "Filament Park Distance", "label": "Filament Park Distance",
"description": "The distance from the tip of the nozzle where to park the filament when an extruder is no longer used.", "description": "The distance from the tip of the nozzle where to park the filament when an extruder is no longer used.",
"unit": "mm",
"default_value": 16, "default_value": 16,
"value": "machine_heat_zone_length", "value": "machine_heat_zone_length",
"type": "float", "type": "float",

View file

@ -32,7 +32,7 @@
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": { "machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;home X/Y\nG28 Z0 ;home Z\nG92 E0 ;zero the extruded length\nG29 ;initiate auto bed leveling sequence\nG92 X132.4 Y20 ;correct bed origin (G29 changes it)" "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;home X/Y\nG28 Z0 ;home Z\nG92 E0 ;zero the extruded length\nG29 ;initiate auto bed leveling sequence"
}, },
"machine_end_gcode": { "machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM106 S0 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z+1 E-5 F9000 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM106 S0 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z+1 E-5 F9000 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"

View file

@ -37,7 +37,7 @@
"overrides": { "overrides": {
"machine_name": { "default_value": "Ultimaker S5" }, "machine_name": { "default_value": "Ultimaker S5" },
"machine_width": { "default_value": 330 }, "machine_width": { "default_value": 330 },
"machine_depth": { "default_value": 245 }, "machine_depth": { "default_value": 240 },
"machine_height": { "default_value": 300 }, "machine_height": { "default_value": 300 },
"machine_heated_bed": { "default_value": true }, "machine_heated_bed": { "default_value": true },
"machine_nozzle_heat_up_speed": { "default_value": 1.4 }, "machine_nozzle_heat_up_speed": { "default_value": 1.4 },

View file

@ -8,7 +8,7 @@
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker2", "icon": "icon_ultimaker2",
"platform": "Vertex_build_panel.stl", "platform": "Vertex_build_panel.stl",
"platform_offset": [0, -2, 0], "platform_offset": [0, -3, 0],
"supports_usb_connection": true, "supports_usb_connection": true,
"supported_actions": ["MachineSettingsAction"] "supported_actions": ["MachineSettingsAction"]
}, },

View file

@ -8,7 +8,7 @@
"file_formats": "text/x-gcode", "file_formats": "text/x-gcode",
"icon": "icon_ultimaker2", "icon": "icon_ultimaker2",
"platform": "Vertex_build_panel.stl", "platform": "Vertex_build_panel.stl",
"platform_offset": [0, -2, 0], "platform_offset": [0, -3, 0],
"machine_extruder_trains": { "machine_extruder_trains": {
"0": "vertex_k8400_dual_1st", "0": "vertex_k8400_dual_1st",
"1": "vertex_k8400_dual_2nd" "1": "vertex_k8400_dual_2nd"

View file

@ -3558,7 +3558,7 @@ msgstr "Druckeinrichtung deaktiviert\nG-Code-Dateien können nicht geändert wer
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:380 #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:380
msgctxt "@label Hours and minutes" msgctxt "@label Hours and minutes"
msgid "00h 00min" msgid "00h 00min"
msgstr "00 Stunden 00 Minuten" msgstr "00 St. 00 M."
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:398 #: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:398
msgctxt "@tooltip" msgctxt "@tooltip"
@ -5493,7 +5493,7 @@ msgstr "Cura-Profil-Reader"
#~ msgctxt "@label" #~ msgctxt "@label"
#~ msgid "00h 00min" #~ msgid "00h 00min"
#~ msgstr "00 Stunden 00 Minuten" #~ msgstr "00 St. 00 M."
#~ msgctxt "@tooltip" #~ msgctxt "@tooltip"
#~ msgid "<b>Time information</b>" #~ msgid "<b>Time information</b>"
@ -5517,7 +5517,7 @@ msgstr "Cura-Profil-Reader"
#~ msgctxt "@label" #~ msgctxt "@label"
#~ msgid "<a href='%1'>Check material compatibility</a>" #~ msgid "<a href='%1'>Check material compatibility</a>"
#~ msgstr "<a href='%1>Materialkompatibilität prüfen</a>" #~ msgstr "<a href='%1'>Materialkompatibilität prüfen</a>"
#~ msgctxt "name" #~ msgctxt "name"
#~ msgid "UM3 Network Connection (Cluster)" #~ msgid "UM3 Network Connection (Cluster)"

1153
resources/packages.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M1 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = fine
weight = 2
[values]
layer_height = 0.04375
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M2 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = high
weight = 1
[values]
layer_height = 0.0875
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M3 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = normal
weight = 0
[values]
layer_height = 0.13125
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,23 +0,0 @@
[general]
version = 4
name = M4 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = fast
weight = -1
global_quality = true
[values]
layer_height = 0.175
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M5 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = faster
weight = -2
[values]
layer_height = 0.21875
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M6 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = draft
weight = -3
[values]
layer_height = 0.2625
layer_height_0 = 0.2625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,22 +0,0 @@
[general]
version = 4
name = M7 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = turbo
weight = -4
[values]
layer_height = 0.30625
layer_height_0 = 0.30625
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -1,23 +0,0 @@
[general]
version = 4
name = M8 Quality
definition = malyan_m200
[metadata]
setting_version = 4
type = quality
quality_type = hyper
weight = -5
global_quality = true
[values]
layer_height = 0.35
layer_height_0 = 0.35
wall_thickness = 1.05
top_bottom_thickness = 0.72
infill_sparse_density = 22
speed_print = 50
speed_layer_0 = =round(speed_print * 30 / 50)
speed_topbottom = 20
cool_min_layer_time = 5
cool_min_speed = 10

View file

@ -25,11 +25,11 @@ jerk_enabled = True
jerk_print = 25 jerk_print = 25
line_width = =machine_nozzle_size * 0.92 line_width = =machine_nozzle_size * 0.92
machine_min_cool_heat_time_window = 15 machine_min_cool_heat_time_window = 15
material_bed_temperature_layer_0 = 90 material_bed_temperature_layer_0 = =material_bed_temperature + 5
material_final_print_temperature = 195 material_final_print_temperature = =material_print_temperature - 10
material_initial_print_temperature = 200 material_initial_print_temperature = =material_print_temperature - 5
material_print_temperature = 205 material_print_temperature = =default_material_print_temperature - 15
material_print_temperature_layer_0 = 208 material_print_temperature_layer_0 = =material_print_temperature + 3
multiple_mesh_overlap = 0 multiple_mesh_overlap = 0
prime_tower_enable = False prime_tower_enable = False
prime_tower_size = 16 prime_tower_size = 16

View file

@ -26,5 +26,6 @@ speed_wall = =math.ceil(speed_print * 45 / 60)
speed_wall_0 = =math.ceil(speed_wall * 35 / 45) speed_wall_0 = =math.ceil(speed_wall * 35 / 45)
wall_thickness = 1 wall_thickness = 1
infill_line_width = 0.4 infill_line_width = =round(line_width * 0.4 / 0.35, 2)
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 60)

View file

@ -25,5 +25,6 @@ speed_topbottom = =math.ceil(speed_print * 30 / 60)
speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall = =math.ceil(speed_print * 40 / 60)
speed_wall_0 = =math.ceil(speed_wall * 30 / 40) speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
infill_line_width = 0.4 infill_line_width = =round(line_width * 0.4 / 0.35, 2)
speed_infill = 45 speed_infill = =math.ceil(speed_print * 45 / 60)

View file

@ -24,5 +24,6 @@ speed_layer_0 = 20
speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_topbottom = =math.ceil(speed_print * 30 / 50)
speed_wall = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50)
infill_line_width = 0.4 infill_line_width = =round(line_width * 0.4 / 0.35, 2)
speed_infill = 40 speed_infill = =math.ceil(speed_print * 40 / 50)

View file

@ -23,5 +23,5 @@ speed_layer_0 = 20
speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55)
speed_wall = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55)
infill_line_width = 0.4 infill_line_width = =round(line_width * 0.4 / 0.35, 2)
speed_infill = 40 speed_infill = =math.ceil(speed_print * 40 / 55)

View file

@ -23,8 +23,7 @@ speed_wall = =math.ceil(speed_print * 45 / 60)
speed_wall_0 = =math.ceil(speed_wall * 35 / 45) speed_wall_0 = =math.ceil(speed_wall * 35 / 45)
wall_thickness = 1 wall_thickness = 1
jerk_travel = 50
infill_pattern = zigzag infill_pattern = zigzag
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 60)
prime_tower_purge_volume = 1 prime_tower_purge_volume = 1

View file

@ -22,8 +22,6 @@ speed_topbottom = =math.ceil(speed_print * 30 / 60)
speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall = =math.ceil(speed_print * 40 / 60)
speed_wall_0 = =math.ceil(speed_wall * 30 / 40) speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
jerk_travel = 50
infill_pattern = zigzag infill_pattern = zigzag
speed_infill = 50 speed_infill = =math.ceil(speed_print * 50 / 60)
prime_tower_purge_volume = 1 prime_tower_purge_volume = 1

View file

@ -23,8 +23,6 @@ speed_layer_0 = 20
speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_topbottom = =math.ceil(speed_print * 30 / 50)
speed_wall = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50)
jerk_travel = 50
infill_pattern = zigzag infill_pattern = zigzag
speed_infill = 40 speed_infill = =math.ceil(speed_print * 40 / 50)
prime_tower_purge_volume = 1 prime_tower_purge_volume = 1

View file

@ -21,8 +21,6 @@ speed_layer_0 = 20
speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55)
speed_wall = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55)
jerk_travel = 50
infill_pattern = zigzag infill_pattern = zigzag
speed_infill = 45 speed_infill = =math.ceil(speed_print * 45 / 55)
prime_tower_purge_volume = 1 prime_tower_purge_volume = 1

View file

@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
machine_min_cool_heat_time_window = 15 machine_min_cool_heat_time_window = 15
machine_nozzle_cool_down_speed = 0.85 machine_nozzle_cool_down_speed = 0.85
machine_nozzle_heat_up_speed = 1.5 machine_nozzle_heat_up_speed = 1.5
material_bed_temperature_layer_0 = 95 material_bed_temperature_layer_0 = =material_bed_temperature + 5
material_final_print_temperature = 205 material_final_print_temperature = =material_print_temperature - 10
material_initial_print_temperature = 210 material_initial_print_temperature = =material_print_temperature - 5
material_print_temperature = 215 material_print_temperature = =default_material_print_temperature - 5
material_print_temperature_layer_0 = 220 material_print_temperature_layer_0 = =material_print_temperature + 5
material_standby_temperature = 100 material_standby_temperature = 100
multiple_mesh_overlap = 0 multiple_mesh_overlap = 0
prime_tower_enable = False prime_tower_enable = False

View file

@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
machine_min_cool_heat_time_window = 15 machine_min_cool_heat_time_window = 15
machine_nozzle_cool_down_speed = 0.85 machine_nozzle_cool_down_speed = 0.85
machine_nozzle_heat_up_speed = 1.5 machine_nozzle_heat_up_speed = 1.5
material_bed_temperature_layer_0 = 95 material_bed_temperature_layer_0 = =material_bed_temperature + 5
material_final_print_temperature = 195 material_final_print_temperature = =material_print_temperature - 12
material_initial_print_temperature = 205 material_initial_print_temperature = =material_print_temperature - 2
material_print_temperature = 207 material_print_temperature = =default_material_print_temperature - 13
material_print_temperature_layer_0 = 210 material_print_temperature_layer_0 = =material_print_temperature + 3
material_standby_temperature = 100 material_standby_temperature = 100
multiple_mesh_overlap = 0 multiple_mesh_overlap = 0
prime_tower_enable = False prime_tower_enable = False

View file

@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
machine_min_cool_heat_time_window = 15 machine_min_cool_heat_time_window = 15
machine_nozzle_cool_down_speed = 0.85 machine_nozzle_cool_down_speed = 0.85
machine_nozzle_heat_up_speed = 1.5 machine_nozzle_heat_up_speed = 1.5
material_bed_temperature_layer_0 = 95 material_bed_temperature_layer_0 = =material_bed_temperature + 5
material_final_print_temperature = 195 material_final_print_temperature = =material_print_temperature - 10
material_initial_print_temperature = 200 material_initial_print_temperature = =material_print_temperature - 5
material_print_temperature = 205 material_print_temperature = =default_material_print_temperature - 15
material_print_temperature_layer_0 = 208 material_print_temperature_layer_0 = =material_print_temperature + 3
material_standby_temperature = 100 material_standby_temperature = 100
multiple_mesh_overlap = 0 multiple_mesh_overlap = 0
prime_tower_enable = False prime_tower_enable = False

View file

@ -18,6 +18,7 @@ brim_width = 8.75
cool_fan_speed_max = 100 cool_fan_speed_max = 100
cool_min_layer_time_fan_speed_max = 6 cool_min_layer_time_fan_speed_max = 6
cool_min_speed = 4 cool_min_speed = 4
gradual_infill_step_height = =5 * layer_height
infill_line_width = =round(line_width * 0.38 / 0.38, 2) infill_line_width = =round(line_width * 0.38 / 0.38, 2)
infill_overlap = 0 infill_overlap = 0
infill_pattern = cross_3d infill_pattern = cross_3d
@ -40,7 +41,6 @@ prime_tower_wipe_enabled = True
retraction_count_max = 12 retraction_count_max = 12
retraction_extra_prime_amount = 0.8 retraction_extra_prime_amount = 0.8
retraction_extrusion_window = 1 retraction_extrusion_window = 1
retraction_hop = 1.5
retraction_hop_only_when_collides = True retraction_hop_only_when_collides = True
retraction_min_travel = =line_width * 2 retraction_min_travel = =line_width * 2
retraction_prime_speed = 15 retraction_prime_speed = 15
@ -61,5 +61,3 @@ travel_avoid_distance = 1.5
wall_0_inset = 0 wall_0_inset = 0
wall_line_width_x = =line_width wall_line_width_x = =line_width
wall_thickness = 0.76 wall_thickness = 0.76
jerk_travel = 50

View file

@ -18,6 +18,7 @@ brim_width = 8.75
cool_fan_speed_max = 100 cool_fan_speed_max = 100
cool_min_layer_time_fan_speed_max = 6 cool_min_layer_time_fan_speed_max = 6
cool_min_speed = 4 cool_min_speed = 4
gradual_infill_step_height = =5 * layer_height
infill_line_width = =round(line_width * 0.38 / 0.38, 2) infill_line_width = =round(line_width * 0.38 / 0.38, 2)
infill_overlap = 0 infill_overlap = 0
infill_pattern = cross_3d infill_pattern = cross_3d
@ -40,7 +41,6 @@ prime_tower_wipe_enabled = True
retraction_count_max = 12 retraction_count_max = 12
retraction_extra_prime_amount = 0.8 retraction_extra_prime_amount = 0.8
retraction_extrusion_window = 1 retraction_extrusion_window = 1
retraction_hop = 1.5
retraction_hop_only_when_collides = True retraction_hop_only_when_collides = True
retraction_min_travel = =line_width * 2 retraction_min_travel = =line_width * 2
retraction_prime_speed = 15 retraction_prime_speed = 15
@ -62,4 +62,3 @@ wall_0_inset = 0
wall_line_width_x = =line_width wall_line_width_x = =line_width
wall_thickness = 0.76 wall_thickness = 0.76
jerk_travel = 50

View file

@ -18,6 +18,7 @@ brim_width = 8.75
cool_fan_speed_max = 100 cool_fan_speed_max = 100
cool_min_layer_time_fan_speed_max = 6 cool_min_layer_time_fan_speed_max = 6
cool_min_speed = 4 cool_min_speed = 4
gradual_infill_step_height = =5 * layer_height
infill_line_width = =round(line_width * 0.38 / 0.38, 2) infill_line_width = =round(line_width * 0.38 / 0.38, 2)
infill_overlap = 0 infill_overlap = 0
infill_pattern = cross_3d infill_pattern = cross_3d
@ -39,7 +40,6 @@ prime_tower_wipe_enabled = True
retraction_count_max = 12 retraction_count_max = 12
retraction_extra_prime_amount = 0.8 retraction_extra_prime_amount = 0.8
retraction_extrusion_window = 1 retraction_extrusion_window = 1
retraction_hop = 1.5
retraction_hop_only_when_collides = True retraction_hop_only_when_collides = True
retraction_min_travel = =line_width * 2 retraction_min_travel = =line_width * 2
retraction_prime_speed = 15 retraction_prime_speed = 15
@ -61,4 +61,3 @@ wall_0_inset = 0
wall_line_width_x = =line_width wall_line_width_x = =line_width
wall_thickness = 0.76 wall_thickness = 0.76
jerk_travel = 50

View file

@ -17,17 +17,15 @@ cool_fan_speed_max = =100
cool_min_speed = 2 cool_min_speed = 2
gradual_infill_step_height = =3 * layer_height gradual_infill_step_height = =3 * layer_height
infill_line_width = =round(line_width * 0.65 / 0.75, 2) infill_line_width = =round(line_width * 0.65 / 0.75, 2)
infill_pattern = cubic
line_width = =machine_nozzle_size * 0.9375 line_width = =machine_nozzle_size * 0.9375
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
machine_nozzle_heat_up_speed = 1.6 machine_nozzle_heat_up_speed = 1.6
material_final_print_temperature = =max(-273.15, material_print_temperature - 15) material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
material_print_temperature = =default_material_print_temperature + 10 material_print_temperature = =default_material_print_temperature + 10
prime_tower_enable = False prime_tower_enable = True
support_angle = 70 support_angle = 70
support_line_width = =line_width * 0.75 support_line_width = =line_width * 0.75
support_pattern = ='triangles'
support_xy_distance = =wall_line_width_0 * 1.5 support_xy_distance = =wall_line_width_0 * 1.5
top_bottom_thickness = =layer_height * 4 top_bottom_thickness = =layer_height * 4
wall_line_width = =round(line_width * 0.75 / 0.75, 2) wall_line_width = =round(line_width * 0.75 / 0.75, 2)
@ -36,8 +34,9 @@ wall_thickness = =wall_line_width_0 + wall_line_width_x
retract_at_layer_change = False retract_at_layer_change = False
speed_print = 45 speed_print = 45
speed_wall = =round(speed_print * 40 / 45) speed_topbottom = =math.ceil(speed_print * 35 / 45)
speed_wall_0 = =round(speed_print * 35 / 45) speed_wall = =math.ceil(speed_print * 40 / 45)
speed_topbottom = =round(speed_print * 35 / 45) speed_wall_x = =speed_wall
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
infill_sparse_density = 15 infill_sparse_density = 15
layer_height_0 = 0.4 layer_height_0 = 0.4

View file

@ -17,18 +17,16 @@ cool_fan_speed_max = =100
cool_min_speed = 2 cool_min_speed = 2
gradual_infill_step_height = =3 * layer_height gradual_infill_step_height = =3 * layer_height
infill_line_width = =round(line_width * 0.65 / 0.75, 2) infill_line_width = =round(line_width * 0.65 / 0.75, 2)
infill_pattern = cubic
line_width = =machine_nozzle_size * 0.9375 line_width = =machine_nozzle_size * 0.9375
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
machine_nozzle_heat_up_speed = 1.6 machine_nozzle_heat_up_speed = 1.6
material_final_print_temperature = =max(-273.15, material_print_temperature - 15) material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
material_print_temperature = =default_material_print_temperature + 15 material_print_temperature = =default_material_print_temperature + 15
prime_tower_enable = False prime_tower_enable = True
raft_margin = 10 raft_margin = 10
support_angle = 70 support_angle = 70
support_line_width = =line_width * 0.75 support_line_width = =line_width * 0.75
support_pattern = ='triangles'
support_xy_distance = =wall_line_width_0 * 1.5 support_xy_distance = =wall_line_width_0 * 1.5
top_bottom_thickness = =layer_height * 4 top_bottom_thickness = =layer_height * 4
wall_line_width = =round(line_width * 0.75 / 0.75, 2) wall_line_width = =round(line_width * 0.75 / 0.75, 2)
@ -36,8 +34,9 @@ wall_line_width_x = =round(wall_line_width * 0.625 / 0.75, 2)
wall_thickness = =wall_line_width_0 + wall_line_width_x wall_thickness = =wall_line_width_0 + wall_line_width_x
retract_at_layer_change = False retract_at_layer_change = False
speed_print = 45 speed_print = 45
speed_wall = =round(speed_print * 40 / 45) speed_topbottom = =math.ceil(speed_print * 35 / 45)
speed_wall_0 = =round(speed_print * 35 / 45) speed_wall = =math.ceil(speed_print * 40 / 45)
speed_topbottom = =round(speed_print * 35 / 45) speed_wall_x = =speed_wall
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
infill_sparse_density = 15 infill_sparse_density = 15
layer_height_0 = 0.4 layer_height_0 = 0.4

View file

@ -17,17 +17,15 @@ cool_fan_speed_max = =100
cool_min_speed = 2 cool_min_speed = 2
gradual_infill_step_height = =3 * layer_height gradual_infill_step_height = =3 * layer_height
infill_line_width = =round(line_width * 0.65 / 0.75, 2) infill_line_width = =round(line_width * 0.65 / 0.75, 2)
infill_pattern = cubic
line_width = =machine_nozzle_size * 0.9375 line_width = =machine_nozzle_size * 0.9375
machine_nozzle_cool_down_speed = 0.75 machine_nozzle_cool_down_speed = 0.75
machine_nozzle_heat_up_speed = 1.6 machine_nozzle_heat_up_speed = 1.6
material_final_print_temperature = =max(-273.15, material_print_temperature - 15) material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
material_print_temperature = =default_material_print_temperature + 10 material_print_temperature = =default_material_print_temperature + 10
prime_tower_enable = False prime_tower_enable = True
support_angle = 70 support_angle = 70
support_line_width = =line_width * 0.75 support_line_width = =line_width * 0.75
support_pattern = ='triangles'
support_xy_distance = =wall_line_width_0 * 1.5 support_xy_distance = =wall_line_width_0 * 1.5
top_bottom_thickness = =layer_height * 4 top_bottom_thickness = =layer_height * 4
wall_line_width = =round(line_width * 0.75 / 0.75, 2) wall_line_width = =round(line_width * 0.75 / 0.75, 2)
@ -35,8 +33,9 @@ wall_line_width_x = =round(wall_line_width * 0.625 / 0.75, 2)
wall_thickness = =wall_line_width_0 + wall_line_width_x wall_thickness = =wall_line_width_0 + wall_line_width_x
retract_at_layer_change = False retract_at_layer_change = False
speed_print = 45 speed_print = 45
speed_wall = =round(speed_print * 40 / 45) speed_topbottom = =math.ceil(speed_print * 35 / 45)
speed_wall_0 = =round(speed_print * 35 / 45) speed_wall = =math.ceil(speed_print * 40 / 45)
speed_topbottom = =round(speed_print * 35 / 45) speed_wall_x = =speed_wall
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
infill_sparse_density = 15 infill_sparse_density = 15
layer_height_0 = 0.4 layer_height_0 = 0.4

View file

@ -1033,4 +1033,59 @@ QtObject {
label: Item { } label: Item { }
} }
} }
property Component toolbox_action_button: Component {
ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color:
{
if (control.installed)
{
return UM.Theme.getColor("action_button_disabled")
}
else
{
if (control.hovered)
{
return UM.Theme.getColor("primary_hover")
}
else
{
return UM.Theme.getColor("primary")
}
}
}
}
label: Label
{
text: control.text
color:
{
if (control.installed)
{
return UM.Theme.getColor("action_button_disabled_text")
}
else
{
if (control.hovered)
{
return UM.Theme.getColor("button_text_hover")
}
else
{
return UM.Theme.getColor("button_text")
}
}
}
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default_bold")
}
}
}
} }

View file

@ -69,8 +69,6 @@
"colors": { "colors": {
"sidebar": [255, 255, 255, 255], "sidebar": [255, 255, 255, 255],
"lining": [192, 193, 194, 255], "lining": [192, 193, 194, 255],
"viewport_overlay": [0, 0, 0, 192], "viewport_overlay": [0, 0, 0, 192],
@ -458,8 +456,8 @@
"toolbox_property_label": [1.0, 2.0], "toolbox_property_label": [1.0, 2.0],
"toolbox_heading_label": [1.0, 4.0], "toolbox_heading_label": [1.0, 4.0],
"toolbox_header": [1.0, 4.0], "toolbox_header": [1.0, 4.0],
"toolbox_action_button": [8.0, 2.5],
"toolbox_progress_bar": [8.0, 0.5], "toolbox_progress_bar": [8.0, 0.5],
"toolbox_chart_row": [1.0, 2.0] "toolbox_chart_row": [1.0, 2.0],
"toolbox_action_button": [8.0, 2.5]
} }
} }

View file

@ -46,14 +46,9 @@ def main():
print("------------- Checking module {mod}".format(**locals())) print("------------- Checking module {mod}".format(**locals()))
result = subprocess.run([sys.executable, mypyModule, "-p", mod]) result = subprocess.run([sys.executable, mypyModule, "-p", mod])
if result.returncode != 0: if result.returncode != 0:
print(""" print("\nModule {mod} failed checking. :(".format(**locals()))
Module {mod} failed checking. :( return 1
""".format(**locals()))
break
else: else:
print(""" print("\n\nDone checking. All is good.")
Done checking. All is good.
""")
return 0 return 0
sys.exit(main()) sys.exit(main())