mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'master' into WIP_improve_initialization
This commit is contained in:
commit
8ad409ff55
74 changed files with 1810 additions and 778 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -14,6 +14,7 @@ CuraEngine.exe
|
|||
LC_MESSAGES
|
||||
.cache
|
||||
*.qmlc
|
||||
.mypy_cache
|
||||
|
||||
#MacOS
|
||||
.DS_Store
|
||||
|
|
|
@ -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.
|
||||
|
||||
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}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
#Add code style test.
|
||||
add_test(
|
||||
NAME "code-style"
|
||||
COMMAND ${PYTHON_EXECUTABLE} run_mypy.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
|
@ -295,7 +295,9 @@ class CuraApplication(QtApplication):
|
|||
|
||||
Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||
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
|
||||
# SettingDefinition and SettingFunction.
|
||||
|
@ -439,7 +441,7 @@ class CuraApplication(QtApplication):
|
|||
"RotateTool",
|
||||
"ScaleTool",
|
||||
"SelectionTool",
|
||||
"TranslateTool"
|
||||
"TranslateTool",
|
||||
])
|
||||
self._i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
@ -986,6 +988,8 @@ class CuraApplication(QtApplication):
|
|||
scene_bounding_box = None
|
||||
is_block_slicing_node = False
|
||||
active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
|
||||
|
||||
print_information = self.getPrintInformation()
|
||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||
if (
|
||||
not issubclass(type(node), CuraSceneNode) or
|
||||
|
@ -997,6 +1001,11 @@ class CuraApplication(QtApplication):
|
|||
is_block_slicing_node = True
|
||||
|
||||
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:
|
||||
scene_bounding_box = node.getBoundingBox()
|
||||
else:
|
||||
|
@ -1004,7 +1013,7 @@ class CuraApplication(QtApplication):
|
|||
if other_bb is not None:
|
||||
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||
|
||||
print_information = self.getPrintInformation()
|
||||
|
||||
if print_information:
|
||||
print_information.setPreSliced(is_block_slicing_node)
|
||||
|
||||
|
@ -1121,39 +1130,6 @@ class CuraApplication(QtApplication):
|
|||
|
||||
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.
|
||||
@pyqtSlot()
|
||||
def resetAllTranslation(self):
|
||||
|
|
|
@ -15,12 +15,10 @@ from UM.Logger import Logger
|
|||
from UM.Resources import Resources
|
||||
from UM.Version import Version
|
||||
|
||||
|
||||
class CuraPackageManager(QObject):
|
||||
Version = 1
|
||||
|
||||
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created
|
||||
# files.
|
||||
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created files.
|
||||
PREFIX_PLACE_HOLDER = "-CP;"
|
||||
|
||||
def __init__(self, parent = None):
|
||||
|
@ -31,13 +29,21 @@ class CuraPackageManager(QObject):
|
|||
self._plugin_registry = self._application.getPluginRegistry()
|
||||
|
||||
# JSON file that keeps track of all installed packages.
|
||||
self._package_management_file_path = os.path.join(os.path.abspath(Resources.getDataStoragePath()),
|
||||
"packages.json")
|
||||
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
|
||||
self._bundled_package_management_file_path = os.path.join(
|
||||
os.path.abspath(Resources.getBundledResourcesPath()),
|
||||
"packages.json"
|
||||
)
|
||||
self._user_package_management_file_path = os.path.join(
|
||||
os.path.abspath(Resources.getDataStoragePath()),
|
||||
"packages.json"
|
||||
)
|
||||
|
||||
installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed.
|
||||
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.
|
||||
|
||||
def initialize(self):
|
||||
self._loadManagementData()
|
||||
|
@ -46,48 +52,62 @@ class CuraPackageManager(QObject):
|
|||
|
||||
# (for initialize) Loads the package management file if exists
|
||||
def _loadManagementData(self) -> None:
|
||||
if not os.path.exists(self._package_management_file_path):
|
||||
Logger.log("i", "Package management file %s doesn't exist, do nothing", self._package_management_file_path)
|
||||
# The bundles package management file should always be there
|
||||
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
|
||||
|
||||
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
|
||||
container_registry = self._application.getContainerRegistry()
|
||||
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._to_remove_package_set = set(management_dict.get("to_remove", []))
|
||||
self._to_install_package_dict = management_dict.get("to_install", {})
|
||||
|
||||
Logger.log("i", "Package management file %s is loaded", self._package_management_file_path)
|
||||
Logger.log("i", "Loaded user packages management file from %s", self._user_package_management_file_path)
|
||||
|
||||
def _saveManagementData(self) -> None:
|
||||
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
|
||||
container_registry = self._application.getContainerRegistry()
|
||||
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,
|
||||
"installed": self._installed_package_dict,
|
||||
"to_remove": list(self._to_remove_package_set),
|
||||
"to_install": self._to_install_package_dict}
|
||||
data_dict["to_remove"] = list(data_dict["to_remove"])
|
||||
json.dump(data_dict, f)
|
||||
Logger.log("i", "Package management file %s is saved", self._package_management_file_path)
|
||||
json.dump(data_dict, f, sort_keys = True, indent = 4)
|
||||
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.
|
||||
def _removeAllScheduledPackages(self) -> None:
|
||||
for package_id in self._to_remove_package_set:
|
||||
self._purgePackage(package_id)
|
||||
del self._installed_package_dict[package_id]
|
||||
self._to_remove_package_set.clear()
|
||||
self._saveManagementData()
|
||||
|
||||
# (for initialize) Installs all packages that have been scheduled to be installed.
|
||||
def _installAllScheduledPackages(self) -> None:
|
||||
for package_id, installation_package_data in self._to_install_package_dict.items():
|
||||
self._installPackage(installation_package_data)
|
||||
self._to_install_package_dict.clear()
|
||||
self._saveManagementData()
|
||||
|
||||
while self._to_install_package_dict:
|
||||
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()
|
||||
|
||||
# Checks the given package is installed. If so, return a dictionary that contains the package's information.
|
||||
def getInstalledPackageInfo(self, package_id: str) -> Optional[dict]:
|
||||
|
@ -99,87 +119,66 @@ class CuraPackageManager(QObject):
|
|||
return package_info
|
||||
|
||||
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
|
||||
|
||||
for section, packages in self.getAllInstalledPackagesInfo().items():
|
||||
for package in packages:
|
||||
if package["package_id"] == package_id:
|
||||
return package
|
||||
if package_id in self._bundled_package_dict:
|
||||
package_info = self._bundled_package_dict[package_id]["package_info"]
|
||||
return package_info
|
||||
|
||||
return None
|
||||
|
||||
def getAllInstalledPackagesInfo(self) -> dict:
|
||||
installed_package_id_set = set(self._installed_package_dict.keys()) | set(self._to_install_package_dict.keys())
|
||||
installed_package_id_set = installed_package_id_set.difference(self._to_remove_package_set)
|
||||
# Add bundled, installed, and to-install packages to the set of installed package IDs
|
||||
all_installed_ids = set()
|
||||
|
||||
managed_package_id_set = installed_package_id_set | self._to_remove_package_set
|
||||
|
||||
# TODO: For absolutely no reason, this function seems to run in a loop
|
||||
# even though no loop is ever called with it.
|
||||
if self._bundled_package_dict.keys():
|
||||
all_installed_ids = all_installed_ids.union(set(self._bundled_package_dict.keys()))
|
||||
if self._installed_package_dict.keys():
|
||||
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>
|
||||
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():
|
||||
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:
|
||||
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_type not in installed_packages_dict:
|
||||
installed_packages_dict[package_type] = []
|
||||
installed_packages_dict[package_type].append( package_info )
|
||||
if package_info is None:
|
||||
continue
|
||||
|
||||
# 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)
|
||||
|
||||
# Also get all bundled plugins
|
||||
all_metadata = self._plugin_registry.getAllMetaData()
|
||||
for item in all_metadata:
|
||||
if item == {}:
|
||||
continue
|
||||
# If the package ID is in bundled, label it as such
|
||||
package_info["is_bundled"] = package_info["package_id"] in self._bundled_package_dict.keys()
|
||||
|
||||
plugin_package_info = self.__convertPluginMetadataToPackageMetadata(item)
|
||||
# Only gather the bundled plugins here.
|
||||
package_id = plugin_package_info["package_id"]
|
||||
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
|
||||
plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
|
||||
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 )
|
||||
# If there is not a section in the dict for this type, add it
|
||||
if package_info["package_type"] not in installed_packages_dict:
|
||||
installed_packages_dict[package_info["package_type"]] = []
|
||||
|
||||
# Finally, add the data
|
||||
installed_packages_dict[package_info["package_type"]].append(package_info)
|
||||
|
||||
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.
|
||||
def isPackageInstalled(self, package_id: str) -> bool:
|
||||
return self.getInstalledPackageInfo(package_id) is not None
|
||||
|
@ -293,7 +292,7 @@ class CuraPackageManager(QObject):
|
|||
from cura.CuraApplication import CuraApplication
|
||||
installation_dirs_dict = {
|
||||
"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)),
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ from UM.i18n import i18nCatalog
|
|||
from UM.Logger import Logger
|
||||
from UM.Qt.Duration import Duration
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase
|
||||
|
||||
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
|
||||
# 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(".")
|
||||
|
||||
# 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
|
||||
# name is "" when I first had some meshes and afterwards I deleted them so the naming should start again
|
||||
is_empty = name == ""
|
||||
if is_gcode or is_project_file or (is_empty or (self._base_name == "" and self._base_name != name)):
|
||||
is_empty = check_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
|
||||
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()
|
||||
|
||||
|
|
|
@ -385,7 +385,8 @@ class ExtruderManager(QObject):
|
|||
|
||||
# Register the extruder trains by position
|
||||
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. ???
|
||||
extruder_train.setParent(global_stack)
|
||||
|
|
|
@ -38,7 +38,7 @@ class ExtruderStack(CuraContainerStack):
|
|||
#
|
||||
# This will set the next stack and ensure that we register this stack as an extruder.
|
||||
@override(ContainerStack)
|
||||
def setNextStack(self, stack: CuraContainerStack) -> None:
|
||||
def setNextStack(self, stack: CuraContainerStack, connect_signals: bool = True) -> None:
|
||||
super().setNextStack(stack)
|
||||
stack.addExtruder(self)
|
||||
self.addMetaDataEntry("machine", stack.id)
|
||||
|
|
|
@ -125,7 +125,7 @@ class GlobalStack(CuraContainerStack):
|
|||
#
|
||||
# This will simply raise an exception since the Global stack cannot have a next stack.
|
||||
@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!")
|
||||
|
||||
# protected:
|
||||
|
@ -153,6 +153,23 @@ class GlobalStack(CuraContainerStack):
|
|||
|
||||
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:
|
||||
global_stack_mime = MimeType(
|
||||
|
|
|
@ -6,6 +6,7 @@ import time
|
|||
#Type hinting.
|
||||
from typing import List, Dict, TYPE_CHECKING, Optional
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
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):
|
||||
# An active machine was saved, so restore it.
|
||||
self.setActiveMachine(active_machine_id)
|
||||
# Make sure _active_container_stack is properly initiated
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(0)
|
||||
|
||||
def _onOutputDevicesChanged(self) -> None:
|
||||
self._printer_output_devices = []
|
||||
|
@ -358,6 +357,10 @@ class MachineManager(QObject):
|
|||
return
|
||||
|
||||
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
|
||||
self._global_container_stack = global_stack
|
||||
self._application.setGlobalContainerStack(global_stack)
|
||||
|
|
|
@ -15,6 +15,7 @@ from UM.Math.Vector import Vector
|
|||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
@ -25,6 +26,15 @@ from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
|||
|
||||
MYPY = False
|
||||
|
||||
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/x-cura-project-file",
|
||||
comment = "Cura Project File",
|
||||
suffixes = ["curaproject.3mf"]
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
if not MYPY:
|
||||
import xml.etree.cElementTree as ET
|
||||
|
|
|
@ -66,8 +66,8 @@ Generate a cube mesh to prevent support material generation in specific areas of
|
|||
*Real bridging - smartavionics
|
||||
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
|
||||
The CuraEngine executable now contains a dedicated icon, author information and a license.
|
||||
*Updated CuraEngine executable - thopiekar & Ultimaker B.V. ❤️
|
||||
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
|
||||
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 user’s system).
|
||||
|
|
|
@ -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.
|
||||
|
||||
from UM.Application import Application
|
||||
|
@ -22,12 +22,16 @@ from cura.Settings.ExtruderManager import ExtruderManager
|
|||
import numpy
|
||||
import math
|
||||
import re
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
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:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
Application.getInstance().hideMessageSignal.connect(self._onHideMessage)
|
||||
self._cancelled = False
|
||||
self._message = None
|
||||
|
@ -44,19 +48,18 @@ class FlavorParser:
|
|||
|
||||
Application.getInstance().getPreferences().addPreference("gcodereader/show_caution", True)
|
||||
|
||||
def _clearValues(self):
|
||||
def _clearValues(self) -> None:
|
||||
self._extruder_number = 0
|
||||
self._extrusion_length_offset = [0]
|
||||
self._layer_type = LayerPolygon.Inset0Type
|
||||
self._layer_number = 0
|
||||
self._previous_z = 0
|
||||
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_extrusion = True # It can become absolute (M82, default) or relative (M83)
|
||||
|
||||
@staticmethod
|
||||
def _getValue(line, code):
|
||||
def _getValue(line: str, code: str) -> Optional[Union[str, int, float]]:
|
||||
n = line.find(code)
|
||||
if n < 0:
|
||||
return None
|
||||
|
@ -71,29 +74,29 @@ class FlavorParser:
|
|||
except:
|
||||
return None
|
||||
|
||||
def _getInt(self, line, code):
|
||||
def _getInt(self, line: str, code: str) -> Optional[int]:
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return int(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _getFloat(self, line, code):
|
||||
def _getFloat(self, line: str, code: str) -> Optional[float]:
|
||||
value = self._getValue(line, code)
|
||||
try:
|
||||
return float(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
def _onHideMessage(self, message):
|
||||
def _onHideMessage(self, message: str) -> None:
|
||||
if message == self._message:
|
||||
self._cancelled = True
|
||||
|
||||
@staticmethod
|
||||
def _getNullBoundingBox():
|
||||
def _getNullBoundingBox() -> AxisAlignedBox:
|
||||
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
|
||||
for point in path:
|
||||
if point[5] > 0:
|
||||
|
@ -139,12 +142,12 @@ class FlavorParser:
|
|||
this_layer.polygons.append(this_poly)
|
||||
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.setLayerHeight(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
|
||||
Af = (self._filament_diameter / 2) ** 2 * numpy.pi
|
||||
# Length of the extruded filament
|
||||
|
@ -166,7 +169,7 @@ class FlavorParser:
|
|||
return 0.35
|
||||
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
|
||||
|
||||
if self._is_absolute_positioning:
|
||||
|
@ -202,7 +205,7 @@ class FlavorParser:
|
|||
_gCode1 = _gCode0
|
||||
|
||||
## 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(
|
||||
params.x if params.x is not None else position.x,
|
||||
params.y if params.y is not None else position.y,
|
||||
|
@ -211,20 +214,20 @@ class FlavorParser:
|
|||
position.e)
|
||||
|
||||
## 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_extrusion = True
|
||||
return position
|
||||
|
||||
## 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_extrusion = False
|
||||
return position
|
||||
|
||||
## Reset the current position to the values specified.
|
||||
# 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:
|
||||
# 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
|
||||
|
@ -236,7 +239,7 @@ class FlavorParser:
|
|||
params.f if params.f is not None else position.f,
|
||||
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)
|
||||
line = line.split(";", 1)[0] # Remove comments (if any)
|
||||
if func is not None:
|
||||
|
@ -257,27 +260,25 @@ class FlavorParser:
|
|||
f = float(item[1:]) / 60
|
||||
if item[0] == "E":
|
||||
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)
|
||||
return func(position, params, path)
|
||||
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
|
||||
if self._extruder_number + 1 > len(position.e):
|
||||
self._extrusion_length_offset.extend([0] * (self._extruder_number - len(position.e) + 1))
|
||||
position.e.extend([0] * (self._extruder_number - len(position.e) + 1))
|
||||
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
|
||||
|
||||
_type_keyword = ";TYPE:"
|
||||
_layer_keyword = ";LAYER:"
|
||||
|
||||
## For showing correct x, y offsets for each extruder
|
||||
def _extruderOffsets(self):
|
||||
def _extruderOffsets(self) -> Dict[int, List[float]]:
|
||||
result = {}
|
||||
for extruder in ExtruderManager.getInstance().getExtruderStacks():
|
||||
result[int(extruder.getMetaData().get("position", "0"))] = [
|
||||
|
@ -285,7 +286,7 @@ class FlavorParser:
|
|||
extruder.getProperty("machine_nozzle_offset_y", "value")]
|
||||
return result
|
||||
|
||||
def processGCodeStream(self, stream):
|
||||
def processGCodeStream(self, stream: str) -> Optional[CuraSceneNode]:
|
||||
Logger.log("d", "Preparing to load GCode")
|
||||
self._cancelled = False
|
||||
# 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")
|
||||
|
||||
settings = Application.getInstance().getGlobalContainerStack()
|
||||
machine_width = settings.getProperty("machine_width", "value")
|
||||
machine_depth = settings.getProperty("machine_depth", "value")
|
||||
|
||||
if not self._center_is_zero:
|
||||
if not settings.getProperty("machine_center_is_zero", "value"):
|
||||
machine_width = settings.getProperty("machine_width", "value")
|
||||
machine_depth = settings.getProperty("machine_depth", "value")
|
||||
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
|
||||
|
||||
Logger.log("d", "GCode loading finished")
|
||||
|
|
|
@ -5,10 +5,20 @@ from UM.FileHandler.FileReader import FileReader
|
|||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Application import Application
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
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 GCodeReader(MeshReader):
|
||||
|
||||
|
|
|
@ -66,9 +66,9 @@ class GCodeWriter(MeshWriter):
|
|||
|
||||
active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
scene = Application.getInstance().getController().getScene()
|
||||
gcode_dict = getattr(scene, "gcode_dict")
|
||||
if not gcode_dict:
|
||||
if not hasattr(scene, "gcode_dict"):
|
||||
return False
|
||||
gcode_dict = getattr(scene, "gcode_dict")
|
||||
gcode_list = gcode_dict.get(active_build_plate, None)
|
||||
if gcode_list is not None:
|
||||
has_settings = False
|
||||
|
|
|
@ -56,8 +56,6 @@ class MachineSettingsAction(MachineAction):
|
|||
if self._isEmptyDefinitionChanges(definition_changes_id):
|
||||
return
|
||||
|
||||
self._container_registry.removeContainer(definition_changes_id)
|
||||
|
||||
def _reset(self):
|
||||
if not self._global_container_stack:
|
||||
return
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Machine Settings action",
|
||||
"author": "fieldOfView",
|
||||
"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,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
|
|
@ -12,11 +12,14 @@ Window
|
|||
property var selection: null
|
||||
title: catalog.i18nc("@title", "Toolbox")
|
||||
modality: Qt.ApplicationModal
|
||||
flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint
|
||||
|
||||
width: 720 * screenScaleFactor
|
||||
height: 640 * screenScaleFactor
|
||||
minimumWidth: 720 * screenScaleFactor
|
||||
maximumWidth: 720 * screenScaleFactor
|
||||
minimumHeight: 350 * screenScaleFactor
|
||||
minimumWidth: width
|
||||
maximumWidth: minimumWidth
|
||||
minimumHeight: height
|
||||
maximumHeight: minimumHeight
|
||||
color: UM.Theme.getColor("sidebar")
|
||||
UM.I18nCatalog
|
||||
{
|
||||
|
@ -76,7 +79,7 @@ Window
|
|||
{
|
||||
id: footer
|
||||
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:
|
||||
Connections
|
||||
|
|
29
plugins/Toolbox/resources/qml/ToolboxActionButtonStyle.qml
Normal file
29
plugins/Toolbox/resources/qml/ToolboxActionButtonStyle.qml
Normal 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
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// 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.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
Item
|
||||
{
|
||||
id: page
|
||||
property var details: base.selection
|
||||
property var details: base.selection || {}
|
||||
anchors.fill: parent
|
||||
ToolboxBackColumn
|
||||
{
|
||||
|
@ -32,6 +32,7 @@ Item
|
|||
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: details.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
|
@ -53,7 +54,7 @@ Item
|
|||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
text: details.name
|
||||
text: details.name || ""
|
||||
font: UM.Theme.getFont("large")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
|
@ -62,7 +63,7 @@ Item
|
|||
Label
|
||||
{
|
||||
id: description
|
||||
text: details.description
|
||||
text: details.description || ""
|
||||
anchors
|
||||
{
|
||||
top: title.bottom
|
||||
|
@ -114,6 +115,7 @@ Item
|
|||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import UM 1.1 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
property var packageData
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
height: visible ? childrenRect.height : 0
|
||||
visible: packageData.type == "material" && packageData.has_configs
|
||||
|
@ -36,8 +37,8 @@ Item
|
|||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
elide: Text.ElideRight
|
||||
text: styleData.value || ""
|
||||
color: UM.Theme.getColor("text")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
@ -55,8 +56,8 @@ Item
|
|||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
elide: Text.ElideRight
|
||||
text: styleData.value || ""
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
|
@ -67,8 +68,8 @@ Item
|
|||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: styleData.elideMode
|
||||
text: styleData.value
|
||||
elide: Text.ElideRight
|
||||
text: styleData.value || ""
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// 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.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
@ -33,6 +33,7 @@ Item
|
|||
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: details.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
|
@ -54,8 +55,9 @@ Item
|
|||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
text: details.name
|
||||
text: details.name || ""
|
||||
font: UM.Theme.getFont("large")
|
||||
color: UM.Theme.getColor("text")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
|
@ -113,7 +115,7 @@ Item
|
|||
text:
|
||||
{
|
||||
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")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
@ -133,6 +135,7 @@ Item
|
|||
}
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,8 @@ Item
|
|||
{
|
||||
id: tile
|
||||
property bool installed: toolbox.isInstalled(model.id)
|
||||
property var packageData: model
|
||||
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
|
||||
{
|
||||
id: normalData
|
||||
|
@ -46,6 +45,7 @@ Item
|
|||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: controls
|
||||
|
@ -76,58 +76,9 @@ Item
|
|||
}
|
||||
enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) //Don't allow installing while another download is running.
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
property alias installed: tile.installed
|
||||
style: UM.Theme.styles.toolbox_action_button
|
||||
onClicked:
|
||||
{
|
||||
if (installed)
|
||||
|
@ -164,6 +115,7 @@ Item
|
|||
id: compatibilityChart
|
||||
anchors.top: normalData.bottom
|
||||
width: normalData.width
|
||||
packageData: model
|
||||
}
|
||||
|
||||
Rectangle
|
||||
|
|
|
@ -9,9 +9,7 @@ import UM 1.1 as UM
|
|||
|
||||
Column
|
||||
{
|
||||
// HACK: GridLayouts don't render to the correct height with odd numbers of
|
||||
// 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
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
Label
|
||||
|
@ -36,6 +34,7 @@ Column
|
|||
delegate: ToolboxDownloadsGridTile
|
||||
{
|
||||
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
|
||||
Layout.preferredHeight: UM.Theme.getSize("toolbox_thumbnail_small").height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// 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.Styles 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
@ -34,10 +34,11 @@ Item
|
|||
Image
|
||||
{
|
||||
anchors.centerIn: parent
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_small").width - 26
|
||||
height: UM.Theme.getSize("toolbox_thumbnail_small").height - 26
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width
|
||||
height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
}
|
||||
}
|
||||
Column
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// 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.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
@ -9,7 +9,7 @@ import UM 1.1 as UM
|
|||
Item
|
||||
{
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_large").width
|
||||
height: childrenRect.height
|
||||
height: thumbnail.height + packageName.height
|
||||
Rectangle
|
||||
{
|
||||
id: highlight
|
||||
|
@ -40,14 +40,16 @@ Item
|
|||
height: UM.Theme.getSize("toolbox_thumbnail_large").height - 2 * UM.Theme.getSize("default_margin").height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: model.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: packageName
|
||||
text: model.name
|
||||
anchors
|
||||
{
|
||||
bottom: parent.bottom
|
||||
top: thumbnail.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
|
|
@ -14,8 +14,7 @@ Item
|
|||
height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0
|
||||
Label
|
||||
{
|
||||
visible: toolbox.restartRequired
|
||||
text: catalog.i18nc("@info", "You will need to restart Cura before changes in plugins have effect.")
|
||||
text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.")
|
||||
height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors
|
||||
|
@ -38,7 +37,6 @@ Item
|
|||
right: parent.right
|
||||
rightMargin: UM.Theme.getSize("wide_margin").width
|
||||
}
|
||||
visible: toolbox.restartRequired
|
||||
iconName: "dialog-restart"
|
||||
onClicked: toolbox.restart()
|
||||
style: ButtonStyle
|
||||
|
@ -49,7 +47,7 @@ Item
|
|||
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
|
||||
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
|
||||
}
|
||||
label: Text
|
||||
label: Label
|
||||
{
|
||||
color: UM.Theme.getColor("button_text")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
|
@ -61,7 +59,7 @@ Item
|
|||
}
|
||||
ToolboxShadow
|
||||
{
|
||||
visible: toolbox.restartRequired
|
||||
visible: footer.visible
|
||||
anchors.bottom: footer.top
|
||||
reversed: true
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ Item
|
|||
ToolboxTabButton
|
||||
{
|
||||
text: catalog.i18nc("@title:tab", "Plugins")
|
||||
active: toolbox.viewCategory == "plugin"
|
||||
active: toolbox.viewCategory == "plugin" && enabled
|
||||
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
|
||||
onClicked:
|
||||
{
|
||||
toolbox.filterModelByProp("packages", "type", "plugin")
|
||||
|
@ -35,7 +36,8 @@ Item
|
|||
ToolboxTabButton
|
||||
{
|
||||
text: catalog.i18nc("@title:tab", "Materials")
|
||||
active: toolbox.viewCategory == "material"
|
||||
active: toolbox.viewCategory == "material" && enabled
|
||||
enabled: toolbox.viewPage != "loading" && toolbox.viewPage != "errored"
|
||||
onClicked:
|
||||
{
|
||||
toolbox.filterModelByProp("authors", "package_types", "material")
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// 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.Styles 1.4
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
height: UM.Theme.getSize("toolbox_installed_tile").height
|
||||
width: parent.width
|
||||
property bool canUpdate: false
|
||||
property bool isEnabled: true
|
||||
height: UM.Theme.getSize("toolbox_installed_tile").height
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: UM.Theme.getColor("lining")
|
||||
|
@ -23,197 +20,86 @@ Item
|
|||
height: UM.Theme.getSize("default_lining").height
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
Column
|
||||
Row
|
||||
{
|
||||
id: pluginInfo
|
||||
property var color: isEnabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
||||
id: tileRow
|
||||
height: parent.height
|
||||
anchors
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
topPadding: UM.Theme.getSize("default_margin").height
|
||||
|
||||
CheckBox
|
||||
{
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: authorInfo.left
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: model.name
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
color: pluginInfo.color
|
||||
}
|
||||
Text
|
||||
{
|
||||
text: model.description
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
clip: true
|
||||
wrapMode: Text.WordWrap
|
||||
color: pluginInfo.color
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
Column
|
||||
{
|
||||
id: authorInfo
|
||||
height: parent.height
|
||||
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
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (model.author_email)
|
||||
{
|
||||
return "<a href=\"mailto:" + model.author_email + "?Subject=Cura: " + model.name + "\">" + model.author_name + "</a>"
|
||||
}
|
||||
else
|
||||
{
|
||||
return model.author_name
|
||||
}
|
||||
}
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
|
||||
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
||||
}
|
||||
}
|
||||
Column
|
||||
{
|
||||
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")
|
||||
}
|
||||
}
|
||||
id: disableButton
|
||||
checked: isEnabled
|
||||
visible: model.type == "plugin"
|
||||
width: visible ? UM.Theme.getSize("checkbox").width : 0
|
||||
enabled: !toolbox.isDownloading
|
||||
style: ButtonStyle
|
||||
style: UM.Theme.styles.checkbox
|
||||
onClicked: toolbox.isEnabled(model.id) ? toolbox.disable(model.id) : toolbox.enable(model.id)
|
||||
}
|
||||
Column
|
||||
{
|
||||
id: pluginInfo
|
||||
topPadding: UM.Theme.getSize("default_margin").height / 2
|
||||
property var color: model.type === "plugin" && !isEnabled ? UM.Theme.getColor("lining") : UM.Theme.getColor("text")
|
||||
width: tileRow.width - (authorInfo.width + pluginActions.width + 2 * tileRow.spacing + ((disableButton.visible) ? disableButton.width + tileRow.spacing : 0))
|
||||
Label
|
||||
{
|
||||
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
|
||||
}
|
||||
text: model.name
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
wrapMode: Text.WordWrap
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
color: pluginInfo.color
|
||||
}
|
||||
onClicked:
|
||||
Label
|
||||
{
|
||||
if (model.is_bundled)
|
||||
text: model.description
|
||||
maximumLineCount: 3
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
color: pluginInfo.color
|
||||
}
|
||||
}
|
||||
Column
|
||||
{
|
||||
id: authorInfo
|
||||
width: Math.floor(UM.Theme.getSize("toolbox_action_button").width * 1.25)
|
||||
|
||||
Label
|
||||
{
|
||||
text:
|
||||
{
|
||||
if (toolbox.isEnabled(model.id))
|
||||
if (model.author_email)
|
||||
{
|
||||
toolbox.disable(model.id)
|
||||
return "<a href=\"mailto:" + model.author_email + "?Subject=Cura: " + model.name + "\">" + model.author_name + "</a>"
|
||||
}
|
||||
else
|
||||
{
|
||||
toolbox.enable(model.id)
|
||||
return model.author_name
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toolbox.uninstall(model.id)
|
||||
}
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("toolbox_property_label").height
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin")
|
||||
color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
|
||||
linkColor: UM.Theme.getColor("text_link")
|
||||
}
|
||||
}
|
||||
Button
|
||||
ToolboxInstalledTileActions
|
||||
{
|
||||
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);
|
||||
}
|
||||
id: pluginActions
|
||||
}
|
||||
ProgressBar
|
||||
Connections
|
||||
{
|
||||
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")
|
||||
}
|
||||
}
|
||||
target: toolbox
|
||||
onEnabledChanged: isEnabled = toolbox.isEnabled(model.id)
|
||||
onMetadataChanged: canUpdate = toolbox.canUpdate(model.id)
|
||||
}
|
||||
}
|
||||
Connections
|
||||
{
|
||||
target: toolbox
|
||||
onEnabledChanged: isEnabled = toolbox.isEnabled(model.id)
|
||||
onMetadataChanged: canUpdate = toolbox.canUpdate(model.id)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ UM.Dialog
|
|||
anchors.right: parent.right
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
readOnly: true
|
||||
text: licenseDialog.licenseContent
|
||||
text: licenseDialog.licenseContent || ""
|
||||
}
|
||||
}
|
||||
rightButtons:
|
||||
|
|
|
@ -25,7 +25,7 @@ Button
|
|||
height: UM.Theme.getSize("sidebar_header_highlight").height
|
||||
}
|
||||
}
|
||||
label: Text
|
||||
label: Label
|
||||
{
|
||||
text: control.text
|
||||
color:
|
||||
|
@ -43,7 +43,7 @@ Button
|
|||
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
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
|
|
@ -28,8 +28,9 @@ class PackagesModel(ListModel):
|
|||
self.addRoleName(Qt.UserRole + 11, "download_url")
|
||||
self.addRoleName(Qt.UserRole + 12, "last_updated")
|
||||
self.addRoleName(Qt.UserRole + 13, "is_bundled")
|
||||
self.addRoleName(Qt.UserRole + 14, "has_configs")
|
||||
self.addRoleName(Qt.UserRole + 15, "supported_configs")
|
||||
self.addRoleName(Qt.UserRole + 14, "is_enabled")
|
||||
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.
|
||||
self._filter = {} # type: Dict[str, str]
|
||||
|
@ -52,20 +53,26 @@ class PackagesModel(ListModel):
|
|||
configs_model = ConfigsModel()
|
||||
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({
|
||||
"id": package["package_id"],
|
||||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author_id": package["author"]["author_id"] if "author_id" in package["author"] else package["author"]["name"],
|
||||
"author_name": package["author"]["display_name"] if "display_name" in package["author"] else package["author"]["name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else "None",
|
||||
"description": package["description"],
|
||||
"author_id": package["author"]["author_id"],
|
||||
"author_name": package["author"]["display_name"],
|
||||
"author_email": package["author"]["email"] if "email" in package["author"] else None,
|
||||
"description": package["description"] if "description" 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,
|
||||
"download_url": package["download_url"] if "download_url" 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_enabled": package["is_enabled"] if "is_enabled" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model
|
||||
})
|
||||
|
|
|
@ -13,19 +13,17 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkRepl
|
|||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Qt.Bindings.PluginsModel import PluginsModel
|
||||
from UM.Extension import Extension
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Version import Version
|
||||
|
||||
import cura
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from .AuthorsModel import AuthorsModel
|
||||
from .PackagesModel import PackagesModel
|
||||
from .ConfigsModel import ConfigsModel
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
## The Toolbox class is responsible of communicating with the server through the API
|
||||
class Toolbox(QObject, Extension):
|
||||
def __init__(self, parent=None) -> None:
|
||||
|
@ -34,7 +32,7 @@ class Toolbox(QObject, Extension):
|
|||
self._application = Application.getInstance()
|
||||
self._package_manager = None
|
||||
self._plugin_registry = Application.getInstance().getPluginRegistry()
|
||||
self._packages_version = self._plugin_registry.APIVersion
|
||||
self._packages_version = self._getPackagesVersion()
|
||||
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)
|
||||
|
||||
|
@ -66,22 +64,22 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
# Data:
|
||||
self._metadata = {
|
||||
"authors": [],
|
||||
"packages": [],
|
||||
"plugins_showcase": [],
|
||||
"plugins_installed": [],
|
||||
"materials_showcase": [],
|
||||
"authors": [],
|
||||
"packages": [],
|
||||
"plugins_showcase": [],
|
||||
"plugins_installed": [],
|
||||
"materials_showcase": [],
|
||||
"materials_installed": []
|
||||
}
|
||||
|
||||
# Models:
|
||||
self._models = {
|
||||
"authors": AuthorsModel(self),
|
||||
"packages": PackagesModel(self),
|
||||
"plugins_showcase": PackagesModel(self),
|
||||
"plugins_available": PackagesModel(self),
|
||||
"plugins_installed": PackagesModel(self),
|
||||
"materials_showcase": AuthorsModel(self),
|
||||
"authors": AuthorsModel(self),
|
||||
"packages": PackagesModel(self),
|
||||
"plugins_showcase": PackagesModel(self),
|
||||
"plugins_available": PackagesModel(self),
|
||||
"plugins_installed": PackagesModel(self),
|
||||
"materials_showcase": AuthorsModel(self),
|
||||
"materials_available": PackagesModel(self),
|
||||
"materials_installed": PackagesModel(self)
|
||||
}
|
||||
|
@ -154,6 +152,13 @@ class Toolbox(QObject, Extension):
|
|||
def _onAppInitialized(self) -> None:
|
||||
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()
|
||||
def browsePackages(self) -> None:
|
||||
# Create the network manager:
|
||||
|
@ -244,7 +249,6 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
@pyqtSlot()
|
||||
def restart(self):
|
||||
self._package_manager._removeAllScheduledPackages()
|
||||
CuraApplication.getInstance().windowClosed()
|
||||
|
||||
# Checks
|
||||
|
@ -326,8 +330,8 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
# Handlers for Network Events
|
||||
# --------------------------------------------------------------------------
|
||||
def _onNetworkAccessibleChanged(self, accessible: int) -> None:
|
||||
if accessible == 0:
|
||||
def _onNetworkAccessibleChanged(self, network_accessibility: QNetworkAccessManager.NetworkAccessibility) -> None:
|
||||
if network_accessibility == QNetworkAccessManager.NotAccessible:
|
||||
self.resetDownload()
|
||||
|
||||
def _onRequestFinished(self, reply: QNetworkReply) -> None:
|
||||
|
@ -347,50 +351,55 @@ class Toolbox(QObject, Extension):
|
|||
if reply.operation() == QNetworkAccessManager.GetOperation:
|
||||
for type, url in self._request_urls.items():
|
||||
if reply.url() == url:
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
|
||||
# Check for errors:
|
||||
if "errors" in json_data:
|
||||
for error in json_data["errors"]:
|
||||
Logger.log("e", "%s", error["title"])
|
||||
return
|
||||
|
||||
# Create model and apply metadata:
|
||||
if not self._models[type]:
|
||||
Logger.log("e", "Could not find the %s model.", type)
|
||||
break
|
||||
|
||||
# HACK: Eventually get rid of the code from here...
|
||||
if type is "plugins_showcase" or type is "materials_showcase":
|
||||
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
else:
|
||||
# ...until here.
|
||||
# This hack arises for multiple reasons but the main
|
||||
# one is because there are not separate API calls
|
||||
# for different kinds of showcases.
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
# Do some auto filtering
|
||||
# TODO: Make multiple API calls in the future to handle this
|
||||
if type is "packages":
|
||||
self._models[type].setFilter({"type": "plugin"})
|
||||
if type is "authors":
|
||||
self._models[type].setFilter({"package_types": "material"})
|
||||
|
||||
self.metadataChanged.emit()
|
||||
|
||||
if self.loadingComplete() is True:
|
||||
self.setViewPage("overview")
|
||||
|
||||
# Check for errors:
|
||||
if "errors" in json_data:
|
||||
for error in json_data["errors"]:
|
||||
Logger.log("e", "%s", error["title"])
|
||||
return
|
||||
|
||||
# Create model and apply metadata:
|
||||
if not self._models[type]:
|
||||
Logger.log("e", "Could not find the %s model.", type)
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
|
||||
break
|
||||
|
||||
# HACK: Eventually get rid of the code from here...
|
||||
if type is "plugins_showcase" or type is "materials_showcase":
|
||||
self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
|
||||
self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
|
||||
self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
|
||||
self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
|
||||
else:
|
||||
# ...until here.
|
||||
# This hack arises for multiple reasons but the main
|
||||
# one is because there are not separate API calls
|
||||
# for different kinds of showcases.
|
||||
self._metadata[type] = json_data["data"]
|
||||
self._models[type].setMetadata(self._metadata[type])
|
||||
|
||||
# Do some auto filtering
|
||||
# TODO: Make multiple API calls in the future to handle this
|
||||
if type is "packages":
|
||||
self._models[type].setFilter({"type": "plugin"})
|
||||
if type is "authors":
|
||||
self._models[type].setFilter({"package_types": "material"})
|
||||
|
||||
self.metadataChanged.emit()
|
||||
|
||||
if self.loadingComplete() is True:
|
||||
self.setViewPage("overview")
|
||||
|
||||
else:
|
||||
self.setViewPage("errored")
|
||||
self.resetDownload()
|
||||
return
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
|
||||
break
|
||||
|
||||
else:
|
||||
# Ignore any operation that is not a get operation
|
||||
|
@ -403,7 +412,7 @@ class Toolbox(QObject, Extension):
|
|||
if bytes_sent == bytes_total:
|
||||
self.setIsDownloading(False)
|
||||
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)
|
||||
file_path = self._temp_plugin_file.name
|
||||
# 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.activePackageChanged.emit()
|
||||
|
||||
@pyqtProperty("QVariantMap", fset = setActivePackage, notify = activePackageChanged)
|
||||
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
|
||||
def activePackage(self) -> Optional[Dict[str, Any]]:
|
||||
return self._active_package
|
||||
|
||||
|
|
|
@ -11,6 +11,16 @@ except ImportError:
|
|||
|
||||
from UM.i18n import i18nCatalog #To translate the file format description.
|
||||
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")
|
||||
|
||||
|
|
|
@ -188,14 +188,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin):
|
|||
b"name": system_info["name"].encode("utf-8"),
|
||||
b"address": address.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:
|
||||
# Cluster needs an additional request, before it's completed.
|
||||
properties[b"incomplete"] = b"true"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "UM3 Network Connection",
|
||||
"author": "Ultimaker B.V.",
|
||||
"description": "Manages network connections to Ultimaker 3 printers",
|
||||
"description": "Manages network connections to Ultimaker 3 printers.",
|
||||
"version": "1.0.0",
|
||||
"api": 4,
|
||||
"i18n-catalog": "cura"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Ultimaker machine actions",
|
||||
"author": "Ultimaker B.V.",
|
||||
"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,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "UserAgreement",
|
||||
"author": "Ultimaker B.V.",
|
||||
"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,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
|
|
@ -227,6 +227,7 @@
|
|||
{
|
||||
"label": "Outer nozzle diameter",
|
||||
"description": "The outer diameter of the tip of the nozzle.",
|
||||
"unit": "mm",
|
||||
"default_value": 1,
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -238,6 +239,7 @@
|
|||
{
|
||||
"label": "Nozzle length",
|
||||
"description": "The height difference between the tip of the nozzle and the lowest part of the print head.",
|
||||
"unit": "mm",
|
||||
"default_value": 3,
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -261,6 +263,7 @@
|
|||
{
|
||||
"label": "Heat zone length",
|
||||
"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,
|
||||
"type": "float",
|
||||
"settable_per_mesh": false,
|
||||
|
@ -271,6 +274,7 @@
|
|||
{
|
||||
"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.",
|
||||
"unit": "mm",
|
||||
"default_value": 16,
|
||||
"value": "machine_heat_zone_length",
|
||||
"type": "float",
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
|
||||
"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": {
|
||||
"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"
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
"overrides": {
|
||||
"machine_name": { "default_value": "Ultimaker S5" },
|
||||
"machine_width": { "default_value": 330 },
|
||||
"machine_depth": { "default_value": 245 },
|
||||
"machine_depth": { "default_value": 240 },
|
||||
"machine_height": { "default_value": 300 },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_nozzle_heat_up_speed": { "default_value": 1.4 },
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2",
|
||||
"platform": "Vertex_build_panel.stl",
|
||||
"platform_offset": [0, -2, 0],
|
||||
"platform_offset": [0, -3, 0],
|
||||
"supports_usb_connection": true,
|
||||
"supported_actions": ["MachineSettingsAction"]
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2",
|
||||
"platform": "Vertex_build_panel.stl",
|
||||
"platform_offset": [0, -2, 0],
|
||||
"platform_offset": [0, -3, 0],
|
||||
"machine_extruder_trains": {
|
||||
"0": "vertex_k8400_dual_1st",
|
||||
"1": "vertex_k8400_dual_2nd"
|
||||
|
|
|
@ -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
|
||||
msgctxt "@label Hours and minutes"
|
||||
msgid "00h 00min"
|
||||
msgstr "00 Stunden 00 Minuten"
|
||||
msgstr "00 St. 00 M."
|
||||
|
||||
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:398
|
||||
msgctxt "@tooltip"
|
||||
|
@ -5493,7 +5493,7 @@ msgstr "Cura-Profil-Reader"
|
|||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "00h 00min"
|
||||
#~ msgstr "00 Stunden 00 Minuten"
|
||||
#~ msgstr "00 St. 00 M."
|
||||
|
||||
#~ msgctxt "@tooltip"
|
||||
#~ msgid "<b>Time information</b>"
|
||||
|
@ -5517,7 +5517,7 @@ msgstr "Cura-Profil-Reader"
|
|||
|
||||
#~ msgctxt "@label"
|
||||
#~ 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"
|
||||
#~ msgid "UM3 Network Connection (Cluster)"
|
||||
|
|
1153
resources/packages.json
Normal file
1153
resources/packages.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -25,11 +25,11 @@ jerk_enabled = True
|
|||
jerk_print = 25
|
||||
line_width = =machine_nozzle_size * 0.92
|
||||
machine_min_cool_heat_time_window = 15
|
||||
material_bed_temperature_layer_0 = 90
|
||||
material_final_print_temperature = 195
|
||||
material_initial_print_temperature = 200
|
||||
material_print_temperature = 205
|
||||
material_print_temperature_layer_0 = 208
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature - 15
|
||||
material_print_temperature_layer_0 = =material_print_temperature + 3
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
prime_tower_size = 16
|
||||
|
|
|
@ -26,5 +26,6 @@ speed_wall = =math.ceil(speed_print * 45 / 60)
|
|||
speed_wall_0 = =math.ceil(speed_wall * 35 / 45)
|
||||
wall_thickness = 1
|
||||
|
||||
infill_line_width = 0.4
|
||||
speed_infill = 50
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 50 / 60)
|
||||
|
||||
|
|
|
@ -25,5 +25,6 @@ speed_topbottom = =math.ceil(speed_print * 30 / 60)
|
|||
speed_wall = =math.ceil(speed_print * 40 / 60)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
|
||||
infill_line_width = 0.4
|
||||
speed_infill = 45
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 45 / 60)
|
||||
|
||||
|
|
|
@ -24,5 +24,6 @@ speed_layer_0 = 20
|
|||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
infill_line_width = 0.4
|
||||
speed_infill = 40
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
||||
|
||||
|
|
|
@ -23,5 +23,5 @@ speed_layer_0 = 20
|
|||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 55)
|
||||
|
||||
infill_line_width = 0.4
|
||||
speed_infill = 40
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 40 / 55)
|
||||
|
|
|
@ -23,8 +23,7 @@ speed_wall = =math.ceil(speed_print * 45 / 60)
|
|||
speed_wall_0 = =math.ceil(speed_wall * 35 / 45)
|
||||
wall_thickness = 1
|
||||
|
||||
jerk_travel = 50
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = 50
|
||||
speed_infill = =math.ceil(speed_print * 50 / 60)
|
||||
prime_tower_purge_volume = 1
|
||||
|
|
|
@ -22,8 +22,6 @@ speed_topbottom = =math.ceil(speed_print * 30 / 60)
|
|||
speed_wall = =math.ceil(speed_print * 40 / 60)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
|
||||
jerk_travel = 50
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = 50
|
||||
speed_infill = =math.ceil(speed_print * 50 / 60)
|
||||
prime_tower_purge_volume = 1
|
||||
|
|
|
@ -23,8 +23,6 @@ speed_layer_0 = 20
|
|||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
jerk_travel = 50
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = 40
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
||||
prime_tower_purge_volume = 1
|
||||
|
|
|
@ -21,8 +21,6 @@ speed_layer_0 = 20
|
|||
speed_topbottom = =math.ceil(speed_print * 30 / 55)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 55)
|
||||
|
||||
jerk_travel = 50
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = 45
|
||||
speed_infill = =math.ceil(speed_print * 45 / 55)
|
||||
prime_tower_purge_volume = 1
|
||||
|
|
|
@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
|
|||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature_layer_0 = 95
|
||||
material_final_print_temperature = 205
|
||||
material_initial_print_temperature = 210
|
||||
material_print_temperature = 215
|
||||
material_print_temperature_layer_0 = 220
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature - 5
|
||||
material_print_temperature_layer_0 = =material_print_temperature + 5
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
|
|
|
@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
|
|||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature_layer_0 = 95
|
||||
material_final_print_temperature = 195
|
||||
material_initial_print_temperature = 205
|
||||
material_print_temperature = 207
|
||||
material_print_temperature_layer_0 = 210
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_final_print_temperature = =material_print_temperature - 12
|
||||
material_initial_print_temperature = =material_print_temperature - 2
|
||||
material_print_temperature = =default_material_print_temperature - 13
|
||||
material_print_temperature_layer_0 = =material_print_temperature + 3
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
|
|
|
@ -29,11 +29,11 @@ line_width = =machine_nozzle_size * 0.95
|
|||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature_layer_0 = 95
|
||||
material_final_print_temperature = 195
|
||||
material_initial_print_temperature = 200
|
||||
material_print_temperature = 205
|
||||
material_print_temperature_layer_0 = 208
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature - 15
|
||||
material_print_temperature_layer_0 = =material_print_temperature + 3
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
prime_tower_enable = False
|
||||
|
|
|
@ -18,6 +18,7 @@ brim_width = 8.75
|
|||
cool_fan_speed_max = 100
|
||||
cool_min_layer_time_fan_speed_max = 6
|
||||
cool_min_speed = 4
|
||||
gradual_infill_step_height = =5 * layer_height
|
||||
infill_line_width = =round(line_width * 0.38 / 0.38, 2)
|
||||
infill_overlap = 0
|
||||
infill_pattern = cross_3d
|
||||
|
@ -40,7 +41,6 @@ prime_tower_wipe_enabled = True
|
|||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -61,5 +61,3 @@ travel_avoid_distance = 1.5
|
|||
wall_0_inset = 0
|
||||
wall_line_width_x = =line_width
|
||||
wall_thickness = 0.76
|
||||
|
||||
jerk_travel = 50
|
||||
|
|
|
@ -18,6 +18,7 @@ brim_width = 8.75
|
|||
cool_fan_speed_max = 100
|
||||
cool_min_layer_time_fan_speed_max = 6
|
||||
cool_min_speed = 4
|
||||
gradual_infill_step_height = =5 * layer_height
|
||||
infill_line_width = =round(line_width * 0.38 / 0.38, 2)
|
||||
infill_overlap = 0
|
||||
infill_pattern = cross_3d
|
||||
|
@ -40,7 +41,6 @@ prime_tower_wipe_enabled = True
|
|||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -62,4 +62,3 @@ wall_0_inset = 0
|
|||
wall_line_width_x = =line_width
|
||||
wall_thickness = 0.76
|
||||
|
||||
jerk_travel = 50
|
||||
|
|
|
@ -18,6 +18,7 @@ brim_width = 8.75
|
|||
cool_fan_speed_max = 100
|
||||
cool_min_layer_time_fan_speed_max = 6
|
||||
cool_min_speed = 4
|
||||
gradual_infill_step_height = =5 * layer_height
|
||||
infill_line_width = =round(line_width * 0.38 / 0.38, 2)
|
||||
infill_overlap = 0
|
||||
infill_pattern = cross_3d
|
||||
|
@ -39,7 +40,6 @@ prime_tower_wipe_enabled = True
|
|||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.8
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 1.5
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = =line_width * 2
|
||||
retraction_prime_speed = 15
|
||||
|
@ -61,4 +61,3 @@ wall_0_inset = 0
|
|||
wall_line_width_x = =line_width
|
||||
wall_thickness = 0.76
|
||||
|
||||
jerk_travel = 50
|
||||
|
|
|
@ -17,17 +17,15 @@ cool_fan_speed_max = =100
|
|||
cool_min_speed = 2
|
||||
gradual_infill_step_height = =3 * layer_height
|
||||
infill_line_width = =round(line_width * 0.65 / 0.75, 2)
|
||||
infill_pattern = cubic
|
||||
line_width = =machine_nozzle_size * 0.9375
|
||||
machine_nozzle_cool_down_speed = 0.75
|
||||
machine_nozzle_heat_up_speed = 1.6
|
||||
material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
|
||||
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
|
||||
material_print_temperature = =default_material_print_temperature + 10
|
||||
prime_tower_enable = False
|
||||
prime_tower_enable = True
|
||||
support_angle = 70
|
||||
support_line_width = =line_width * 0.75
|
||||
support_pattern = ='triangles'
|
||||
support_xy_distance = =wall_line_width_0 * 1.5
|
||||
top_bottom_thickness = =layer_height * 4
|
||||
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
|
||||
speed_print = 45
|
||||
speed_wall = =round(speed_print * 40 / 45)
|
||||
speed_wall_0 = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =math.ceil(speed_print * 35 / 45)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 45)
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||
infill_sparse_density = 15
|
||||
layer_height_0 = 0.4
|
||||
|
|
|
@ -17,18 +17,16 @@ cool_fan_speed_max = =100
|
|||
cool_min_speed = 2
|
||||
gradual_infill_step_height = =3 * layer_height
|
||||
infill_line_width = =round(line_width * 0.65 / 0.75, 2)
|
||||
infill_pattern = cubic
|
||||
line_width = =machine_nozzle_size * 0.9375
|
||||
machine_nozzle_cool_down_speed = 0.75
|
||||
machine_nozzle_heat_up_speed = 1.6
|
||||
material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
|
||||
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
|
||||
material_print_temperature = =default_material_print_temperature + 15
|
||||
prime_tower_enable = False
|
||||
prime_tower_enable = True
|
||||
raft_margin = 10
|
||||
support_angle = 70
|
||||
support_line_width = =line_width * 0.75
|
||||
support_pattern = ='triangles'
|
||||
support_xy_distance = =wall_line_width_0 * 1.5
|
||||
top_bottom_thickness = =layer_height * 4
|
||||
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
|
||||
retract_at_layer_change = False
|
||||
speed_print = 45
|
||||
speed_wall = =round(speed_print * 40 / 45)
|
||||
speed_wall_0 = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =math.ceil(speed_print * 35 / 45)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 45)
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||
infill_sparse_density = 15
|
||||
layer_height_0 = 0.4
|
||||
|
|
|
@ -17,17 +17,15 @@ cool_fan_speed_max = =100
|
|||
cool_min_speed = 2
|
||||
gradual_infill_step_height = =3 * layer_height
|
||||
infill_line_width = =round(line_width * 0.65 / 0.75, 2)
|
||||
infill_pattern = cubic
|
||||
line_width = =machine_nozzle_size * 0.9375
|
||||
machine_nozzle_cool_down_speed = 0.75
|
||||
machine_nozzle_heat_up_speed = 1.6
|
||||
material_final_print_temperature = =max(-273.15, material_print_temperature - 15)
|
||||
material_initial_print_temperature = =max(-273.15, material_print_temperature - 10)
|
||||
material_print_temperature = =default_material_print_temperature + 10
|
||||
prime_tower_enable = False
|
||||
prime_tower_enable = True
|
||||
support_angle = 70
|
||||
support_line_width = =line_width * 0.75
|
||||
support_pattern = ='triangles'
|
||||
support_xy_distance = =wall_line_width_0 * 1.5
|
||||
top_bottom_thickness = =layer_height * 4
|
||||
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
|
||||
retract_at_layer_change = False
|
||||
speed_print = 45
|
||||
speed_wall = =round(speed_print * 40 / 45)
|
||||
speed_wall_0 = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =round(speed_print * 35 / 45)
|
||||
speed_topbottom = =math.ceil(speed_print * 35 / 45)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 45)
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||
infill_sparse_density = 15
|
||||
layer_height_0 = 0.4
|
||||
|
|
|
@ -1033,4 +1033,59 @@ QtObject {
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,8 +69,6 @@
|
|||
|
||||
"colors": {
|
||||
|
||||
|
||||
|
||||
"sidebar": [255, 255, 255, 255],
|
||||
"lining": [192, 193, 194, 255],
|
||||
"viewport_overlay": [0, 0, 0, 192],
|
||||
|
@ -458,8 +456,8 @@
|
|||
"toolbox_property_label": [1.0, 2.0],
|
||||
"toolbox_heading_label": [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_chart_row": [1.0, 2.0]
|
||||
"toolbox_chart_row": [1.0, 2.0],
|
||||
"toolbox_action_button": [8.0, 2.5]
|
||||
}
|
||||
}
|
||||
|
|
11
run_mypy.py
11
run_mypy.py
|
@ -46,14 +46,9 @@ def main():
|
|||
print("------------- Checking module {mod}".format(**locals()))
|
||||
result = subprocess.run([sys.executable, mypyModule, "-p", mod])
|
||||
if result.returncode != 0:
|
||||
print("""
|
||||
Module {mod} failed checking. :(
|
||||
""".format(**locals()))
|
||||
break
|
||||
print("\nModule {mod} failed checking. :(".format(**locals()))
|
||||
return 1
|
||||
else:
|
||||
print("""
|
||||
|
||||
Done checking. All is good.
|
||||
""")
|
||||
print("\n\nDone checking. All is good.")
|
||||
return 0
|
||||
sys.exit(main())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue