Merge branch 'master' into WIP_improve_initialization

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

1
.gitignore vendored
View file

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

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
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}
)

View file

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

View file

@ -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,11 +29,19 @@ 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"
)
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.
@ -46,47 +52,61 @@ 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()
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.
@ -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
# 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"]] = []
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 )
# 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)),
}

View file

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

View file

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

View file

@ -38,7 +38,7 @@ class ExtruderStack(CuraContainerStack):
#
# This will set the next stack and ensure that we register this stack as an extruder.
@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)

View file

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

View file

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

View file

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

View file

@ -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 users system).

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
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()
if not settings.getProperty("machine_center_is_zero", "value"):
machine_width = settings.getProperty("machine_width", "value")
machine_depth = settings.getProperty("machine_depth", "value")
if not self._center_is_zero:
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
Logger.log("d", "GCode loading finished")

View file

@ -5,10 +5,20 @@ from UM.FileHandler.FileReader import FileReader
from UM.Mesh.MeshReader import MeshReader
from UM.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):

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// 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)
}
}

View file

@ -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")
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,52 +20,54 @@ Item
height: UM.Theme.getSize("default_lining").height
anchors.bottom: parent.bottom
}
Row
{
id: tileRow
height: parent.height
width: parent.width
spacing: UM.Theme.getSize("default_margin").width
topPadding: UM.Theme.getSize("default_margin").height
CheckBox
{
id: disableButton
checked: isEnabled
visible: model.type == "plugin"
width: visible ? UM.Theme.getSize("checkbox").width : 0
enabled: !toolbox.isDownloading
style: UM.Theme.styles.checkbox
onClicked: toolbox.isEnabled(model.id) ? toolbox.disable(model.id) : toolbox.enable(model.id)
}
Column
{
id: pluginInfo
property var color: isEnabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining")
height: parent.height
anchors
{
left: parent.left
top: parent.top
right: authorInfo.left
topMargin: UM.Theme.getSize("default_margin").height
rightMargin: UM.Theme.getSize("default_margin").width
}
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
{
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
Label
{
text: model.description
maximumLineCount: 3
elide: Text.ElideRight
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:
@ -89,126 +88,12 @@ Item
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")
}
}
Column
ToolboxInstalledTileActions
{
id: pluginActions
width: childrenRect.width
height: childrenRect.height
spacing: UM.Theme.getSize("default_margin").height
anchors
{
top: parent.top
right: parent.right
topMargin: UM.Theme.getSize("default_margin").height
}
Button {
id: removeButton
text:
{
if (model.is_bundled)
{
return isEnabled ? catalog.i18nc("@action:button", "Disable") : catalog.i18nc("@action:button", "Enable")
}
else
{
return catalog.i18nc("@action:button", "Uninstall")
}
}
enabled: !toolbox.isDownloading
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: "transparent"
border
{
width: UM.Theme.getSize("default_lining").width
color: UM.Theme.getColor("lining")
}
}
label: Text
{
text: control.text
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
onClicked:
{
if (model.is_bundled)
{
if (toolbox.isEnabled(model.id))
{
toolbox.disable(model.id)
}
else
{
toolbox.enable(model.id)
}
}
else
{
toolbox.uninstall(model.id)
}
}
}
Button
{
id: updateButton
text: catalog.i18nc("@action:button", "Update")
visible: canUpdate
style: ButtonStyle
{
background: Rectangle
{
implicitWidth: UM.Theme.getSize("toolbox_action_button").width
implicitHeight: UM.Theme.getSize("toolbox_action_button").height
color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary")
}
label: Label
{
text: control.text
color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font: UM.Theme.getFont("default_bold")
}
}
onClicked:
{
toolbox.update(model.id);
}
}
ProgressBar
{
id: progressbar
anchors
{
left: updateButton.left
right: updateButton.right
top: updateButton.bottom
topMargin: Math.floor(UM.Theme.getSize("default_margin") / 4)
}
value: toolbox.isDownloading ? toolbox.downloadProgress : 0
visible: toolbox.isDownloading
style: ProgressBarStyle
{
background: Rectangle
{
color: UM.Theme.getColor("lining")
implicitHeight: Math.floor(UM.Theme.getSize("toolbox_progress_bar").height)
}
progress: Rectangle
{
color: UM.Theme.getColor("primary")
}
}
}
}
Connections
{
@ -216,4 +101,5 @@ Item
onEnabledChanged: isEnabled = toolbox.isEnabled(model.id)
onMetadataChanged: canUpdate = toolbox.canUpdate(model.id)
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
@ -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,6 +351,7 @@ class Toolbox(QObject, Extension):
if reply.operation() == QNetworkAccessManager.GetOperation:
for type, url in self._request_urls.items():
if reply.url() == url:
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200:
try:
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
@ -391,6 +396,10 @@ class Toolbox(QObject, Extension):
except json.decoder.JSONDecodeError:
Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
break
else:
self.setViewPage("errored")
self.resetDownload()
return
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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"]
},

View file

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

View file

@ -3558,7 +3558,7 @@ msgstr "Druckeinrichtung deaktiviert\nG-Code-Dateien können nicht geändert wer
#: /home/ruben/Projects/Cura/resources/qml/Sidebar.qml:380
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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,11 +25,11 @@ jerk_enabled = True
jerk_print = 25
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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")
}
}
}
}

View file

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

View file

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