mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-02 16:54:23 +08:00
Merge remote-tracking branch 'upstream/master' into mb-improve-overhanging-walls
This commit is contained in:
commit
aa8535f374
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
@ -1,5 +1,6 @@
|
||||
parallel_nodes(['linux && cura', 'windows && cura']) {
|
||||
timeout(time: 2, unit: "HOURS") {
|
||||
|
||||
// Prepare building
|
||||
stage('Prepare') {
|
||||
// Ensure we start with a clean build directory.
|
||||
|
@ -1,10 +1,13 @@
|
||||
[Desktop Entry]
|
||||
Name=Ultimaker Cura
|
||||
Name[de]=Ultimaker Cura
|
||||
Name[nl]=Ultimaker Cura
|
||||
GenericName=3D Printing Software
|
||||
GenericName[de]=3D-Druck-Software
|
||||
GenericName[nl]=3D-printsoftware
|
||||
Comment=Cura converts 3D models into paths for a 3D printer. It prepares your print for maximum accuracy, minimum printing time and good reliability with many extra features that make your print come out great.
|
||||
Comment[de]=Cura wandelt 3D-Modelle in Pfade für einen 3D-Drucker um. Es bereitet Ihren Druck für maximale Genauigkeit, minimale Druckzeit und guter Zuverlässigkeit mit vielen zusätzlichen Funktionen vor, damit Ihr Druck großartig wird.
|
||||
Comment[nl]=Cura converteert 3D-modellen naar paden voor een 3D printer. Het bereidt je print voor om zeer precies, snel en betrouwbaar te kunnen printen, met veel extra functionaliteit om je print er goed uit te laten komen.
|
||||
Exec=@CMAKE_INSTALL_FULL_BINDIR@/cura %F
|
||||
TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
|
||||
Icon=cura-icon
|
||||
@ -12,4 +15,4 @@ Terminal=false
|
||||
Type=Application
|
||||
MimeType=application/sla;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml;
|
||||
Categories=Graphics;
|
||||
Keywords=3D;Printing;
|
||||
Keywords=3D;Printing;Slicer;
|
||||
|
@ -1,12 +1,13 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
import shutil
|
||||
|
||||
from typing import Optional
|
||||
from typing import Dict, Optional
|
||||
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
|
||||
|
||||
from UM import i18nCatalog
|
||||
@ -28,9 +29,9 @@ class Backup:
|
||||
# Re-use translation catalog.
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def __init__(self, zip_file: bytes = None, meta_data: dict = None) -> None:
|
||||
def __init__(self, zip_file: bytes = None, meta_data: Dict[str, str] = None) -> None:
|
||||
self.zip_file = zip_file # type: Optional[bytes]
|
||||
self.meta_data = meta_data # type: Optional[dict]
|
||||
self.meta_data = meta_data # type: Optional[Dict[str, str]]
|
||||
|
||||
## Create a back-up from the current user config folder.
|
||||
def makeFromCurrent(self) -> None:
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from UM.Logger import Logger
|
||||
from cura.Backups.Backup import Backup
|
||||
@ -18,7 +19,7 @@ class BackupsManager:
|
||||
## Get a back-up of the current configuration.
|
||||
# \return A tuple containing a ZipFile (the actual back-up) and a dict
|
||||
# containing some metadata (like version).
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]:
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, str]]]:
|
||||
self._disableAutoSave()
|
||||
backup = Backup()
|
||||
backup.makeFromCurrent()
|
||||
@ -30,7 +31,7 @@ class BackupsManager:
|
||||
# \param zip_file A bytes object containing the actual back-up.
|
||||
# \param meta_data A dict containing some metadata that is needed to
|
||||
# restore the back-up correctly.
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str]) -> None:
|
||||
if not meta_data.get("cura_release", None):
|
||||
# If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
|
||||
Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
|
||||
@ -43,13 +44,13 @@ class BackupsManager:
|
||||
if restored:
|
||||
# At this point, Cura will need to restart for the changes to take effect.
|
||||
# We don't want to store the data at this point as that would override the just-restored backup.
|
||||
self._application.windowClosed(save_data=False)
|
||||
self._application.windowClosed(save_data = False)
|
||||
|
||||
## Here we try to disable the auto-save plug-in as it might interfere with
|
||||
# restoring a back-up.
|
||||
def _disableAutoSave(self):
|
||||
def _disableAutoSave(self) -> None:
|
||||
self._application.setSaveDataEnabled(False)
|
||||
|
||||
## Re-enable auto-save after we're done.
|
||||
def _enableAutoSave(self):
|
||||
def _enableAutoSave(self) -> None:
|
||||
self._application.setSaveDataEnabled(True)
|
||||
|
@ -47,10 +47,10 @@ class BuildVolume(SceneNode):
|
||||
self._disallowed_area_color = None
|
||||
self._error_area_color = None
|
||||
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
self._depth = 0
|
||||
self._shape = ""
|
||||
self._width = 0 #type: float
|
||||
self._height = 0 #type: float
|
||||
self._depth = 0 #type: float
|
||||
self._shape = "" #type: str
|
||||
|
||||
self._shader = None
|
||||
|
||||
@ -154,19 +154,19 @@ class BuildVolume(SceneNode):
|
||||
if active_extruder_changed is not None:
|
||||
active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild)
|
||||
|
||||
def setWidth(self, width):
|
||||
def setWidth(self, width: float) -> None:
|
||||
if width is not None:
|
||||
self._width = width
|
||||
|
||||
def setHeight(self, height):
|
||||
def setHeight(self, height: float) -> None:
|
||||
if height is not None:
|
||||
self._height = height
|
||||
|
||||
def setDepth(self, depth):
|
||||
def setDepth(self, depth: float) -> None:
|
||||
if depth is not None:
|
||||
self._depth = depth
|
||||
|
||||
def setShape(self, shape: str):
|
||||
def setShape(self, shape: str) -> None:
|
||||
if shape:
|
||||
self._shape = shape
|
||||
|
||||
@ -294,7 +294,7 @@ class BuildVolume(SceneNode):
|
||||
if not self._width or not self._height or not self._depth:
|
||||
return
|
||||
|
||||
if not self._application._qml_engine:
|
||||
if not self._engine_ready:
|
||||
return
|
||||
|
||||
if not self._volume_outline_color:
|
||||
|
@ -5,6 +5,7 @@ import copy
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import cast, TYPE_CHECKING, Optional
|
||||
|
||||
import numpy
|
||||
|
||||
@ -13,8 +14,6 @@ from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Camera import Camera
|
||||
from UM.Math.Vector import Vector
|
||||
@ -84,7 +83,6 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
from plugins.SliceInfoPlugin.SliceInfo import SliceInfo
|
||||
|
||||
from .SingleInstance import SingleInstance
|
||||
from .AutoSave import AutoSave
|
||||
@ -98,6 +96,8 @@ from . import CuraSplashScreen
|
||||
from . import CameraImageProvider
|
||||
from . import MachineActionManager
|
||||
|
||||
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
|
||||
|
||||
from cura.Settings.MachineManager import MachineManager
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Settings.UserChangesModel import UserChangesModel
|
||||
@ -110,6 +110,10 @@ from cura.ObjectsModel import ObjectsModel
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from plugins.SliceInfoPlugin.SliceInfo import SliceInfo
|
||||
|
||||
|
||||
numpy.seterr(all = "ignore")
|
||||
|
||||
try:
|
||||
@ -155,6 +159,8 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self._boot_loading_time = time.time()
|
||||
|
||||
self._on_exit_callback_manager = OnExitCallbackManager(self)
|
||||
|
||||
# Variables set from CLI
|
||||
self._files_to_open = []
|
||||
self._use_single_instance = False
|
||||
@ -276,6 +282,8 @@ class CuraApplication(QtApplication):
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
||||
self._machine_action_manager.initialize()
|
||||
|
||||
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
|
||||
|
||||
def __sendCommandToSingleInstance(self):
|
||||
self._single_instance = SingleInstance(self, self._files_to_open)
|
||||
|
||||
@ -359,35 +367,35 @@ class CuraApplication(QtApplication):
|
||||
|
||||
empty_definition_changes_container = copy.deepcopy(empty_container)
|
||||
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
|
||||
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||
empty_definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
self._container_registry.addContainer(empty_definition_changes_container)
|
||||
self.empty_definition_changes_container = empty_definition_changes_container
|
||||
|
||||
empty_variant_container = copy.deepcopy(empty_container)
|
||||
empty_variant_container.setMetaDataEntry("id", "empty_variant")
|
||||
empty_variant_container.addMetaDataEntry("type", "variant")
|
||||
empty_variant_container.setMetaDataEntry("type", "variant")
|
||||
self._container_registry.addContainer(empty_variant_container)
|
||||
self.empty_variant_container = empty_variant_container
|
||||
|
||||
empty_material_container = copy.deepcopy(empty_container)
|
||||
empty_material_container.setMetaDataEntry("id", "empty_material")
|
||||
empty_material_container.addMetaDataEntry("type", "material")
|
||||
empty_material_container.setMetaDataEntry("type", "material")
|
||||
self._container_registry.addContainer(empty_material_container)
|
||||
self.empty_material_container = empty_material_container
|
||||
|
||||
empty_quality_container = copy.deepcopy(empty_container)
|
||||
empty_quality_container.setMetaDataEntry("id", "empty_quality")
|
||||
empty_quality_container.setName("Not Supported")
|
||||
empty_quality_container.addMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.addMetaDataEntry("type", "quality")
|
||||
empty_quality_container.addMetaDataEntry("supported", False)
|
||||
empty_quality_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_container.setMetaDataEntry("type", "quality")
|
||||
empty_quality_container.setMetaDataEntry("supported", False)
|
||||
self._container_registry.addContainer(empty_quality_container)
|
||||
self.empty_quality_container = empty_quality_container
|
||||
|
||||
empty_quality_changes_container = copy.deepcopy(empty_container)
|
||||
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
|
||||
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
|
||||
empty_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
empty_quality_changes_container.setMetaDataEntry("quality_type", "not_supported")
|
||||
self._container_registry.addContainer(empty_quality_changes_container)
|
||||
self.empty_quality_changes_container = empty_quality_changes_container
|
||||
|
||||
@ -407,43 +415,6 @@ class CuraApplication(QtApplication):
|
||||
}
|
||||
)
|
||||
|
||||
"""
|
||||
self._currently_loading_files = []
|
||||
self._non_sliceable_extensions = []
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
self._machine_manager = None # This is initialized on demand.
|
||||
self._extruder_manager = None
|
||||
self._material_manager = None
|
||||
self._quality_manager = None
|
||||
self._object_manager = None
|
||||
self._build_plate_model = None
|
||||
self._multi_build_plate_model = None
|
||||
self._setting_visibility_presets_model = None
|
||||
self._setting_inheritance_manager = None
|
||||
self._simple_mode_settings_manager = None
|
||||
self._cura_scene_controller = None
|
||||
self._machine_error_checker = None
|
||||
self._auto_save = None
|
||||
self._save_data_enabled = True
|
||||
|
||||
self._additional_components = {} # Components to add to certain areas in the interface
|
||||
|
||||
super().__init__(name = "cura",
|
||||
version = CuraVersion,
|
||||
buildtype = CuraBuildType,
|
||||
is_debug_mode = CuraDebugMode,
|
||||
tray_icon_name = "cura-icon-32.png",
|
||||
**kwargs)
|
||||
|
||||
# FOR TESTING ONLY
|
||||
if kwargs["parsed_command_line"].get("trigger_early_crash", False):
|
||||
assert not "This crash is triggered by the trigger_early_crash command line argument."
|
||||
|
||||
self._variant_manager = None
|
||||
|
||||
self.default_theme = "cura-light"
|
||||
"""
|
||||
# Runs preparations that needs to be done before the starting process.
|
||||
def startSplashWindowPhase(self):
|
||||
super().startSplashWindowPhase()
|
||||
@ -554,8 +525,8 @@ class CuraApplication(QtApplication):
|
||||
def setNeedToShowUserAgreement(self, set_value = True):
|
||||
self._need_to_show_user_agreement = set_value
|
||||
|
||||
## The "Quit" button click event handler.
|
||||
@pyqtSlot()
|
||||
# DO NOT call this function to close the application, use checkAndExitApplication() instead which will perform
|
||||
# pre-exit checks such as checking for in-progress USB printing, etc.
|
||||
def closeApplication(self):
|
||||
Logger.log("i", "Close application")
|
||||
main_window = self.getMainWindow()
|
||||
@ -564,6 +535,32 @@ class CuraApplication(QtApplication):
|
||||
else:
|
||||
self.exit(0)
|
||||
|
||||
# This function first performs all upon-exit checks such as USB printing that is in progress.
|
||||
# Use this to close the application.
|
||||
@pyqtSlot()
|
||||
def checkAndExitApplication(self) -> None:
|
||||
self._on_exit_callback_manager.resetCurrentState()
|
||||
self._on_exit_callback_manager.triggerNextCallback()
|
||||
|
||||
@pyqtSlot(result = bool)
|
||||
def getIsAllChecksPassed(self) -> bool:
|
||||
return self._on_exit_callback_manager.getIsAllChecksPassed()
|
||||
|
||||
def getOnExitCallbackManager(self) -> "OnExitCallbackManager":
|
||||
return self._on_exit_callback_manager
|
||||
|
||||
def triggerNextExitCheck(self) -> None:
|
||||
self._on_exit_callback_manager.triggerNextCallback()
|
||||
|
||||
showConfirmExitDialog = pyqtSignal(str, arguments = ["message"])
|
||||
|
||||
def setConfirmExitDialogCallback(self, callback):
|
||||
self._confirm_exit_dialog_callback = callback
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def callConfirmExitDialogCallback(self, yes_or_no: bool):
|
||||
self._confirm_exit_dialog_callback(yes_or_no)
|
||||
|
||||
## Signal to connect preferences action in QML
|
||||
showPreferencesWindow = pyqtSignal()
|
||||
|
||||
@ -1565,7 +1562,7 @@ class CuraApplication(QtApplication):
|
||||
self.callLater(self.openProjectFile.emit, file)
|
||||
return
|
||||
|
||||
if Preferences.getInstance().getValue("cura/select_models_on_load"):
|
||||
if self.getPreferences().getValue("cura/select_models_on_load"):
|
||||
Selection.clear()
|
||||
|
||||
f = file.toLocalFile()
|
||||
@ -1602,10 +1599,12 @@ class CuraApplication(QtApplication):
|
||||
|
||||
def _readMeshFinished(self, job):
|
||||
nodes = job.getResult()
|
||||
filename = job.getFileName()
|
||||
self._currently_loading_files.remove(filename)
|
||||
file_name = job.getFileName()
|
||||
file_name_lower = file_name.lower()
|
||||
file_extension = file_name_lower.split(".")[-1]
|
||||
self._currently_loading_files.remove(file_name)
|
||||
|
||||
self.fileLoaded.emit(filename)
|
||||
self.fileLoaded.emit(file_name)
|
||||
arrange_objects_on_load = not self.getPreferences().getValue("cura/use_multi_build_plate")
|
||||
target_build_plate = self.getMultiBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
|
||||
|
||||
@ -1622,7 +1621,7 @@ class CuraApplication(QtApplication):
|
||||
default_extruder_position = self.getMachineManager().defaultExtruderPosition
|
||||
default_extruder_id = self._global_container_stack.extruders[default_extruder_position].getId()
|
||||
|
||||
select_models_on_load = Preferences.getInstance().getValue("cura/select_models_on_load")
|
||||
select_models_on_load = self.getPreferences().getValue("cura/select_models_on_load")
|
||||
|
||||
for original_node in nodes:
|
||||
|
||||
@ -1638,15 +1637,11 @@ class CuraApplication(QtApplication):
|
||||
node.scale(original_node.getScale())
|
||||
|
||||
node.setSelectable(True)
|
||||
node.setName(os.path.basename(filename))
|
||||
node.setName(os.path.basename(file_name))
|
||||
self.getBuildVolume().checkBoundsAndUpdate(node)
|
||||
|
||||
is_non_sliceable = False
|
||||
filename_lower = filename.lower()
|
||||
for extension in self._non_sliceable_extensions:
|
||||
if filename_lower.endswith(extension):
|
||||
is_non_sliceable = True
|
||||
break
|
||||
is_non_sliceable = "." + file_extension in self._non_sliceable_extensions
|
||||
|
||||
if is_non_sliceable:
|
||||
self.callLater(lambda: self.getController().setActiveView("SimulationView"))
|
||||
|
||||
@ -1665,7 +1660,7 @@ class CuraApplication(QtApplication):
|
||||
if not child.getDecorator(ConvexHullDecorator):
|
||||
child.addDecorator(ConvexHullDecorator())
|
||||
|
||||
if arrange_objects_on_load:
|
||||
if file_extension != "3mf" and arrange_objects_on_load:
|
||||
if node.callDecoration("isSliceable"):
|
||||
# Only check position if it's not already blatantly obvious that it won't fit.
|
||||
if node.getBoundingBox() is None or self._volume.getBoundingBox() is None or node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
||||
@ -1699,7 +1694,7 @@ class CuraApplication(QtApplication):
|
||||
if select_models_on_load:
|
||||
Selection.add(node)
|
||||
|
||||
self.fileCompleted.emit(filename)
|
||||
self.fileCompleted.emit(file_name)
|
||||
|
||||
def addNonSliceableExtension(self, extension):
|
||||
self._non_sliceable_extensions.append(extension)
|
||||
|
@ -1,7 +1,11 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import List, Tuple
|
||||
|
||||
from cura.CuraApplication import CuraApplication #To find some resource types.
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
from UM.PackageManager import PackageManager #The class we're extending.
|
||||
from UM.Resources import Resources #To find storage paths for some resource types.
|
||||
|
||||
@ -15,3 +19,23 @@ class CuraPackageManager(PackageManager):
|
||||
self._installation_dirs_dict["qualities"] = Resources.getStoragePath(CuraApplication.ResourceTypes.QualityInstanceContainer)
|
||||
|
||||
super().initialize()
|
||||
|
||||
## Returns a list of where the package is used
|
||||
# empty if it is never used.
|
||||
# It loops through all the package contents and see if some of the ids are used.
|
||||
# The list consists of 3-tuples: (global_stack, extruder_nr, container_id)
|
||||
def getMachinesUsingPackage(self, package_id: str) -> Tuple[List[Tuple[GlobalStack, str, str]], List[Tuple[GlobalStack, str, str]]]:
|
||||
ids = self.getPackageContainerIds(package_id)
|
||||
container_stacks = self._application.getContainerRegistry().findContainerStacks()
|
||||
global_stacks = [container_stack for container_stack in container_stacks if isinstance(container_stack, GlobalStack)]
|
||||
machine_with_materials = []
|
||||
machine_with_qualities = []
|
||||
for container_id in ids:
|
||||
for global_stack in global_stacks:
|
||||
for extruder_nr, extruder_stack in global_stack.extruders.items():
|
||||
if container_id in (extruder_stack.material.getId(), extruder_stack.material.getMetaData().get("base_file")):
|
||||
machine_with_materials.append((global_stack, extruder_nr, container_id))
|
||||
if container_id == extruder_stack.quality.getId():
|
||||
machine_with_qualities.append((global_stack, extruder_nr, container_id))
|
||||
|
||||
return machine_with_materials, machine_with_qualities
|
||||
|
@ -1,8 +1,11 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import List
|
||||
from cura.Machines.MaterialNode import MaterialNode #For type checking.
|
||||
from typing import List, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.Machines.MaterialNode import MaterialNode
|
||||
|
||||
|
||||
## A MaterialGroup represents a group of material InstanceContainers that are derived from a single material profile.
|
||||
# The main InstanceContainer which has the ID of the material profile file name is called the "root_material". For
|
||||
@ -18,11 +21,11 @@ from cura.Machines.MaterialNode import MaterialNode #For type checking.
|
||||
class MaterialGroup:
|
||||
__slots__ = ("name", "is_read_only", "root_material_node", "derived_material_node_list")
|
||||
|
||||
def __init__(self, name: str, root_material_node: MaterialNode) -> None:
|
||||
def __init__(self, name: str, root_material_node: "MaterialNode") -> None:
|
||||
self.name = name
|
||||
self.is_read_only = False
|
||||
self.root_material_node = root_material_node # type: MaterialNode
|
||||
self.derived_material_node_list = [] #type: List[MaterialNode]
|
||||
self.derived_material_node_list = [] # type: List[MaterialNode]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "%s[%s]" % (self.__class__.__name__, self.name)
|
||||
|
@ -378,9 +378,8 @@ class MaterialManager(QObject):
|
||||
# Look at the guid to material dictionary
|
||||
root_material_id = None
|
||||
for material_group in self._guid_material_groups_map[material_guid]:
|
||||
if material_group.is_read_only:
|
||||
root_material_id = material_group.root_material_node.metadata["id"]
|
||||
break
|
||||
root_material_id = material_group.root_material_node.metadata["id"]
|
||||
break
|
||||
|
||||
if not root_material_id:
|
||||
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
||||
|
@ -109,6 +109,10 @@ class BrandMaterialsModel(ListModel):
|
||||
if brand.lower() == "generic":
|
||||
continue
|
||||
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(metadata.get("removed", False)):
|
||||
continue
|
||||
|
||||
if brand not in brand_group_dict:
|
||||
brand_group_dict[brand] = {}
|
||||
|
||||
|
@ -41,10 +41,15 @@ class GenericMaterialsModel(BaseMaterialsModel):
|
||||
item_list = []
|
||||
for root_material_id, container_node in available_material_dict.items():
|
||||
metadata = container_node.metadata
|
||||
|
||||
# Only add results for generic materials
|
||||
if metadata["brand"].lower() != "generic":
|
||||
continue
|
||||
|
||||
# Do not include the materials from a to-be-removed package
|
||||
if bool(metadata.get("removed", False)):
|
||||
continue
|
||||
|
||||
item = {"root_material_id": root_material_id,
|
||||
"id": metadata["id"],
|
||||
"name": metadata["name"],
|
||||
|
@ -340,6 +340,13 @@ class QualityManager(QObject):
|
||||
|
||||
return quality_group_dict
|
||||
|
||||
def getDefaultQualityType(self, machine: "GlobalStack") -> Optional[QualityGroup]:
|
||||
preferred_quality_type = machine.definition.getMetaDataEntry("preferred_quality_type")
|
||||
quality_group_dict = self.getQualityGroups(machine)
|
||||
quality_group = quality_group_dict.get(preferred_quality_type)
|
||||
return quality_group
|
||||
|
||||
|
||||
#
|
||||
# Methods for GUI
|
||||
#
|
||||
@ -459,18 +466,18 @@ class QualityManager(QObject):
|
||||
# Create a new quality_changes container for the quality.
|
||||
quality_changes = InstanceContainer(new_id)
|
||||
quality_changes.setName(new_name)
|
||||
quality_changes.addMetaDataEntry("type", "quality_changes")
|
||||
quality_changes.addMetaDataEntry("quality_type", quality_type)
|
||||
quality_changes.setMetaDataEntry("type", "quality_changes")
|
||||
quality_changes.setMetaDataEntry("quality_type", quality_type)
|
||||
|
||||
# If we are creating a container for an extruder, ensure we add that to the container
|
||||
if extruder_stack is not None:
|
||||
quality_changes.addMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
|
||||
quality_changes.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
|
||||
|
||||
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
|
||||
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
|
||||
quality_changes.setDefinition(machine_definition_id)
|
||||
|
||||
quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion)
|
||||
quality_changes.setMetaDataEntry("setting_version", self._application.SettingVersion)
|
||||
return quality_changes
|
||||
|
||||
|
||||
|
@ -369,8 +369,9 @@ class PrintInformation(QObject):
|
||||
def baseName(self):
|
||||
return self._base_name
|
||||
|
||||
## Created an acronymn-like abbreviated machine name from the currently active machine name
|
||||
# Called each time the global stack is switched
|
||||
## Created an acronym-like abbreviated machine name from the currently
|
||||
# active machine name.
|
||||
# Called each time the global stack is switched.
|
||||
def _setAbbreviatedMachineName(self):
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
if not global_container_stack:
|
||||
|
@ -5,7 +5,7 @@ import os
|
||||
import urllib.parse
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import Dict, Union
|
||||
from typing import Dict, Union, Optional
|
||||
|
||||
from PyQt5.QtCore import QObject, QUrl, QVariant
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
@ -47,13 +47,20 @@ class ContainerManager(QObject):
|
||||
self._container_name_filters = {} # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
@pyqtSlot(str, str, result=str)
|
||||
def getContainerMetaDataEntry(self, container_id, entry_name):
|
||||
def getContainerMetaDataEntry(self, container_id: str, entry_names: str) -> str:
|
||||
metadatas = self._container_registry.findContainersMetadata(id = container_id)
|
||||
if not metadatas:
|
||||
Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id)
|
||||
return ""
|
||||
|
||||
return str(metadatas[0].get(entry_name, ""))
|
||||
entries = entry_names.split("/")
|
||||
result = metadatas[0]
|
||||
while entries:
|
||||
entry = entries.pop(0)
|
||||
result = result.get(entry, {})
|
||||
if not result:
|
||||
return ""
|
||||
return str(result)
|
||||
|
||||
## Set a metadata entry of the specified container.
|
||||
#
|
||||
@ -68,6 +75,7 @@ class ContainerManager(QObject):
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
# TODO: This is ONLY used by MaterialView for material containers. Maybe refactor this.
|
||||
# Update: In order for QML to use objects and sub objects, those (sub) objects must all be QObject. Is that what we want?
|
||||
@pyqtSlot("QVariant", str, str)
|
||||
def setContainerMetaDataEntry(self, container_node, entry_name, entry_value):
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
@ -102,63 +110,6 @@ class ContainerManager(QObject):
|
||||
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
|
||||
container.metaDataChanged.emit(container)
|
||||
|
||||
## Set a setting property of the specified container.
|
||||
#
|
||||
# This will set the specified property of the specified setting of the container
|
||||
# and all containers that share the same base_file (if any). The latter only
|
||||
# happens for material containers.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to change.
|
||||
# \param setting_key \type{str} The key of the setting.
|
||||
# \param property_name \type{str} The name of the property, eg "value".
|
||||
# \param property_value \type{str} The new value of the property.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, str, result = bool)
|
||||
def setContainerProperty(self, container_id, setting_key, property_name, property_value):
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot set properties of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not set properties of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
basefile = container.getMetaDataEntry("base_file", container_id)
|
||||
for sibbling_container in self._container_registry.findInstanceContainers(base_file = basefile):
|
||||
if sibbling_container != container:
|
||||
sibbling_container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
return True
|
||||
|
||||
## Get a setting property of the specified container.
|
||||
#
|
||||
# This will get the specified property of the specified setting of the
|
||||
# specified container.
|
||||
#
|
||||
# \param container_id The ID of the container to get the setting property
|
||||
# of.
|
||||
# \param setting_key The key of the setting to get the property of.
|
||||
# \param property_name The property to obtain.
|
||||
# \return The value of the specified property. The type of this property
|
||||
# value depends on the type of the property. For instance, the "value"
|
||||
# property of an integer setting will be a Python int, but the "value"
|
||||
# property of an enum setting will be a Python str.
|
||||
@pyqtSlot(str, str, str, result = QVariant)
|
||||
def getContainerProperty(self, container_id: str, setting_key: str, property_name: str):
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not get properties of container %s because it was not found.", container_id)
|
||||
return ""
|
||||
container = containers[0]
|
||||
|
||||
return container.getProperty(setting_key, property_name)
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def makeUniqueName(self, original_name):
|
||||
return self._container_registry.uniqueName(original_name)
|
||||
|
@ -260,11 +260,11 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
|
||||
profile = InstanceContainer(profile_id)
|
||||
profile.setName(quality_name)
|
||||
profile.addMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
|
||||
profile.addMetaDataEntry("type", "quality_changes")
|
||||
profile.addMetaDataEntry("definition", expected_machine_definition)
|
||||
profile.addMetaDataEntry("quality_type", quality_type)
|
||||
profile.addMetaDataEntry("position", "0")
|
||||
profile.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
profile.setMetaDataEntry("definition", expected_machine_definition)
|
||||
profile.setMetaDataEntry("quality_type", quality_type)
|
||||
profile.setMetaDataEntry("position", "0")
|
||||
profile.setDirty(True)
|
||||
if idx == 0:
|
||||
# move all per-extruder settings to the first extruder's quality_changes
|
||||
@ -298,7 +298,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
extruder_id = machine_extruders[profile_index - 1].definition.getId()
|
||||
extruder_position = str(profile_index - 1)
|
||||
if not profile.getMetaDataEntry("position"):
|
||||
profile.addMetaDataEntry("position", extruder_position)
|
||||
profile.setMetaDataEntry("position", extruder_position)
|
||||
else:
|
||||
profile.setMetaDataEntry("position", extruder_position)
|
||||
profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")
|
||||
@ -349,7 +349,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
if "type" in profile.getMetaData():
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
else:
|
||||
profile.addMetaDataEntry("type", "quality_changes")
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
quality_type = profile.getMetaDataEntry("quality_type")
|
||||
if not quality_type:
|
||||
@ -480,16 +480,16 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
extruder_stack = ExtruderStack.ExtruderStack(unique_name)
|
||||
extruder_stack.setName(extruder_definition.getName())
|
||||
extruder_stack.setDefinition(extruder_definition)
|
||||
extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
extruder_stack.setMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
|
||||
# create a new definition_changes container for the extruder stack
|
||||
definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings") if create_new_ids else extruder_stack.getId() + "_settings"
|
||||
definition_changes_name = definition_changes_id
|
||||
definition_changes = InstanceContainer(definition_changes_id, parent = application)
|
||||
definition_changes.setName(definition_changes_name)
|
||||
definition_changes.addMetaDataEntry("setting_version", application.SettingVersion)
|
||||
definition_changes.addMetaDataEntry("type", "definition_changes")
|
||||
definition_changes.addMetaDataEntry("definition", extruder_definition.getId())
|
||||
definition_changes.setMetaDataEntry("setting_version", application.SettingVersion)
|
||||
definition_changes.setMetaDataEntry("type", "definition_changes")
|
||||
definition_changes.setMetaDataEntry("definition", extruder_definition.getId())
|
||||
|
||||
# move definition_changes settings if exist
|
||||
for setting_key in definition_changes.getAllKeys():
|
||||
@ -514,9 +514,9 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
user_container_name = user_container_id
|
||||
user_container = InstanceContainer(user_container_id, parent = application)
|
||||
user_container.setName(user_container_name)
|
||||
user_container.addMetaDataEntry("type", "user")
|
||||
user_container.addMetaDataEntry("machine", machine.getId())
|
||||
user_container.addMetaDataEntry("setting_version", application.SettingVersion)
|
||||
user_container.setMetaDataEntry("type", "user")
|
||||
user_container.setMetaDataEntry("machine", machine.getId())
|
||||
user_container.setMetaDataEntry("setting_version", application.SettingVersion)
|
||||
user_container.setDefinition(machine.definition.getId())
|
||||
user_container.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position"))
|
||||
|
||||
@ -580,7 +580,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
|
||||
if extruder_quality_changes_container:
|
||||
quality_changes_id = extruder_quality_changes_container.getId()
|
||||
extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
extruder_quality_changes_container.setMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0]
|
||||
else:
|
||||
# if we still cannot find a quality changes container for the extruder, create a new one
|
||||
@ -588,10 +588,10 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||
container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name)
|
||||
extruder_quality_changes_container = InstanceContainer(container_id, parent = application)
|
||||
extruder_quality_changes_container.setName(container_name)
|
||||
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||
extruder_quality_changes_container.addMetaDataEntry("setting_version", application.SettingVersion)
|
||||
extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
|
||||
extruder_quality_changes_container.setMetaDataEntry("type", "quality_changes")
|
||||
extruder_quality_changes_container.setMetaDataEntry("setting_version", application.SettingVersion)
|
||||
extruder_quality_changes_container.setMetaDataEntry("position", extruder_definition.getMetaDataEntry("position"))
|
||||
extruder_quality_changes_container.setMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
|
||||
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
|
||||
|
||||
self.addContainer(extruder_quality_changes_container)
|
||||
|
@ -57,7 +57,7 @@ class CuraContainerStack(ContainerStack):
|
||||
self.containersChanged.connect(self._onContainersChanged)
|
||||
|
||||
import cura.CuraApplication #Here to prevent circular imports.
|
||||
self.addMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
|
||||
self.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
|
||||
|
||||
# This is emitted whenever the containersChanged signal from the ContainerStack base class is emitted.
|
||||
pyqtContainersChanged = pyqtSignal()
|
||||
|
@ -146,7 +146,7 @@ class CuraStackBuilder:
|
||||
stack.setName(extruder_definition.getName())
|
||||
stack.setDefinition(extruder_definition)
|
||||
|
||||
stack.addMetaDataEntry("position", position)
|
||||
stack.setMetaDataEntry("position", position)
|
||||
|
||||
user_container = cls.createUserChangesContainer(new_stack_id + "_user", machine_definition_id, new_stack_id,
|
||||
is_global_stack = False)
|
||||
@ -208,11 +208,11 @@ class CuraStackBuilder:
|
||||
|
||||
container = InstanceContainer(unique_container_name)
|
||||
container.setDefinition(definition_id)
|
||||
container.addMetaDataEntry("type", "user")
|
||||
container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
container.setMetaDataEntry("type", "user")
|
||||
container.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
|
||||
metadata_key_to_add = "machine" if is_global_stack else "extruder"
|
||||
container.addMetaDataEntry(metadata_key_to_add, stack_id)
|
||||
container.setMetaDataEntry(metadata_key_to_add, stack_id)
|
||||
|
||||
return container
|
||||
|
||||
@ -226,8 +226,8 @@ class CuraStackBuilder:
|
||||
|
||||
definition_changes_container = InstanceContainer(unique_container_name)
|
||||
definition_changes_container.setDefinition(container_stack.getBottom().getId())
|
||||
definition_changes_container.addMetaDataEntry("type", "definition_changes")
|
||||
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
definition_changes_container.setMetaDataEntry("type", "definition_changes")
|
||||
definition_changes_container.setMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||
|
||||
registry.addContainer(definition_changes_container)
|
||||
container_stack.definitionChanges = definition_changes_container
|
||||
|
@ -29,7 +29,7 @@ class ExtruderStack(CuraContainerStack):
|
||||
def __init__(self, container_id: str) -> None:
|
||||
super().__init__(container_id)
|
||||
|
||||
self.addMetaDataEntry("type", "extruder_train") # For backward compatibility
|
||||
self.setMetaDataEntry("type", "extruder_train") # For backward compatibility
|
||||
|
||||
self.propertiesChanged.connect(self._onPropertiesChanged)
|
||||
|
||||
@ -42,7 +42,7 @@ class ExtruderStack(CuraContainerStack):
|
||||
def setNextStack(self, stack: CuraContainerStack, connect_signals: bool = True) -> None:
|
||||
super().setNextStack(stack)
|
||||
stack.addExtruder(self)
|
||||
self.addMetaDataEntry("machine", stack.id)
|
||||
self.setMetaDataEntry("machine", stack.id)
|
||||
|
||||
# For backward compatibility: Register the extruder with the Extruder Manager
|
||||
ExtruderManager.getInstance().registerExtruder(self, stack.id)
|
||||
@ -53,7 +53,7 @@ class ExtruderStack(CuraContainerStack):
|
||||
|
||||
def setEnabled(self, enabled: bool) -> None:
|
||||
if "enabled" not in self._metadata:
|
||||
self.addMetaDataEntry("enabled", "True")
|
||||
self.setMetaDataEntry("enabled", "True")
|
||||
self.setMetaDataEntry("enabled", str(enabled))
|
||||
self.enabledChanged.emit()
|
||||
|
||||
@ -138,7 +138,7 @@ class ExtruderStack(CuraContainerStack):
|
||||
def deserialize(self, contents: str, file_name: Optional[str] = None) -> None:
|
||||
super().deserialize(contents, file_name)
|
||||
if "enabled" not in self.getMetaData():
|
||||
self.addMetaDataEntry("enabled", "True")
|
||||
self.setMetaDataEntry("enabled", "True")
|
||||
stacks = ContainerRegistry.getInstance().findContainerStacks(id=self.getMetaDataEntry("machine", ""))
|
||||
if stacks:
|
||||
self.setNextStack(stacks[0])
|
||||
|
@ -27,7 +27,7 @@ class GlobalStack(CuraContainerStack):
|
||||
def __init__(self, container_id: str) -> None:
|
||||
super().__init__(container_id)
|
||||
|
||||
self.addMetaDataEntry("type", "machine") # For backward compatibility
|
||||
self.setMetaDataEntry("type", "machine") # For backward compatibility
|
||||
|
||||
self._extruders = {} # type: Dict[str, "ExtruderStack"]
|
||||
|
||||
|
@ -985,6 +985,11 @@ class MachineManager(QObject):
|
||||
self.updateDefaultExtruder()
|
||||
self.updateNumberExtrudersEnabled()
|
||||
self.correctExtruderSettings()
|
||||
|
||||
# In case this extruder is being disabled and it's the currently selected one, switch to the default extruder
|
||||
if not enabled and position == ExtruderManager.getInstance().activeExtruderIndex:
|
||||
ExtruderManager.getInstance().setActiveExtruderIndex(int(self._default_extruder_position))
|
||||
|
||||
# ensure that the quality profile is compatible with current combination, or choose a compatible one if available
|
||||
self._updateQualityWithMaterial()
|
||||
self.extruderChanged.emit()
|
||||
@ -1299,9 +1304,9 @@ class MachineManager(QObject):
|
||||
new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id)
|
||||
if not new_machine:
|
||||
return
|
||||
new_machine.addMetaDataEntry("um_network_key", self.activeMachineNetworkKey)
|
||||
new_machine.addMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName)
|
||||
new_machine.addMetaDataEntry("hidden", False)
|
||||
new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey)
|
||||
new_machine.setMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName)
|
||||
new_machine.setMetaDataEntry("hidden", False)
|
||||
else:
|
||||
Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey)
|
||||
new_machine.setMetaDataEntry("hidden", False)
|
||||
@ -1392,8 +1397,13 @@ class MachineManager(QObject):
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, variant_name, material_diameter, root_material_id)
|
||||
self.setMaterial(position, material_node)
|
||||
|
||||
## global_stack: if you want to provide your own global_stack instead of the current active one
|
||||
# if you update an active machine, special measures have to be taken.
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setMaterial(self, position: str, container_node) -> None:
|
||||
def setMaterial(self, position: str, container_node, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
if global_stack is not None and global_stack != self._global_container_stack:
|
||||
global_stack.extruders[position].material = container_node.getContainer()
|
||||
return
|
||||
position = str(position)
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
@ -1434,8 +1444,22 @@ class MachineManager(QObject):
|
||||
quality_group = quality_group_dict[quality_type]
|
||||
self.setQualityGroup(quality_group)
|
||||
|
||||
## Optionally provide global_stack if you want to use your own
|
||||
# The active global_stack is treated differently.
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False) -> None:
|
||||
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
if global_stack is not None and global_stack != self._global_container_stack:
|
||||
if quality_group is None:
|
||||
Logger.log("e", "Could not set quality group because quality group is None")
|
||||
return
|
||||
if quality_group.node_for_global is None:
|
||||
Logger.log("e", "Could not set quality group [%s] because it has no node_for_global", str(quality_group))
|
||||
return
|
||||
global_stack.quality = quality_group.node_for_global.getContainer()
|
||||
for extruder_nr, extruder_stack in global_stack.extruders.items():
|
||||
extruder_stack.quality = quality_group.nodes_for_extruders[extruder_nr].getContainer()
|
||||
return
|
||||
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityGroup(quality_group)
|
||||
|
@ -37,7 +37,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
|
||||
self._stack = PerObjectContainerStack(container_id = "per_object_stack_" + str(id(self)))
|
||||
self._stack.setDirty(False) # This stack does not need to be saved.
|
||||
user_container = InstanceContainer(container_id = self._generateUniqueName())
|
||||
user_container.addMetaDataEntry("type", "user")
|
||||
user_container.setMetaDataEntry("type", "user")
|
||||
self._stack.userChanges = user_container
|
||||
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
|
||||
|
||||
|
69
cura/TaskManagement/OnExitCallbackManager.py
Normal file
69
cura/TaskManagement/OnExitCallbackManager.py
Normal file
@ -0,0 +1,69 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, List
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
|
||||
#
|
||||
# This class manages a all registered upon-exit checks that need to be perform when the application tries to exit.
|
||||
# For example, to show a confirmation dialog when there is USB printing in progress, etc. All callbacks will be called
|
||||
# in the order of when they got registered. If all callbacks "passes", that is, for example, if the user clicks "yes"
|
||||
# on the exit confirmation dialog or nothing that's blocking the exit, then the application will quit after that.
|
||||
#
|
||||
class OnExitCallbackManager:
|
||||
|
||||
def __init__(self, application: "CuraApplication") -> None:
|
||||
self._application = application
|
||||
self._on_exit_callback_list = list() # type: List[Callable]
|
||||
self._current_callback_idx = 0
|
||||
self._is_all_checks_passed = False
|
||||
|
||||
def addCallback(self, callback: Callable) -> None:
|
||||
self._on_exit_callback_list.append(callback)
|
||||
Logger.log("d", "on-app-exit callback [%s] added.", callback)
|
||||
|
||||
# Reset the current state so the next time it will call all the callbacks again.
|
||||
def resetCurrentState(self) -> None:
|
||||
self._current_callback_idx = 0
|
||||
self._is_all_checks_passed = False
|
||||
|
||||
def getIsAllChecksPassed(self) -> bool:
|
||||
return self._is_all_checks_passed
|
||||
|
||||
# Trigger the next callback if available. If not, it means that all callbacks have "passed", which means we should
|
||||
# not block the application to quit, and it will call the application to actually quit.
|
||||
def triggerNextCallback(self) -> None:
|
||||
# Get the next callback and schedule that if
|
||||
this_callback = None
|
||||
if self._current_callback_idx < len(self._on_exit_callback_list):
|
||||
this_callback = self._on_exit_callback_list[self._current_callback_idx]
|
||||
self._current_callback_idx += 1
|
||||
|
||||
if this_callback is not None:
|
||||
Logger.log("d", "Scheduled the next on-app-exit callback [%s]", this_callback)
|
||||
self._application.callLater(this_callback)
|
||||
else:
|
||||
Logger.log("d", "No more on-app-exit callbacks to process. Tell the app to exit.")
|
||||
|
||||
self._is_all_checks_passed = True
|
||||
|
||||
# Tell the application to exit
|
||||
self._application.callLater(self._application.closeApplication)
|
||||
|
||||
# This is the callback function which an on-exit callback should call when it finishes, it should provide the
|
||||
# "should_proceed" flag indicating whether this check has "passed", or in other words, whether quiting the
|
||||
# application should be blocked. If the last on-exit callback doesn't block the quiting, it will call the next
|
||||
# registered on-exit callback if available.
|
||||
def onCurrentCallbackFinished(self, should_proceed: bool = True) -> None:
|
||||
if not should_proceed:
|
||||
Logger.log("d", "on-app-exit callback finished and we should not proceed.")
|
||||
# Reset the state
|
||||
self.resetCurrentState()
|
||||
return
|
||||
|
||||
self.triggerNextCallback()
|
0
cura/TaskManagement/__init__.py
Normal file
0
cura/TaskManagement/__init__.py
Normal file
11
cura_app.py
11
cura_app.py
@ -12,14 +12,14 @@ from UM.Platform import Platform
|
||||
|
||||
parser = argparse.ArgumentParser(prog = "cura",
|
||||
add_help = False)
|
||||
parser.add_argument('--debug',
|
||||
action='store_true',
|
||||
parser.add_argument("--debug",
|
||||
action="store_true",
|
||||
default = False,
|
||||
help = "Turn on the debug mode by setting this option."
|
||||
)
|
||||
parser.add_argument('--trigger-early-crash',
|
||||
dest = 'trigger_early_crash',
|
||||
action = 'store_true',
|
||||
parser.add_argument("--trigger-early-crash",
|
||||
dest = "trigger_early_crash",
|
||||
action = "store_true",
|
||||
default = False,
|
||||
help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog."
|
||||
)
|
||||
@ -131,6 +131,7 @@ faulthandler.enable(all_threads = True)
|
||||
# first seems to prevent Sip from going into a state where it
|
||||
# tries to create PyQt objects on a non-main thread.
|
||||
import Arcus #@UnusedImport
|
||||
import Savitar #@UnusedImport
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
app = CuraApplication()
|
||||
|
@ -59,7 +59,7 @@ class ThreeMFReader(MeshReader):
|
||||
if transformation == "":
|
||||
return Matrix()
|
||||
|
||||
splitted_transformation = transformation.split()
|
||||
split_transformation = transformation.split()
|
||||
## Transformation is saved as:
|
||||
## M00 M01 M02 0.0
|
||||
## M10 M11 M12 0.0
|
||||
@ -68,20 +68,20 @@ class ThreeMFReader(MeshReader):
|
||||
## We switch the row & cols as that is how everyone else uses matrices!
|
||||
temp_mat = Matrix()
|
||||
# Rotation & Scale
|
||||
temp_mat._data[0, 0] = splitted_transformation[0]
|
||||
temp_mat._data[1, 0] = splitted_transformation[1]
|
||||
temp_mat._data[2, 0] = splitted_transformation[2]
|
||||
temp_mat._data[0, 1] = splitted_transformation[3]
|
||||
temp_mat._data[1, 1] = splitted_transformation[4]
|
||||
temp_mat._data[2, 1] = splitted_transformation[5]
|
||||
temp_mat._data[0, 2] = splitted_transformation[6]
|
||||
temp_mat._data[1, 2] = splitted_transformation[7]
|
||||
temp_mat._data[2, 2] = splitted_transformation[8]
|
||||
temp_mat._data[0, 0] = split_transformation[0]
|
||||
temp_mat._data[1, 0] = split_transformation[1]
|
||||
temp_mat._data[2, 0] = split_transformation[2]
|
||||
temp_mat._data[0, 1] = split_transformation[3]
|
||||
temp_mat._data[1, 1] = split_transformation[4]
|
||||
temp_mat._data[2, 1] = split_transformation[5]
|
||||
temp_mat._data[0, 2] = split_transformation[6]
|
||||
temp_mat._data[1, 2] = split_transformation[7]
|
||||
temp_mat._data[2, 2] = split_transformation[8]
|
||||
|
||||
# Translation
|
||||
temp_mat._data[0, 3] = splitted_transformation[9]
|
||||
temp_mat._data[1, 3] = splitted_transformation[10]
|
||||
temp_mat._data[2, 3] = splitted_transformation[11]
|
||||
temp_mat._data[0, 3] = split_transformation[9]
|
||||
temp_mat._data[1, 3] = split_transformation[10]
|
||||
temp_mat._data[2, 3] = split_transformation[11]
|
||||
|
||||
return temp_mat
|
||||
|
||||
@ -169,8 +169,6 @@ class ThreeMFReader(MeshReader):
|
||||
archive = zipfile.ZipFile(file_name, "r")
|
||||
self._base_name = os.path.basename(file_name)
|
||||
parser = Savitar.ThreeMFParser()
|
||||
with open("/tmp/test.xml", "wb") as f:
|
||||
f.write(archive.open("3D/3dmodel.model").read())
|
||||
scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
|
||||
self._unit = scene_3mf.getUnit()
|
||||
for node in scene_3mf.getSceneNodes():
|
||||
|
@ -963,7 +963,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
if not extruder_info:
|
||||
continue
|
||||
if "enabled" not in extruder_stack.getMetaData():
|
||||
extruder_stack.addMetaDataEntry("enabled", "True")
|
||||
extruder_stack.setMetaDataEntry("enabled", "True")
|
||||
extruder_stack.setMetaDataEntry("enabled", str(extruder_info.enabled))
|
||||
|
||||
def _updateActiveMachine(self, global_stack):
|
||||
|
@ -1,4 +1,9 @@
|
||||
|
||||
[3.4.1]
|
||||
*Bug fixes
|
||||
- Fixed an issue that would occasionally cause an unnecessary extra skin wall to be printed, which increased print time.
|
||||
- Fixed an issue in which supports were not generated on the initial layer, because the engine expected a brim to be in place.
|
||||
- Conical and tree supports are now limited within the build plate volume.
|
||||
- Fixed various startup crashes, including: copying of the version folder, errors while deleting packages, storing the old files, and losing data on install.
|
||||
|
||||
[3.4.0]
|
||||
|
||||
|
@ -21,6 +21,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.SettingInstance import SettingInstance #For typing.
|
||||
from UM.Tool import Tool #For typing.
|
||||
from UM.Mesh.MeshData import MeshData #For typing.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
@ -62,15 +63,15 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if Platform.isLinux() and not default_engine_location:
|
||||
if not os.getenv("PATH"):
|
||||
raise OSError("There is something wrong with your Linux installation.")
|
||||
for pathdir in os.getenv("PATH").split(os.pathsep):
|
||||
for pathdir in cast(str, os.getenv("PATH")).split(os.pathsep):
|
||||
execpath = os.path.join(pathdir, executable_name)
|
||||
if os.path.exists(execpath):
|
||||
default_engine_location = execpath
|
||||
break
|
||||
|
||||
self._application = CuraApplication.getInstance() #type: CuraApplication
|
||||
self._multi_build_plate_model = None #type: MultiBuildPlateModel
|
||||
self._machine_error_checker = None #type: MachineErrorChecker
|
||||
self._multi_build_plate_model = None #type: Optional[MultiBuildPlateModel]
|
||||
self._machine_error_checker = None #type: Optional[MachineErrorChecker]
|
||||
|
||||
if not default_engine_location:
|
||||
raise EnvironmentError("Could not find CuraEngine")
|
||||
@ -120,7 +121,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._engine_is_fresh = True #type: bool # Is the newly started engine used before or not?
|
||||
|
||||
self._backend_log_max_lines = 20000 #type: int # Maximum number of lines to buffer
|
||||
self._error_message = None #type: Message # Pop-up message that shows errors.
|
||||
self._error_message = None #type: Optional[Message] # Pop-up message that shows errors.
|
||||
self._last_num_objects = defaultdict(int) #type: Dict[int, int] # Count number of objects to see if there is something changed
|
||||
self._postponed_scene_change_sources = [] #type: List[SceneNode] # scene change is postponed (by a tool)
|
||||
|
||||
@ -145,7 +146,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
||||
|
||||
self._application.getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
if self._multi_build_plate_model:
|
||||
self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||
|
||||
self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
@ -246,7 +249,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if self._application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate:
|
||||
self._application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced)
|
||||
|
||||
if self._process is None:
|
||||
if self._process is None: # type: ignore
|
||||
self._createSocket()
|
||||
self.stopSlicing()
|
||||
self._engine_is_fresh = False # Yes we're going to use the engine
|
||||
@ -284,12 +287,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if self._application.getUseExternalBackend():
|
||||
return
|
||||
|
||||
if self._process is not None:
|
||||
if self._process is not None: # type: ignore
|
||||
Logger.log("d", "Killing engine process")
|
||||
try:
|
||||
self._process.terminate()
|
||||
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait())
|
||||
self._process = None
|
||||
self._process.terminate() # type: ignore
|
||||
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait()) # type: ignore
|
||||
self._process = None # type: ignore
|
||||
|
||||
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
|
||||
@ -328,6 +331,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
|
||||
if job.getResult() == StartJobResult.SettingError:
|
||||
if self._application.platformActivity:
|
||||
if not self._global_container_stack:
|
||||
Logger.log("w", "Global container stack not assigned to CuraEngineBackend!")
|
||||
return
|
||||
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
|
||||
error_keys = [] #type: List[str]
|
||||
for extruder in extruders:
|
||||
@ -361,6 +367,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if not stack:
|
||||
continue
|
||||
for key in stack.getErrorKeys():
|
||||
if not self._global_container_stack:
|
||||
Logger.log("e", "CuraEngineBackend does not have global_container_stack assigned.")
|
||||
continue
|
||||
definition = cast(DefinitionContainerInterface, self._global_container_stack.getBottom()).findDefinitions(key = key)
|
||||
if not definition:
|
||||
Logger.log("e", "When checking settings for errors, unable to find definition for key {key} in per-object stack.".format(key = key))
|
||||
@ -409,7 +418,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||
# Notify the user that it's now up to the backend to do it's job
|
||||
self.backendStateChange.emit(BackendState.Processing)
|
||||
|
||||
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
|
||||
if self._slice_start_time:
|
||||
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
|
||||
|
||||
## Determine enable or disable auto slicing. Return True for enable timer and False otherwise.
|
||||
# It disables when
|
||||
@ -447,7 +457,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||
# Only count sliceable objects
|
||||
if node.callDecoration("isSliceable"):
|
||||
build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||
num_objects[build_plate_number] += 1
|
||||
if build_plate_number is not None:
|
||||
num_objects[build_plate_number] += 1
|
||||
return num_objects
|
||||
|
||||
## Listener for when the scene has changed.
|
||||
@ -476,15 +487,14 @@ class CuraEngineBackend(QObject, Backend):
|
||||
else:
|
||||
# we got a single scenenode
|
||||
if not source.callDecoration("isGroup"):
|
||||
if source.getMeshData() is None:
|
||||
return
|
||||
if source.getMeshData().getVertices() is None:
|
||||
mesh_data = source.getMeshData()
|
||||
if mesh_data is None or mesh_data.getVertices() is None:
|
||||
return
|
||||
|
||||
build_plate_changed.add(source_build_plate_number)
|
||||
# There are some SceneNodes that do not have any build plate associated, then do not add to the list.
|
||||
if source_build_plate_number is not None:
|
||||
build_plate_changed.add(source_build_plate_number)
|
||||
|
||||
build_plate_changed.discard(None)
|
||||
build_plate_changed.discard(-1) # object not on build plate
|
||||
if not build_plate_changed:
|
||||
return
|
||||
|
||||
@ -524,6 +534,11 @@ class CuraEngineBackend(QObject, Backend):
|
||||
if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]:
|
||||
Logger.log("w", "A socket error caused the connection to be reset")
|
||||
|
||||
# _terminate()' function sets the job status to 'cancel', after reconnecting to another Port the job status
|
||||
# needs to be updated. Otherwise backendState is "Unable To Slice"
|
||||
if error.getErrorCode() == Arcus.ErrorCode.BindFailedError and self._start_slice_job is not None:
|
||||
self._start_slice_job.setIsCancelled(False)
|
||||
|
||||
## Remove old layer data (if any)
|
||||
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
|
||||
for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
|
||||
@ -577,9 +592,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||
#
|
||||
# \param message The protobuf message containing sliced layer data.
|
||||
def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
||||
if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
|
||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
|
||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
|
||||
if self._start_slice_job_build_plate is not None:
|
||||
if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
|
||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
|
||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
|
||||
|
||||
## Called when a progress message is received from the engine.
|
||||
#
|
||||
@ -619,7 +635,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||
gcode_list[index] = replaced
|
||||
|
||||
self._slicing = False
|
||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
||||
if self._slice_start_time:
|
||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
||||
Logger.log("d", "Number of models per buildplate: %s", dict(self._numObjectsPerBuildPlate()))
|
||||
|
||||
# See if we need to process the sliced layers job.
|
||||
@ -658,7 +675,11 @@ class CuraEngineBackend(QObject, Backend):
|
||||
## Creates a new socket connection.
|
||||
def _createSocket(self, protocol_file: str = None) -> None:
|
||||
if not protocol_file:
|
||||
protocol_file = os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto"))
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if not plugin_path:
|
||||
Logger.log("e", "Could not get plugin path!", self.getPluginId())
|
||||
return
|
||||
protocol_file = os.path.abspath(os.path.join(plugin_path, "Cura.proto"))
|
||||
super()._createSocket(protocol_file)
|
||||
self._engine_is_fresh = True
|
||||
|
||||
@ -773,9 +794,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
# We should reset our state and start listening for new connections.
|
||||
def _onBackendQuit(self) -> None:
|
||||
if not self._restart:
|
||||
if self._process:
|
||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
|
||||
self._process = None
|
||||
if self._process: # type: ignore
|
||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) # type: ignore
|
||||
self._process = None # type: ignore
|
||||
|
||||
## Called when the global container stack changes
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
@ -831,6 +852,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._change_timer.start()
|
||||
|
||||
def _extruderChanged(self) -> None:
|
||||
if not self._multi_build_plate_model:
|
||||
Logger.log("w", "CuraEngineBackend does not have multi_build_plate_model assigned!")
|
||||
return
|
||||
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
||||
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||
|
@ -5,7 +5,7 @@ import numpy
|
||||
from string import Formatter
|
||||
from enum import IntEnum
|
||||
import time
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from typing import Any, cast, Dict, List, Optional, Set
|
||||
import re
|
||||
import Arcus #For typing.
|
||||
|
||||
@ -41,39 +41,32 @@ class StartJobResult(IntEnum):
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcode
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key: str, *args: str, **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
|
||||
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||
|
||||
if isinstance(key, str):
|
||||
extruder_nr = int(default_extruder_nr)
|
||||
|
||||
key_fragments = [fragment.strip() for fragment in key.split(",")]
|
||||
if len(key_fragments) == 2:
|
||||
try:
|
||||
extruder_nr = int(kwargs["default_extruder_nr"])
|
||||
extruder_nr = int(key_fragments[1])
|
||||
except ValueError:
|
||||
extruder_nr = -1
|
||||
|
||||
key_fragments = [fragment.strip() for fragment in key.split(",")]
|
||||
if len(key_fragments) == 2:
|
||||
try:
|
||||
extruder_nr = int(key_fragments[1])
|
||||
except ValueError:
|
||||
try:
|
||||
extruder_nr = int(kwargs["-1"][key_fragments[1]]) # get extruder_nr values from the global stack
|
||||
except (KeyError, ValueError):
|
||||
# either the key does not exist, or the value is not an int
|
||||
Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end g-code, using global stack", key_fragments[1], key_fragments[0])
|
||||
elif len(key_fragments) != 1:
|
||||
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end g-code", key)
|
||||
return "{" + str(key) + "}"
|
||||
|
||||
key = key_fragments[0]
|
||||
try:
|
||||
return kwargs[str(extruder_nr)][key]
|
||||
except KeyError:
|
||||
Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key)
|
||||
return "{" + key + "}"
|
||||
else:
|
||||
extruder_nr = int(kwargs["-1"][key_fragments[1]]) # get extruder_nr values from the global stack #TODO: How can you ever provide the '-1' kwarg?
|
||||
except (KeyError, ValueError):
|
||||
# either the key does not exist, or the value is not an int
|
||||
Logger.log("w", "Unable to determine stack nr '%s' for key '%s' in start/end g-code, using global stack", key_fragments[1], key_fragments[0])
|
||||
elif len(key_fragments) != 1:
|
||||
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end g-code", key)
|
||||
return "{" + str(key) + "}"
|
||||
return "{" + key + "}"
|
||||
|
||||
key = key_fragments[0]
|
||||
try:
|
||||
return kwargs[str(extruder_nr)][key]
|
||||
except KeyError:
|
||||
Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key)
|
||||
return "{" + key + "}"
|
||||
|
||||
|
||||
## Job class that builds up the message of scene data to send to CuraEngine.
|
||||
@ -216,12 +209,15 @@ class StartSliceJob(Job):
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
|
||||
extruders_enabled = {position: stack.isEnabled for position, stack in CuraApplication.getInstance().getGlobalContainerStack().extruders.items()}
|
||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not global_stack:
|
||||
return
|
||||
extruders_enabled = {position: stack.isEnabled for position, stack in global_stack.extruders.items()}
|
||||
filtered_object_groups = []
|
||||
has_model_with_disabled_extruders = False
|
||||
associated_disabled_extruders = set()
|
||||
for group in object_groups:
|
||||
stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
stack = global_stack
|
||||
skip_group = False
|
||||
for node in group:
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
@ -234,7 +230,7 @@ class StartSliceJob(Job):
|
||||
|
||||
if has_model_with_disabled_extruders:
|
||||
self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
|
||||
associated_disabled_extruders = [str(c) for c in sorted([int(p) + 1 for p in associated_disabled_extruders])]
|
||||
associated_disabled_extruders = {str(c) for c in sorted([int(p) + 1 for p in associated_disabled_extruders])}
|
||||
self.setMessage(", ".join(associated_disabled_extruders))
|
||||
return
|
||||
|
||||
@ -294,6 +290,9 @@ class StartSliceJob(Job):
|
||||
def isCancelled(self) -> bool:
|
||||
return self._is_cancelled
|
||||
|
||||
def setIsCancelled(self, value: bool):
|
||||
self._is_cancelled = value
|
||||
|
||||
## Creates a dictionary of tokens to replace in g-code pieces.
|
||||
#
|
||||
# This indicates what should be replaced in the start and end g-codes.
|
||||
@ -325,7 +324,7 @@ class StartSliceJob(Job):
|
||||
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
||||
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
||||
if not self._all_extruders_settings:
|
||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
global_stack = cast(ContainerStack, CuraApplication.getInstance().getGlobalContainerStack())
|
||||
|
||||
# NB: keys must be strings for the string formatter
|
||||
self._all_extruders_settings = {
|
||||
|
@ -75,7 +75,7 @@ class CuraProfileReader(ProfileReader):
|
||||
def _loadProfile(self, serialized, profile_id):
|
||||
# Create an empty profile.
|
||||
profile = InstanceContainer(profile_id)
|
||||
profile.addMetaDataEntry("type", "quality_changes")
|
||||
profile.setMetaDataEntry("type", "quality_changes")
|
||||
try:
|
||||
profile.deserialize(serialized)
|
||||
except ContainerFormatError as e:
|
||||
|
@ -286,6 +286,10 @@ class FlavorParser:
|
||||
self._cancelled = False
|
||||
# We obtain the filament diameter from the selected extruder to calculate line widths
|
||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
|
||||
if not global_stack:
|
||||
return None
|
||||
|
||||
self._filament_diameter = global_stack.extruders[str(self._extruder_number)].getProperty("material_diameter", "value")
|
||||
|
||||
scene_node = CuraSceneNode()
|
||||
|
@ -127,11 +127,11 @@ class GCodeWriter(MeshWriter):
|
||||
flat_global_container = self._createFlattenedContainerInstance(stack.userChanges, container_with_profile)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_global_container.getMetaDataEntry("type", None) is None:
|
||||
flat_global_container.addMetaDataEntry("type", "quality_changes")
|
||||
flat_global_container.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
|
||||
if flat_global_container.getMetaDataEntry("quality_type", None) is None:
|
||||
flat_global_container.addMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
flat_global_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
|
||||
# Get the machine definition ID for quality profiles
|
||||
machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(stack.definition)
|
||||
@ -151,15 +151,15 @@ class GCodeWriter(MeshWriter):
|
||||
flat_extruder_quality = self._createFlattenedContainerInstance(extruder.userChanges, extruder_quality)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_extruder_quality.getMetaDataEntry("type", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("type", "quality_changes")
|
||||
flat_extruder_quality.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
# Ensure that extruder is set. (Can happen if we have empty quality changes).
|
||||
if flat_extruder_quality.getMetaDataEntry("position", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("position", extruder.getMetaDataEntry("position"))
|
||||
flat_extruder_quality.setMetaDataEntry("position", extruder.getMetaDataEntry("position"))
|
||||
|
||||
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
|
||||
if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None:
|
||||
flat_extruder_quality.addMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
flat_extruder_quality.setMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
|
||||
# Change the default definition
|
||||
flat_extruder_quality.setMetaDataEntry("definition", machine_definition_id_for_quality)
|
||||
|
@ -145,9 +145,9 @@ class LegacyProfileReader(ProfileReader):
|
||||
if len(profile.getAllKeys()) == 0:
|
||||
Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
|
||||
|
||||
profile.addMetaDataEntry("type", "profile")
|
||||
profile.setMetaDataEntry("type", "profile")
|
||||
# don't know what quality_type it is based on, so use "normal" by default
|
||||
profile.addMetaDataEntry("quality_type", "normal")
|
||||
profile.setMetaDataEntry("quality_type", "normal")
|
||||
profile.setName(profile_id)
|
||||
profile.setDirty(True)
|
||||
|
||||
|
@ -135,10 +135,7 @@ class MachineSettingsAction(MachineAction):
|
||||
|
||||
material_node = None
|
||||
if has_materials:
|
||||
if "has_materials" in self._global_container_stack.getMetaData():
|
||||
self._global_container_stack.setMetaDataEntry("has_materials", True)
|
||||
else:
|
||||
self._global_container_stack.addMetaDataEntry("has_materials", True)
|
||||
self._global_container_stack.setMetaDataEntry("has_materials", True)
|
||||
else:
|
||||
# The metadata entry is stored in an ini, and ini files are parsed as strings only.
|
||||
# Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False.
|
||||
|
@ -248,7 +248,7 @@ class PostProcessingPlugin(QObject, Extension):
|
||||
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if "post_processing_scripts" not in global_stack.getMetaData():
|
||||
global_stack.addMetaDataEntry("post_processing_scripts", "")
|
||||
global_stack.setMetaDataEntry("post_processing_scripts", "")
|
||||
Application.getInstance().getGlobalContainerStack().setMetaDataEntry("post_processing_scripts", script_list_strs)
|
||||
|
||||
## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection.
|
||||
|
@ -48,7 +48,7 @@ class Script:
|
||||
self._stack.addContainer(self._definition)
|
||||
self._instance = InstanceContainer(container_id="ScriptInstanceContainer")
|
||||
self._instance.setDefinition(self._definition.getId())
|
||||
self._instance.addMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0))
|
||||
self._instance.setMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0))
|
||||
self._stack.addContainer(self._instance)
|
||||
self._stack.propertyChanged.connect(self._onPropertyChanged)
|
||||
|
||||
@ -105,9 +105,12 @@ class Script:
|
||||
if m is None:
|
||||
return default
|
||||
try:
|
||||
return float(m.group(0))
|
||||
except:
|
||||
return default
|
||||
return int(m.group(0))
|
||||
except ValueError: #Not an integer.
|
||||
try:
|
||||
return float(m.group(0))
|
||||
except ValueError: #Not a number at all.
|
||||
return default
|
||||
|
||||
## Convenience function to produce a line of g-code.
|
||||
#
|
||||
|
@ -58,10 +58,10 @@ class FilamentChange(Script):
|
||||
color_change = "M600"
|
||||
|
||||
if initial_retract is not None and initial_retract > 0.:
|
||||
color_change = color_change + (" E%.2f" % initial_retract)
|
||||
color_change = color_change + (" E-%.2f" % initial_retract)
|
||||
|
||||
if later_retract is not None and later_retract > 0.:
|
||||
color_change = color_change + (" L%.2f" % later_retract)
|
||||
color_change = color_change + (" L-%.2f" % later_retract)
|
||||
|
||||
color_change = color_change + " ; Generated by FilamentChange plugin"
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from ..Script import Script
|
||||
# from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
from UM.Application import Application #To get the current printer's settings.
|
||||
|
||||
class PauseAtHeight(Script):
|
||||
def __init__(self):
|
||||
@ -136,6 +140,10 @@ class PauseAtHeight(Script):
|
||||
layers_started = False
|
||||
redo_layers = self.getSettingValueByKey("redo_layers")
|
||||
standby_temperature = self.getSettingValueByKey("standby_temperature")
|
||||
firmware_retract = Application.getInstance().getGlobalContainerStack().getProperty("machine_firmware_retract", "value")
|
||||
control_temperatures = Application.getInstance().getGlobalContainerStack().getProperty("machine_nozzle_temp_enabled", "value")
|
||||
|
||||
is_griffin = False
|
||||
|
||||
# T = ExtruderManager.getInstance().getActiveExtruderStack().getProperty("material_print_temperature", "value")
|
||||
|
||||
@ -153,6 +161,8 @@ class PauseAtHeight(Script):
|
||||
|
||||
# Scroll each line of instruction for each layer in the G-code
|
||||
for line in lines:
|
||||
if ";FLAVOR:Griffin" in line:
|
||||
is_griffin = True
|
||||
# Fist positive layer reached
|
||||
if ";LAYER:0" in line:
|
||||
layers_started = True
|
||||
@ -162,12 +172,12 @@ class PauseAtHeight(Script):
|
||||
|
||||
#Track the latest printing temperature in order to resume at the correct temperature.
|
||||
if line.startswith("T"):
|
||||
current_t = int(self.getValue(line, "T"))
|
||||
current_t = self.getValue(line, "T")
|
||||
m = self.getValue(line, "M")
|
||||
if m is not None and (int(m) == 104 or int(m) == 109) and self.getValue(line, "S") is not None:
|
||||
if m is not None and (m == 104 or m == 109) and self.getValue(line, "S") is not None:
|
||||
extruder = current_t
|
||||
if self.getValue(line, "T") is not None:
|
||||
extruder = int(self.getValue(line, "T"))
|
||||
extruder = self.getValue(line, "T")
|
||||
target_temperature[extruder] = self.getValue(line, "S")
|
||||
|
||||
if not layers_started:
|
||||
@ -247,57 +257,71 @@ class PauseAtHeight(Script):
|
||||
prepend_gcode += ";added code by post processing\n"
|
||||
prepend_gcode += ";script: PauseAtHeight.py\n"
|
||||
if pause_at == "height":
|
||||
prepend_gcode += ";current z: {z}\n".format(z=current_z)
|
||||
prepend_gcode += ";current height: {height}\n".format(height=current_height)
|
||||
prepend_gcode += ";current z: {z}\n".format(z = current_z)
|
||||
prepend_gcode += ";current height: {height}\n".format(height = current_height)
|
||||
else:
|
||||
prepend_gcode += ";current layer: {layer}\n".format(layer=current_layer)
|
||||
prepend_gcode += ";current layer: {layer}\n".format(layer = current_layer)
|
||||
|
||||
# Retraction
|
||||
prepend_gcode += self.putValue(M=83) + "\n"
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G=1, E=-retraction_amount, F=retraction_speed * 60) + "\n"
|
||||
if not is_griffin:
|
||||
# Retraction
|
||||
prepend_gcode += self.putValue(M = 83) + "\n"
|
||||
if retraction_amount != 0:
|
||||
if firmware_retract: #Can't set the distance directly to what the user wants. We have to choose ourselves.
|
||||
retraction_count = 1 if control_temperatures else 3 #Retract more if we don't control the temperature.
|
||||
for i in range(retraction_count):
|
||||
prepend_gcode += self.putValue(G = 10) + "\n"
|
||||
else:
|
||||
prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"
|
||||
|
||||
# Move the head away
|
||||
prepend_gcode += self.putValue(G=1, Z=current_z + 1, F=300) + "\n"
|
||||
# Move the head away
|
||||
prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
|
||||
|
||||
# This line should be ok
|
||||
prepend_gcode += self.putValue(G=1, X=park_x, Y=park_y, F=9000) + "\n"
|
||||
# This line should be ok
|
||||
prepend_gcode += self.putValue(G = 1, X = park_x, Y = park_y, F = 9000) + "\n"
|
||||
|
||||
if current_z < 15:
|
||||
prepend_gcode += self.putValue(G=1, Z=15, F=300) + "\n"
|
||||
if current_z < 15:
|
||||
prepend_gcode += self.putValue(G = 1, Z = 15, F = 300) + "\n"
|
||||
|
||||
# Set extruder standby temperature
|
||||
prepend_gcode += self.putValue(M=104, S=standby_temperature) + "; standby temperature\n"
|
||||
if control_temperatures:
|
||||
# Set extruder standby temperature
|
||||
prepend_gcode += self.putValue(M = 104, S = standby_temperature) + "; standby temperature\n"
|
||||
|
||||
# Wait till the user continues printing
|
||||
prepend_gcode += self.putValue(M=0) + ";Do the actual pause\n"
|
||||
prepend_gcode += self.putValue(M = 0) + ";Do the actual pause\n"
|
||||
|
||||
# Set extruder resume temperature
|
||||
prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, 0))) + "; resume temperature\n"
|
||||
if not is_griffin:
|
||||
if control_temperatures:
|
||||
# Set extruder resume temperature
|
||||
prepend_gcode += self.putValue(M = 109, S = int(target_temperature.get(current_t, 0))) + "; resume temperature\n"
|
||||
|
||||
# Push the filament back,
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G=1, E=retraction_amount, F=retraction_speed * 60) + "\n"
|
||||
# Push the filament back,
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
|
||||
|
||||
# Optionally extrude material
|
||||
if extrude_amount != 0:
|
||||
prepend_gcode += self.putValue(G=1, E=extrude_amount, F=extrude_speed * 60) + "\n"
|
||||
# Optionally extrude material
|
||||
if extrude_amount != 0:
|
||||
prepend_gcode += self.putValue(G = 1, E = extrude_amount, F = extrude_speed * 60) + "\n"
|
||||
|
||||
# and retract again, the properly primes the nozzle
|
||||
# when changing filament.
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G=1, E=-retraction_amount, F=retraction_speed * 60) + "\n"
|
||||
# and retract again, the properly primes the nozzle
|
||||
# when changing filament.
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = retraction_speed * 60) + "\n"
|
||||
|
||||
# Move the head back
|
||||
prepend_gcode += self.putValue(G=1, Z=current_z + 1, F=300) + "\n"
|
||||
prepend_gcode += self.putValue(G=1, X=x, Y=y, F=9000) + "\n"
|
||||
if retraction_amount != 0:
|
||||
prepend_gcode += self.putValue(G=1, E=retraction_amount, F=retraction_speed * 60) + "\n"
|
||||
prepend_gcode += self.putValue(G=1, F=9000) + "\n"
|
||||
prepend_gcode += self.putValue(M=82) + "\n"
|
||||
# Move the head back
|
||||
prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
|
||||
prepend_gcode += self.putValue(G = 1, X = x, Y = y, F = 9000) + "\n"
|
||||
if retraction_amount != 0:
|
||||
if firmware_retract: #Can't set the distance directly to what the user wants. We have to choose ourselves.
|
||||
retraction_count = 1 if control_temperatures else 3 #Retract more if we don't control the temperature.
|
||||
for i in range(retraction_count):
|
||||
prepend_gcode += self.putValue(G = 11) + "\n"
|
||||
else:
|
||||
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = retraction_speed * 60) + "\n"
|
||||
prepend_gcode += self.putValue(G = 1, F = 9000) + "\n"
|
||||
prepend_gcode += self.putValue(M = 82) + "\n"
|
||||
|
||||
# reset extrude value to pre pause value
|
||||
prepend_gcode += self.putValue(G=92, E=current_e) + "\n"
|
||||
# reset extrude value to pre pause value
|
||||
prepend_gcode += self.putValue(G = 92, E = current_e) + "\n"
|
||||
|
||||
layer = prepend_gcode + layer
|
||||
|
||||
|
@ -35,25 +35,40 @@ class GCodeStep():
|
||||
Class to store the current value of each G_Code parameter
|
||||
for any G-Code step
|
||||
"""
|
||||
def __init__(self, step):
|
||||
def __init__(self, step, in_relative_movement: bool = False):
|
||||
self.step = step
|
||||
self.step_x = 0
|
||||
self.step_y = 0
|
||||
self.step_z = 0
|
||||
self.step_e = 0
|
||||
self.step_f = 0
|
||||
|
||||
self.in_relative_movement = in_relative_movement
|
||||
|
||||
self.comment = ""
|
||||
|
||||
def readStep(self, line):
|
||||
"""
|
||||
Reads gcode from line into self
|
||||
"""
|
||||
self.step_x = _getValue(line, "X", self.step_x)
|
||||
self.step_y = _getValue(line, "Y", self.step_y)
|
||||
self.step_z = _getValue(line, "Z", self.step_z)
|
||||
self.step_e = _getValue(line, "E", self.step_e)
|
||||
self.step_f = _getValue(line, "F", self.step_f)
|
||||
return
|
||||
if not self.in_relative_movement:
|
||||
self.step_x = _getValue(line, "X", self.step_x)
|
||||
self.step_y = _getValue(line, "Y", self.step_y)
|
||||
self.step_z = _getValue(line, "Z", self.step_z)
|
||||
self.step_e = _getValue(line, "E", self.step_e)
|
||||
self.step_f = _getValue(line, "F", self.step_f)
|
||||
else:
|
||||
delta_step_x = _getValue(line, "X", 0)
|
||||
delta_step_y = _getValue(line, "Y", 0)
|
||||
delta_step_z = _getValue(line, "Z", 0)
|
||||
delta_step_e = _getValue(line, "E", 0)
|
||||
delta_step_f = _getValue(line, "F", 0)
|
||||
|
||||
self.step_x += delta_step_x
|
||||
self.step_y += delta_step_y
|
||||
self.step_z += delta_step_z
|
||||
self.step_e += delta_step_e
|
||||
self.step_f += delta_step_f
|
||||
|
||||
def copyPosFrom(self, step):
|
||||
"""
|
||||
@ -65,7 +80,9 @@ class GCodeStep():
|
||||
self.step_e = step.step_e
|
||||
self.step_f = step.step_f
|
||||
self.comment = step.comment
|
||||
return
|
||||
|
||||
def setInRelativeMovement(self, value: bool) -> None:
|
||||
self.in_relative_movement = value
|
||||
|
||||
|
||||
# Execution part of the stretch plugin
|
||||
@ -86,6 +103,7 @@ class Stretcher():
|
||||
# of already deposited material for current layer
|
||||
self.layer_z = 0 # Z position of the extrusion moves of the current layer
|
||||
self.layergcode = ""
|
||||
self._in_relative_movement = False
|
||||
|
||||
def execute(self, data):
|
||||
"""
|
||||
@ -96,7 +114,8 @@ class Stretcher():
|
||||
+ " and push wall stretch " + str(self.pw_stretch) + "mm")
|
||||
retdata = []
|
||||
layer_steps = []
|
||||
current = GCodeStep(0)
|
||||
in_relative_movement = False
|
||||
current = GCodeStep(0, in_relative_movement)
|
||||
self.layer_z = 0.
|
||||
current_e = 0.
|
||||
for layer in data:
|
||||
@ -107,20 +126,31 @@ class Stretcher():
|
||||
current.comment = line[line.find(";"):]
|
||||
if _getValue(line, "G") == 0:
|
||||
current.readStep(line)
|
||||
onestep = GCodeStep(0)
|
||||
onestep = GCodeStep(0, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
elif _getValue(line, "G") == 1:
|
||||
current.readStep(line)
|
||||
onestep = GCodeStep(1)
|
||||
onestep = GCodeStep(1, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
|
||||
# end of relative movement
|
||||
elif _getValue(line, "G") == 90:
|
||||
in_relative_movement = False
|
||||
current.setInRelativeMovement(in_relative_movement)
|
||||
# start of relative movement
|
||||
elif _getValue(line, "G") == 91:
|
||||
in_relative_movement = True
|
||||
current.setInRelativeMovement(in_relative_movement)
|
||||
|
||||
elif _getValue(line, "G") == 92:
|
||||
current.readStep(line)
|
||||
onestep = GCodeStep(-1)
|
||||
onestep = GCodeStep(-1, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
else:
|
||||
onestep = GCodeStep(-1)
|
||||
onestep = GCodeStep(-1, in_relative_movement)
|
||||
onestep.copyPosFrom(current)
|
||||
onestep.comment = line
|
||||
|
||||
if line.find(";LAYER:") >= 0 and len(layer_steps):
|
||||
# Previous plugin "forgot" to separate two layers...
|
||||
Logger.log("d", "Layer Z " + "{:.3f}".format(self.layer_z)
|
||||
|
@ -11,7 +11,7 @@ from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
# Ignore windows error popups. Fixes the whole "Can't open drive X" when user has an SD card reader.
|
||||
ctypes.windll.kernel32.SetErrorMode(1)
|
||||
ctypes.windll.kernel32.SetErrorMode(1) #type: ignore
|
||||
|
||||
# WinAPI Constants that we need
|
||||
# Hardcoded here due to stupid WinDLL stuff that does not give us access to these values.
|
||||
@ -29,7 +29,7 @@ OPEN_EXISTING = 3 # [CodeStyle: Windows Enum value]
|
||||
|
||||
# Setup the DeviceIoControl function arguments and return type.
|
||||
# See ctypes documentation for details on how to call C functions from python, and why this is important.
|
||||
ctypes.windll.kernel32.DeviceIoControl.argtypes = [
|
||||
ctypes.windll.kernel32.DeviceIoControl.argtypes = [ #type: ignore
|
||||
wintypes.HANDLE, # _In_ HANDLE hDevice
|
||||
wintypes.DWORD, # _In_ DWORD dwIoControlCode
|
||||
wintypes.LPVOID, # _In_opt_ LPVOID lpInBuffer
|
||||
@ -39,7 +39,7 @@ ctypes.windll.kernel32.DeviceIoControl.argtypes = [
|
||||
ctypes.POINTER(wintypes.DWORD), # _Out_opt_ LPDWORD lpBytesReturned
|
||||
wintypes.LPVOID # _Inout_opt_ LPOVERLAPPED lpOverlapped
|
||||
]
|
||||
ctypes.windll.kernel32.DeviceIoControl.restype = wintypes.BOOL
|
||||
ctypes.windll.kernel32.DeviceIoControl.restype = wintypes.BOOL #type: ignore
|
||||
|
||||
|
||||
## Removable drive support for windows
|
||||
|
@ -16,7 +16,7 @@ from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
|
||||
from typing import cast, Optional
|
||||
from .SliceInfoJob import SliceInfoJob
|
||||
|
||||
|
||||
@ -79,11 +79,16 @@ class SliceInfo(QObject, Extension):
|
||||
return dialog
|
||||
|
||||
@pyqtSlot(result = str)
|
||||
def getExampleData(self) -> str:
|
||||
def getExampleData(self) -> Optional[str]:
|
||||
if self._example_data_content is None:
|
||||
file_path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "example_data.json")
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
self._example_data_content = f.read()
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if not plugin_path:
|
||||
Logger.log("e", "Could not get plugin path!", self.getPluginId())
|
||||
return None
|
||||
file_path = os.path.join(plugin_path, "example_data.json")
|
||||
if file_path:
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
self._example_data_content = f.read()
|
||||
return self._example_data_content
|
||||
|
||||
@pyqtSlot(bool)
|
||||
|
8
plugins/Toolbox/resources/images/installed_check.svg
Normal file
8
plugins/Toolbox/resources/images/installed_check.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<path d="M0,512h512V0L0,512z M440.4,318.3L331.2,431.6c-1.4,1.4-2.7,2-4.8,2c-2,0-3.4-0.7-4.8-2l-53.3-57.3l-1.4-2
|
||||
c-1.4-1.4-2-3.4-2-4.8c0-1.4,0.7-3.4,2-4.8l9.6-9.6c2.7-2.7,6.8-2.7,9.6,0l0.7,0.7l37.6,40.2c1.4,1.4,3.4,1.4,4.8,0l91.4-94.9h0.7
|
||||
c2.7-2.7,6.8-2.7,9.6,0l9.5,9.6C443.1,311.5,443.1,315.6,440.4,318.3z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 667 B |
@ -76,11 +76,26 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: columnTextDelegate
|
||||
Label
|
||||
{
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: styleData.value || ""
|
||||
elide: Text.ElideRight
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
|
||||
TableViewColumn
|
||||
{
|
||||
role: "machine"
|
||||
title: "Machine"
|
||||
width: Math.floor(table.width * 0.25)
|
||||
delegate: columnTextDelegate
|
||||
}
|
||||
TableViewColumn
|
||||
{
|
||||
|
@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
// This dialog asks the user whether he/she wants to open a project file as a project or import models.
|
||||
id: base
|
||||
|
||||
title: catalog.i18nc("@title:window", "Confirm uninstall ") + toolbox.pluginToUninstall
|
||||
width: 450 * screenScaleFactor
|
||||
height: 50 * screenScaleFactor + dialogText.height + buttonBar.height
|
||||
|
||||
maximumWidth: 450 * screenScaleFactor
|
||||
maximumHeight: 450 * screenScaleFactor
|
||||
minimumWidth: 450 * screenScaleFactor
|
||||
minimumHeight: 150 * screenScaleFactor
|
||||
|
||||
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal
|
||||
|
||||
Column
|
||||
{
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
|
||||
anchors
|
||||
{
|
||||
fill: parent
|
||||
leftMargin: Math.round(20 * screenScaleFactor)
|
||||
rightMargin: Math.round(20 * screenScaleFactor)
|
||||
topMargin: Math.round(10 * screenScaleFactor)
|
||||
bottomMargin: Math.round(10 * screenScaleFactor)
|
||||
}
|
||||
spacing: Math.round(15 * screenScaleFactor)
|
||||
|
||||
Label
|
||||
{
|
||||
id: dialogText
|
||||
text:
|
||||
{
|
||||
var base_text = catalog.i18nc("@text:window", "You are uninstalling materials and/or profiles that are still in use. Confirming will reset the following materials/profiles to their defaults.")
|
||||
var materials_text = catalog.i18nc("@text:window", "Materials")
|
||||
var qualities_text = catalog.i18nc("@text:window", "Profiles")
|
||||
var machines_with_materials = toolbox.uninstallUsedMaterials
|
||||
var machines_with_qualities = toolbox.uninstallUsedQualities
|
||||
if (machines_with_materials != "")
|
||||
{
|
||||
base_text += "\n\n" + materials_text +": \n" + machines_with_materials
|
||||
}
|
||||
if (machines_with_qualities != "")
|
||||
{
|
||||
base_text += "\n\n" + qualities_text + ": \n" + machines_with_qualities
|
||||
}
|
||||
return base_text
|
||||
}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
font: UM.Theme.getFont("default")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Item {
|
||||
id: buttonBar
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
height: childrenRect.height
|
||||
|
||||
Button {
|
||||
id: cancelButton
|
||||
text: catalog.i18nc("@action:button", "Cancel")
|
||||
anchors.right: confirmButton.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
isDefault: true
|
||||
onClicked: toolbox.closeConfirmResetDialog()
|
||||
}
|
||||
|
||||
Button {
|
||||
id: confirmButton
|
||||
text: catalog.i18nc("@action:button", "Confirm")
|
||||
anchors.right: parent.right
|
||||
onClicked: toolbox.resetMaterialsQualitiesAndUninstall()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -74,6 +74,7 @@ Item
|
||||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Version") + ":"
|
||||
@ -110,6 +111,7 @@ Item
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
spacing: Math.floor(UM.Theme.getSize("narrow_margin").height)
|
||||
height: childrenRect.height
|
||||
Label
|
||||
{
|
||||
text: details.version || catalog.i18nc("@label", "Unknown")
|
||||
|
@ -9,6 +9,9 @@ import UM 1.1 as UM
|
||||
|
||||
Column
|
||||
{
|
||||
property var heading: ""
|
||||
property var model
|
||||
id: gridArea
|
||||
height: childrenRect.height + 2 * padding
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
@ -16,7 +19,7 @@ Column
|
||||
Label
|
||||
{
|
||||
id: heading
|
||||
text: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community contributions") : catalog.i18nc("@label", "Community plugins")
|
||||
text: gridArea.heading
|
||||
width: parent.width
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
@ -24,14 +27,13 @@ Column
|
||||
GridLayout
|
||||
{
|
||||
id: grid
|
||||
property var model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
|
||||
width: parent.width
|
||||
width: parent.width - 2 * parent.padding
|
||||
columns: 2
|
||||
columnSpacing: UM.Theme.getSize("default_margin").height
|
||||
rowSpacing: UM.Theme.getSize("default_margin").width
|
||||
Repeater
|
||||
{
|
||||
model: grid.model
|
||||
model: gridArea.model
|
||||
delegate: ToolboxDownloadsGridTile
|
||||
{
|
||||
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
|
||||
|
@ -9,6 +9,8 @@ import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
height: childrenRect.height
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Rectangle
|
||||
@ -40,6 +42,21 @@ Item
|
||||
source: model.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
}
|
||||
UM.RecolorImage
|
||||
{
|
||||
width: (parent.width * 0.4) | 0
|
||||
height: (parent.height * 0.4) | 0
|
||||
anchors
|
||||
{
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
}
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
visible: installedPackages != 0
|
||||
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
|
||||
source: "../images/installed_check.svg"
|
||||
}
|
||||
}
|
||||
Column
|
||||
{
|
||||
@ -87,8 +104,18 @@ Item
|
||||
switch(toolbox.viewCategory)
|
||||
{
|
||||
case "material":
|
||||
toolbox.viewPage = "author"
|
||||
toolbox.filterModelByProp("packages", "author_id", model.id)
|
||||
|
||||
// If model has a type, it must be a package
|
||||
if (model.type !== undefined)
|
||||
{
|
||||
toolbox.viewPage = "detail"
|
||||
toolbox.filterModelByProp("packages", "id", model.id)
|
||||
}
|
||||
else
|
||||
{
|
||||
toolbox.viewPage = "author"
|
||||
toolbox.filterModelByProp("packages", "author_id", model.id)
|
||||
}
|
||||
break
|
||||
default:
|
||||
toolbox.viewPage = "detail"
|
||||
|
@ -29,6 +29,17 @@ ScrollView
|
||||
{
|
||||
id: allPlugins
|
||||
width: parent.width
|
||||
heading: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins")
|
||||
model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel
|
||||
}
|
||||
|
||||
ToolboxDownloadsGrid
|
||||
{
|
||||
id: genericMaterials
|
||||
visible: toolbox.viewCategory == "material"
|
||||
width: parent.width
|
||||
heading: catalog.i18nc("@label", "Generic Materials")
|
||||
model: toolbox.materialsGenericModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import UM 1.1 as UM
|
||||
|
||||
Rectangle
|
||||
{
|
||||
property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1
|
||||
property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0)
|
||||
id: tileBase
|
||||
width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width)
|
||||
height: thumbnail.height + packageNameBackground.height + (2 * UM.Theme.getSize("default_lining").width)
|
||||
@ -36,6 +38,22 @@ Rectangle
|
||||
source: model.icon_url || "../images/logobot.svg"
|
||||
mipmap: true
|
||||
}
|
||||
UM.RecolorImage
|
||||
{
|
||||
width: (parent.width * 0.3) | 0
|
||||
height: (parent.height * 0.3) | 0
|
||||
anchors
|
||||
{
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: UM.Theme.getSize("default_lining").width
|
||||
}
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
visible: installedPackages != 0
|
||||
color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border")
|
||||
source: "../images/installed_check.svg"
|
||||
}
|
||||
}
|
||||
Rectangle
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ Item
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.")
|
||||
color: UM.Theme.getColor("text")
|
||||
height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors
|
||||
@ -25,6 +26,7 @@ Item
|
||||
right: restartButton.right
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
}
|
||||
|
||||
}
|
||||
Button
|
||||
{
|
||||
|
@ -83,7 +83,7 @@ Column
|
||||
font: UM.Theme.getFont("default")
|
||||
}
|
||||
}
|
||||
onClicked: toolbox.uninstall(model.id)
|
||||
onClicked: toolbox.checkPackageUsageAndUninstall(model.id)
|
||||
Connections
|
||||
{
|
||||
target: toolbox
|
||||
|
@ -43,7 +43,7 @@ class AuthorsModel(ListModel):
|
||||
"package_count": author["package_count"] if "package_count" in author else 0,
|
||||
"package_types": author["package_types"] if "package_types" in author else [],
|
||||
"icon_url": author["icon_url"] if "icon_url" in author else None,
|
||||
"description": "Material and quality profiles from {author_name}".format( author_name = author["display_name"])
|
||||
"description": "Material and quality profiles from {author_name}".format(author_name = author["display_name"])
|
||||
})
|
||||
|
||||
# Filter on all the key-word arguments.
|
||||
|
@ -33,6 +33,7 @@ class PackagesModel(ListModel):
|
||||
self.addRoleName(Qt.UserRole + 16, "has_configs")
|
||||
self.addRoleName(Qt.UserRole + 17, "supported_configs")
|
||||
self.addRoleName(Qt.UserRole + 18, "download_count")
|
||||
self.addRoleName(Qt.UserRole + 19, "tags")
|
||||
|
||||
# List of filters for queries. The result is the union of the each list of results.
|
||||
self._filter = {} # type: Dict[str, str]
|
||||
@ -78,13 +79,15 @@ class PackagesModel(ListModel):
|
||||
"is_installed": package["is_installed"] if "is_installed" in package else False,
|
||||
"has_configs": has_configs,
|
||||
"supported_configs": configs_model,
|
||||
"download_count": package["download_count"] if "download_count" in package else 0
|
||||
|
||||
"download_count": package["download_count"] if "download_count" in package else 0,
|
||||
"tags": package["tags"] if "tags" in package else []
|
||||
})
|
||||
|
||||
# Filter on all the key-word arguments.
|
||||
for key, value in self._filter.items():
|
||||
if "*" in value:
|
||||
if key is "tags":
|
||||
key_filter = lambda item, value = value: value in item["tags"]
|
||||
elif "*" in value:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
|
||||
else:
|
||||
key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
|
||||
|
@ -6,7 +6,7 @@ import json
|
||||
import os
|
||||
import tempfile
|
||||
import platform
|
||||
from typing import List
|
||||
from typing import cast, List
|
||||
|
||||
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
@ -71,7 +71,8 @@ class Toolbox(QObject, Extension):
|
||||
"plugins_installed": [],
|
||||
"materials_showcase": [],
|
||||
"materials_available": [],
|
||||
"materials_installed": []
|
||||
"materials_installed": [],
|
||||
"materials_generic": []
|
||||
} # type: Dict[str, List[Any]]
|
||||
|
||||
# Models:
|
||||
@ -83,7 +84,8 @@ class Toolbox(QObject, Extension):
|
||||
"plugins_installed": PackagesModel(self),
|
||||
"materials_showcase": AuthorsModel(self),
|
||||
"materials_available": PackagesModel(self),
|
||||
"materials_installed": PackagesModel(self)
|
||||
"materials_installed": PackagesModel(self),
|
||||
"materials_generic": PackagesModel(self)
|
||||
} # type: Dict[str, ListModel]
|
||||
|
||||
# These properties are for keeping track of the UI state:
|
||||
@ -102,6 +104,9 @@ class Toolbox(QObject, Extension):
|
||||
self._active_package = None # type: Optional[Dict[str, Any]]
|
||||
|
||||
self._dialog = None #type: Optional[QObject]
|
||||
self._confirm_reset_dialog = None #type: Optional[QObject]
|
||||
self._resetUninstallVariables()
|
||||
|
||||
self._restart_required = False #type: bool
|
||||
|
||||
# variables for the license agreement dialog
|
||||
@ -130,6 +135,13 @@ class Toolbox(QObject, Extension):
|
||||
filterChanged = pyqtSignal()
|
||||
metadataChanged = pyqtSignal()
|
||||
showLicenseDialog = pyqtSignal()
|
||||
uninstallVariablesChanged = pyqtSignal()
|
||||
|
||||
def _resetUninstallVariables(self):
|
||||
self._package_id_to_uninstall = None
|
||||
self._package_name_to_uninstall = ""
|
||||
self._package_used_materials = []
|
||||
self._package_used_qualities = []
|
||||
|
||||
@pyqtSlot(result = str)
|
||||
def getLicenseDialogPluginName(self) -> str:
|
||||
@ -168,7 +180,8 @@ class Toolbox(QObject, Extension):
|
||||
"plugins_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)),
|
||||
"plugins_available": QUrl("{base_url}/packages?package_type=plugin".format(base_url=self._api_url)),
|
||||
"materials_showcase": QUrl("{base_url}/showcase".format(base_url=self._api_url)),
|
||||
"materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url=self._api_url))
|
||||
"materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url=self._api_url)),
|
||||
"materials_generic": QUrl("{base_url}/packages?package_type=material&tags=generic".format(base_url=self._api_url))
|
||||
}
|
||||
|
||||
# Get the API root for the packages API depending on Cura version settings.
|
||||
@ -218,12 +231,19 @@ class Toolbox(QObject, Extension):
|
||||
self._makeRequestByType("authors")
|
||||
self._makeRequestByType("plugins_showcase")
|
||||
self._makeRequestByType("materials_showcase")
|
||||
self._makeRequestByType("materials_available")
|
||||
self._makeRequestByType("materials_generic")
|
||||
|
||||
# Gather installed packages:
|
||||
self._updateInstalledModels()
|
||||
|
||||
if not self._dialog:
|
||||
self._dialog = self._createDialog("Toolbox.qml")
|
||||
|
||||
if not self._dialog:
|
||||
Logger.log("e", "Unexpected error trying to create the 'Toolbox' dialog.")
|
||||
return
|
||||
|
||||
self._dialog.show()
|
||||
|
||||
# Apply enabled/disabled state to installed plugins
|
||||
@ -231,11 +251,16 @@ class Toolbox(QObject, Extension):
|
||||
|
||||
def _createDialog(self, qml_name: str) -> Optional[QObject]:
|
||||
Logger.log("d", "Toolbox: Creating dialog [%s].", qml_name)
|
||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "resources", "qml", qml_name)
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if not plugin_path:
|
||||
return None
|
||||
path = os.path.join(plugin_path, "resources", "qml", qml_name)
|
||||
|
||||
dialog = self._application.createQmlComponent(path, {"toolbox": self})
|
||||
if not dialog:
|
||||
raise Exception("Failed to create toolbox dialog")
|
||||
return dialog
|
||||
|
||||
|
||||
def _convertPluginMetadata(self, plugin: Dict[str, Any]) -> Dict[str, Any]:
|
||||
formatted = {
|
||||
"package_id": plugin["id"],
|
||||
@ -294,9 +319,90 @@ class Toolbox(QObject, Extension):
|
||||
self._restart_required = True
|
||||
self.restartRequiredChanged.emit()
|
||||
|
||||
## Check package usage and uninstall
|
||||
# If the package is in use, you'll get a confirmation dialog to set everything to default
|
||||
@pyqtSlot(str)
|
||||
def uninstall(self, plugin_id: str) -> None:
|
||||
self._package_manager.removePackage(plugin_id, force_add = True)
|
||||
def checkPackageUsageAndUninstall(self, package_id: str) -> None:
|
||||
package_used_materials, package_used_qualities = self._package_manager.getMachinesUsingPackage(package_id)
|
||||
if package_used_materials or package_used_qualities:
|
||||
# Set up "uninstall variables" for resetMaterialsQualitiesAndUninstall
|
||||
self._package_id_to_uninstall = package_id
|
||||
package_info = self._package_manager.getInstalledPackageInfo(package_id)
|
||||
self._package_name_to_uninstall = package_info.get("display_name", package_info.get("package_id"))
|
||||
self._package_used_materials = package_used_materials
|
||||
self._package_used_qualities = package_used_qualities
|
||||
# Ask change to default material / profile
|
||||
if self._confirm_reset_dialog is None:
|
||||
self._confirm_reset_dialog = self._createDialog("ToolboxConfirmUninstallResetDialog.qml")
|
||||
self.uninstallVariablesChanged.emit()
|
||||
if self._confirm_reset_dialog is None:
|
||||
Logger.log("e", "ToolboxConfirmUninstallResetDialog should have been initialized, but it is not. Not showing dialog and not uninstalling package.")
|
||||
else:
|
||||
self._confirm_reset_dialog.show()
|
||||
else:
|
||||
# Plain uninstall
|
||||
self.uninstall(package_id)
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def pluginToUninstall(self):
|
||||
return self._package_name_to_uninstall
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def uninstallUsedMaterials(self):
|
||||
return "\n".join(["%s (%s)" % (str(global_stack.getName()), material) for global_stack, extruder_nr, material in self._package_used_materials])
|
||||
|
||||
@pyqtProperty(str, notify = uninstallVariablesChanged)
|
||||
def uninstallUsedQualities(self):
|
||||
return "\n".join(["%s (%s)" % (str(global_stack.getName()), quality) for global_stack, extruder_nr, quality in self._package_used_qualities])
|
||||
|
||||
@pyqtSlot()
|
||||
def closeConfirmResetDialog(self):
|
||||
if self._confirm_reset_dialog is not None:
|
||||
self._confirm_reset_dialog.close()
|
||||
|
||||
## Uses "uninstall variables" to reset qualities and materials, then uninstall
|
||||
# It's used as an action on Confirm reset on Uninstall
|
||||
@pyqtSlot()
|
||||
def resetMaterialsQualitiesAndUninstall(self):
|
||||
application = CuraApplication.getInstance()
|
||||
material_manager = application.getMaterialManager()
|
||||
quality_manager = application.getQualityManager()
|
||||
machine_manager = application.getMachineManager()
|
||||
|
||||
for global_stack, extruder_nr, container_id in self._package_used_materials:
|
||||
default_material_node = material_manager.getDefaultMaterial(global_stack, extruder_nr, global_stack.extruders[extruder_nr].variant.getName())
|
||||
machine_manager.setMaterial(extruder_nr, default_material_node, global_stack = global_stack)
|
||||
for global_stack, extruder_nr, container_id in self._package_used_qualities:
|
||||
default_quality_group = quality_manager.getDefaultQualityType(global_stack)
|
||||
machine_manager.setQualityGroup(default_quality_group, global_stack = global_stack)
|
||||
|
||||
self._markPackageMaterialsAsToBeUninstalled(self._package_id_to_uninstall)
|
||||
|
||||
self.uninstall(self._package_id_to_uninstall)
|
||||
self._resetUninstallVariables()
|
||||
self.closeConfirmResetDialog()
|
||||
|
||||
def _markPackageMaterialsAsToBeUninstalled(self, package_id: str) -> None:
|
||||
container_registry = self._application.getContainerRegistry()
|
||||
|
||||
all_containers = self._package_manager.getPackageContainerIds(package_id)
|
||||
for container_id in all_containers:
|
||||
containers = container_registry.findInstanceContainers(id = container_id)
|
||||
if not containers:
|
||||
continue
|
||||
container = containers[0]
|
||||
if container.getMetaDataEntry("type") != "material":
|
||||
continue
|
||||
root_material_id = container.getMetaDataEntry("base_file")
|
||||
root_material_containers = container_registry.findInstanceContainers(id = root_material_id)
|
||||
if not root_material_containers:
|
||||
continue
|
||||
root_material_container = root_material_containers[0]
|
||||
root_material_container.setMetaDataEntry("removed", True)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def uninstall(self, package_id: str) -> None:
|
||||
self._package_manager.removePackage(package_id, force_add = True)
|
||||
self.installChanged.emit()
|
||||
self._updateInstalledModels()
|
||||
self.metadataChanged.emit()
|
||||
@ -400,6 +506,22 @@ class Toolbox(QObject, Extension):
|
||||
def isInstalled(self, package_id: str) -> bool:
|
||||
return self._package_manager.isPackageInstalled(package_id)
|
||||
|
||||
@pyqtSlot(str, result = int)
|
||||
def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int:
|
||||
count = 0
|
||||
for package in self._metadata["materials_installed"]:
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@pyqtSlot(str, result = int)
|
||||
def getTotalNumberOfPackagesByAuthor(self, author_id: str) -> int:
|
||||
count = 0
|
||||
for package in self._metadata["materials_available"]:
|
||||
if package["author"]["author_id"] == author_id:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@pyqtSlot(str, result = bool)
|
||||
def isEnabled(self, package_id: str) -> bool:
|
||||
if package_id in self._plugin_registry.getActivePlugins():
|
||||
@ -522,6 +644,8 @@ class Toolbox(QObject, Extension):
|
||||
self._models[type].setFilter({"type": "plugin"})
|
||||
if type is "authors":
|
||||
self._models[type].setFilter({"package_types": "material"})
|
||||
if type is "materials_generic":
|
||||
self._models[type].setFilter({"tags": "generic"})
|
||||
|
||||
self.metadataChanged.emit()
|
||||
|
||||
@ -621,27 +745,31 @@ class Toolbox(QObject, Extension):
|
||||
# --------------------------------------------------------------------------
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def authorsModel(self) -> AuthorsModel:
|
||||
return self._models["authors"]
|
||||
return cast(AuthorsModel, self._models["authors"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def packagesModel(self) -> PackagesModel:
|
||||
return self._models["packages"]
|
||||
return cast(PackagesModel, self._models["packages"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsShowcaseModel(self) -> PackagesModel:
|
||||
return self._models["plugins_showcase"]
|
||||
return cast(PackagesModel, self._models["plugins_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def pluginsInstalledModel(self) -> PackagesModel:
|
||||
return self._models["plugins_installed"]
|
||||
return cast(PackagesModel, self._models["plugins_installed"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsShowcaseModel(self) -> PackagesModel:
|
||||
return self._models["materials_showcase"]
|
||||
def materialsShowcaseModel(self) -> AuthorsModel:
|
||||
return cast(AuthorsModel, self._models["materials_showcase"])
|
||||
|
||||
@pyqtProperty(QObject, notify = metadataChanged)
|
||||
def materialsInstalledModel(self) -> PackagesModel:
|
||||
return self._models["materials_installed"]
|
||||
return cast(PackagesModel, self._models["materials_installed"])
|
||||
|
||||
@pyqtProperty(QObject, notify=metadataChanged)
|
||||
def materialsGenericModel(self) -> PackagesModel:
|
||||
return cast(PackagesModel, self._models["materials_generic"])
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any, cast, Set, Tuple, Union
|
||||
from typing import Any, cast, Optional, Set, Tuple, Union
|
||||
|
||||
from UM.FileHandler.FileHandler import FileHandler
|
||||
from UM.FileHandler.FileWriter import FileWriter #To choose based on the output file mode (text vs. binary).
|
||||
@ -9,6 +9,7 @@ from UM.FileHandler.WriteFileJob import WriteFileJob #To call the file writer as
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Mesh.MeshWriter import MeshWriter # For typing
|
||||
from UM.Message import Message
|
||||
from UM.Qt.Duration import Duration, DurationFormat
|
||||
from UM.OutputDevice import OutputDeviceError #To show that something went wrong when writing.
|
||||
@ -103,8 +104,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
else:
|
||||
file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()
|
||||
|
||||
global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
#Create a list from the supported file formats string.
|
||||
machine_file_formats = CuraApplication.getInstance().getGlobalContainerStack().getMetaDataEntry("file_formats").split(";")
|
||||
if not global_stack:
|
||||
Logger.log("e", "Missing global stack!")
|
||||
return
|
||||
|
||||
machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";")
|
||||
machine_file_formats = [file_type.strip() for file_type in machine_file_formats]
|
||||
#Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format.
|
||||
if "application/x-ufp" not in machine_file_formats and self.printerType == "ultimaker3" and Version(self.firmwareVersion) >= Version("4.4"):
|
||||
@ -125,7 +131,14 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
else:
|
||||
writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, preferred_format["mime_type"]))
|
||||
|
||||
if not writer:
|
||||
Logger.log("e", "Unexpected error when trying to get the FileWriter")
|
||||
return
|
||||
|
||||
#This function pauses with the yield, waiting on instructions on which printer it needs to print with.
|
||||
if not writer:
|
||||
Logger.log("e", "Missing file or mesh writer!")
|
||||
return
|
||||
self._sending_job = self._sendPrintJob(writer, preferred_format, nodes)
|
||||
self._sending_job.send(None) #Start the generator.
|
||||
|
||||
@ -205,14 +218,14 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
yield #To prevent having to catch the StopIteration exception.
|
||||
|
||||
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None:
|
||||
self._write_job_progress_message.hide()
|
||||
if self._write_job_progress_message:
|
||||
self._write_job_progress_message.hide()
|
||||
|
||||
self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1,
|
||||
title = i18n_catalog.i18nc("@info:title", "Sending Data"))
|
||||
self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), icon = None, description = "")
|
||||
self._progress_message.actionTriggered.connect(self._progressMessageActionTriggered)
|
||||
self._progress_message.show()
|
||||
|
||||
parts = []
|
||||
|
||||
target_printer, preferred_format, stream = self._dummy_lambdas
|
||||
@ -249,7 +262,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self.activePrinterChanged.emit()
|
||||
|
||||
def _onPostPrintJobFinished(self, reply: QNetworkReply) -> None:
|
||||
self._progress_message.hide()
|
||||
if self._progress_message:
|
||||
self._progress_message.hide()
|
||||
self._compressing_gcode = False
|
||||
self._sending_gcode = False
|
||||
|
||||
|
@ -19,5 +19,5 @@ class ClusterUM3PrinterOutputController(PrinterOutputController):
|
||||
|
||||
def setJobState(self, job: "PrintJobOutputModel", state: str):
|
||||
data = "{\"action\": \"%s\"}" % state
|
||||
self._output_device.put("print_jobs/%s/action" % job.key, data, onFinished=None)
|
||||
self._output_device.put("print_jobs/%s/action" % job.key, data, on_finished=None)
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
import os.path
|
||||
import time
|
||||
from typing import Optional
|
||||
from typing import cast, Optional
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject
|
||||
|
||||
@ -108,8 +108,9 @@ class DiscoverUM3Action(MachineAction):
|
||||
# Find all the places where there is the same group name and change it accordingly
|
||||
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connect_group_name", value = previous_connect_group_name, new_value = group_name)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("connect_group_name", group_name)
|
||||
global_container_stack.addMetaDataEntry("hidden", False)
|
||||
global_container_stack.setMetaDataEntry("connect_group_name", group_name)
|
||||
# Set the default value for "hidden", which is used when you have a group with multiple types of printers
|
||||
global_container_stack.setMetaDataEntry("hidden", False)
|
||||
|
||||
if self._network_plugin:
|
||||
# Ensure that the connection states are refreshed.
|
||||
@ -130,7 +131,7 @@ class DiscoverUM3Action(MachineAction):
|
||||
global_container_stack.removeMetaDataEntry("network_authentication_key")
|
||||
CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("um_network_key", key)
|
||||
global_container_stack.setMetaDataEntry("um_network_key", key)
|
||||
|
||||
if self._network_plugin:
|
||||
# Ensure that the connection states are refreshed.
|
||||
@ -170,7 +171,10 @@ class DiscoverUM3Action(MachineAction):
|
||||
Logger.log("d", "Creating additional ui components for UM3.")
|
||||
|
||||
# Create networking dialog
|
||||
path = os.path.join(PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), "UM3InfoComponents.qml")
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
|
||||
if not plugin_path:
|
||||
return
|
||||
path = os.path.join(plugin_path, "UM3InfoComponents.qml")
|
||||
self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self})
|
||||
if not self.__additional_components_view:
|
||||
Logger.log("w", "Could not create ui components for UM3.")
|
||||
|
@ -165,7 +165,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
file_name = "none.xml"
|
||||
|
||||
self.postForm("materials", "form-data; name=\"file\";filename=\"%s\"" % file_name, xml_data.encode(), onFinished=None)
|
||||
self.postForm("materials", "form-data; name=\"file\";filename=\"%s\"" % file_name, xml_data.encode(), on_finished=None)
|
||||
|
||||
except NotImplementedError:
|
||||
# If the material container is not the most "generic" one it can't be serialized an will raise a
|
||||
@ -270,7 +270,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
file_name = "%s.gcode.gz" % CuraApplication.getInstance().getPrintInformation().jobName
|
||||
self.postForm("print_job", "form-data; name=\"file\";filename=\"%s\"" % file_name, compressed_gcode,
|
||||
onFinished=self._onPostPrintJobFinished)
|
||||
on_finished=self._onPostPrintJobFinished)
|
||||
|
||||
return
|
||||
|
||||
@ -381,8 +381,8 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self._checkAuthentication()
|
||||
|
||||
# We don't need authentication for requesting info, so we can go right ahead with requesting this.
|
||||
self.get("printer", onFinished=self._onGetPrinterDataFinished)
|
||||
self.get("print_job", onFinished=self._onGetPrintJobFinished)
|
||||
self.get("printer", on_finished=self._onGetPrinterDataFinished)
|
||||
self.get("print_job", on_finished=self._onGetPrintJobFinished)
|
||||
|
||||
def _resetAuthenticationRequestedMessage(self):
|
||||
if self._authentication_requested_message:
|
||||
@ -404,7 +404,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
def _verifyAuthentication(self):
|
||||
Logger.log("d", "Attempting to verify authentication")
|
||||
# This will ensure that the "_onAuthenticationRequired" is triggered, which will setup the authenticator.
|
||||
self.get("auth/verify", onFinished=self._onVerifyAuthenticationCompleted)
|
||||
self.get("auth/verify", on_finished=self._onVerifyAuthenticationCompleted)
|
||||
|
||||
def _onVerifyAuthenticationCompleted(self, reply):
|
||||
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
|
||||
@ -426,7 +426,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
def _checkAuthentication(self):
|
||||
Logger.log("d", "Checking if authentication is correct for id %s and key %s", self._authentication_id, self._getSafeAuthKey())
|
||||
self.get("auth/check/" + str(self._authentication_id), onFinished=self._onCheckAuthenticationFinished)
|
||||
self.get("auth/check/" + str(self._authentication_id), on_finished=self._onCheckAuthenticationFinished)
|
||||
|
||||
def _onCheckAuthenticationFinished(self, reply):
|
||||
if str(self._authentication_id) not in reply.url().toString():
|
||||
@ -455,18 +455,18 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self.setAuthenticationState(AuthState.AuthenticationDenied)
|
||||
self._authentication_failed_message.show()
|
||||
|
||||
def _saveAuthentication(self):
|
||||
def _saveAuthentication(self) -> None:
|
||||
global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if self._authentication_key is None:
|
||||
Logger.log("e", "Authentication key is None, nothing to save.")
|
||||
return
|
||||
if self._authentication_id is None:
|
||||
Logger.log("e", "Authentication id is None, nothing to save.")
|
||||
return
|
||||
if global_container_stack:
|
||||
if "network_authentication_key" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
global_container_stack.setMetaDataEntry("network_authentication_key", self._authentication_key)
|
||||
|
||||
if "network_authentication_id" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
global_container_stack.setMetaDataEntry("network_authentication_id", self._authentication_id)
|
||||
|
||||
# Force save so we are sure the data is not lost.
|
||||
CuraApplication.getInstance().saveStack(global_container_stack)
|
||||
@ -502,7 +502,7 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self.post("auth/request",
|
||||
json.dumps({"application": "Cura-" + CuraApplication.getInstance().getVersion(),
|
||||
"user": self._getUserName()}).encode(),
|
||||
onFinished=self._onRequestAuthenticationFinished)
|
||||
on_finished=self._onRequestAuthenticationFinished)
|
||||
|
||||
self.setAuthenticationState(AuthState.AuthenticationRequested)
|
||||
|
||||
@ -637,4 +637,4 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
result = "********" + result
|
||||
return result
|
||||
|
||||
return self._authentication_key
|
||||
return self._authentication_key
|
||||
|
@ -31,11 +31,11 @@ class LegacyUM3PrinterOutputController(PrinterOutputController):
|
||||
|
||||
def setJobState(self, job: "PrintJobOutputModel", state: str):
|
||||
data = "{\"target\": \"%s\"}" % state
|
||||
self._output_device.put("print_job/state", data, onFinished=None)
|
||||
self._output_device.put("print_job/state", data, on_finished=None)
|
||||
|
||||
def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int):
|
||||
data = str(temperature)
|
||||
self._output_device.put("printer/bed/temperature/target", data, onFinished=self._onPutBedTemperatureCompleted)
|
||||
self._output_device.put("printer/bed/temperature/target", data, on_finished=self._onPutBedTemperatureCompleted)
|
||||
|
||||
def _onPutBedTemperatureCompleted(self, reply):
|
||||
if Version(self._preheat_printer.firmwareVersion) < Version("3.5.92"):
|
||||
@ -51,10 +51,10 @@ class LegacyUM3PrinterOutputController(PrinterOutputController):
|
||||
new_y = head_pos.y + y
|
||||
new_z = head_pos.z + z
|
||||
data = "{\n\"x\":%s,\n\"y\":%s,\n\"z\":%s\n}" %(new_x, new_y, new_z)
|
||||
self._output_device.put("printer/heads/0/position", data, onFinished=None)
|
||||
self._output_device.put("printer/heads/0/position", data, on_finished=None)
|
||||
|
||||
def homeBed(self, printer):
|
||||
self._output_device.put("printer/heads/0/position/z", "0", onFinished=None)
|
||||
self._output_device.put("printer/heads/0/position/z", "0", on_finished=None)
|
||||
|
||||
def _onPreheatBedTimerFinished(self):
|
||||
self.setTargetBedTemperature(self._preheat_printer, 0)
|
||||
@ -89,7 +89,7 @@ class LegacyUM3PrinterOutputController(PrinterOutputController):
|
||||
printer.updateIsPreheating(True)
|
||||
return
|
||||
|
||||
self._output_device.put("printer/bed/pre_heat", data, onFinished = self._onPutPreheatBedCompleted)
|
||||
self._output_device.put("printer/bed/pre_heat", data, on_finished = self._onPutPreheatBedCompleted)
|
||||
printer.updateIsPreheating(True)
|
||||
self._preheat_request_in_progress = True
|
||||
|
||||
|
@ -26,6 +26,10 @@ UM.Dialog
|
||||
{
|
||||
resetPrintersModel()
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDevice.cancelPrintSelection()
|
||||
}
|
||||
}
|
||||
title: catalog.i18nc("@title:window", "Print over network")
|
||||
|
||||
|
@ -23,7 +23,7 @@ if TYPE_CHECKING:
|
||||
#
|
||||
# This way it won't freeze up the interface while sending those materials.
|
||||
class SendMaterialJob(Job):
|
||||
def __init__(self, device: "ClusterUM3OutputDevice"):
|
||||
def __init__(self, device: "ClusterUM3OutputDevice") -> None:
|
||||
super().__init__()
|
||||
self.device = device #type: ClusterUM3OutputDevice
|
||||
|
||||
|
@ -72,7 +72,7 @@ class AutoDetectBaudJob(Job):
|
||||
|
||||
while timeout_time > time():
|
||||
line = serial.readline()
|
||||
if b"ok T:" in line:
|
||||
if b"ok " in line and b"T:" in line:
|
||||
successful_responses += 1
|
||||
if successful_responses >= 3:
|
||||
self.setResult(baud_rate)
|
||||
|
@ -88,6 +88,25 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
self._command_received = Event()
|
||||
self._command_received.set()
|
||||
|
||||
CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
|
||||
|
||||
# This is a callback function that checks if there is any printing in progress via USB when the application tries
|
||||
# to exit. If so, it will show a confirmation before
|
||||
def _checkActivePrintingUponAppExit(self) -> None:
|
||||
application = CuraApplication.getInstance()
|
||||
if not self._is_printing:
|
||||
# This USB printer is not printing, so we have nothing to do. Call the next callback if exists.
|
||||
application.triggerNextExitCheck()
|
||||
return
|
||||
|
||||
application.setConfirmExitDialogCallback(self._onConfirmExitDialogResult)
|
||||
application.showConfirmExitDialog.emit(catalog.i18nc("@label", "A USB print is in progress, closing Cura will stop this print. Are you sure?"))
|
||||
|
||||
def _onConfirmExitDialogResult(self, result: bool) -> None:
|
||||
if result:
|
||||
application = CuraApplication.getInstance()
|
||||
application.triggerNextExitCheck()
|
||||
|
||||
## Reset USB device settings
|
||||
#
|
||||
def resetDeviceSettings(self):
|
||||
@ -304,7 +323,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
if self._firmware_name is None:
|
||||
self.sendCommand("M115")
|
||||
|
||||
if b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
if (b"ok " in line and b"T:" in line) or b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed
|
||||
extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line)
|
||||
# Update all temperature values
|
||||
matched_extruder_nrs = []
|
||||
@ -423,7 +442,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
elapsed_time = int(time() - self._print_start_time)
|
||||
print_job = self._printers[0].activePrintJob
|
||||
if print_job is None:
|
||||
print_job = PrintJobOutputModel(output_controller = GenericOutputController(self), name= Application.getInstance().getPrintInformation().jobName)
|
||||
print_job = PrintJobOutputModel(output_controller = GenericOutputController(self), name= CuraApplication.getInstance().getPrintInformation().jobName)
|
||||
print_job.updateState("printing")
|
||||
self._printers[0].updateActivePrintJob(print_job)
|
||||
|
||||
|
@ -47,10 +47,7 @@ class UM2UpgradeSelection(MachineAction):
|
||||
variant_container = global_container_stack.extruders["0"].variant
|
||||
|
||||
if has_variants:
|
||||
if "has_variants" in global_container_stack.getMetaData():
|
||||
global_container_stack.setMetaDataEntry("has_variants", True)
|
||||
else:
|
||||
global_container_stack.addMetaDataEntry("has_variants", True)
|
||||
global_container_stack.setMetaDataEntry("has_variants", True)
|
||||
|
||||
# Set the variant container to a sane default
|
||||
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
|
||||
|
@ -6,6 +6,55 @@ import io
|
||||
|
||||
from UM.VersionUpgrade import VersionUpgrade
|
||||
|
||||
deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"}
|
||||
|
||||
_RENAMED_MATERIAL_PROFILES = {
|
||||
"dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"dsm_arnitel2045_175_cartesio_0.4_mm": "dsm_arnitel2045_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"dsm_arnitel2045_175_cartesio_0.8_mm": "dsm_arnitel2045_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"dsm_novamid1070_175_cartesio_0.25_mm": "dsm_novamid1070_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"dsm_novamid1070_175_cartesio_0.4_mm": "dsm_novamid1070_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"dsm_novamid1070_175_cartesio_0.8_mm": "dsm_novamid1070_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_abs_175_cartesio_0.25_mm": "generic_abs_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_abs_175_cartesio_0.4_mm": "generic_abs_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_abs_175_cartesio_0.8_mm": "generic_abs_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_hips_175_cartesio_0.25_mm": "generic_hips_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_hips_175_cartesio_0.4_mm": "generic_hips_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_hips_175_cartesio_0.8_mm": "generic_hips_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_nylon_175_cartesio_0.25_mm": "generic_nylon_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_nylon_175_cartesio_0.4_mm": "generic_nylon_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_nylon_175_cartesio_0.8_mm": "generic_nylon_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_pc_cartesio_0.25_mm": "generic_pc_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_pc_cartesio_0.4_mm": "generic_pc_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_pc_cartesio_0.8_mm": "generic_pc_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_pc_175_cartesio_0.25_mm": "generic_pc_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_pc_175_cartesio_0.4_mm": "generic_pc_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_pc_175_cartesio_0.8_mm": "generic_pc_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_petg_175_cartesio_0.25_mm": "generic_petg_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_petg_175_cartesio_0.4_mm": "generic_petg_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_petg_175_cartesio_0.8_mm": "generic_petg_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_pla_175_cartesio_0.25_mm": "generic_pla_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_pla_175_cartesio_0.4_mm": "generic_pla_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_pla_175_cartesio_0.8_mm": "generic_pla_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_pva_cartesio_0.25_mm": "generic_pva_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_pva_cartesio_0.4_mm": "generic_pva_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_pva_cartesio_0.8_mm": "generic_pva_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"generic_pva_175_cartesio_0.25_mm": "generic_pva_175_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"generic_pva_175_cartesio_0.4_mm": "generic_pva_175_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"generic_pva_175_cartesio_0.8_mm": "generic_pva_175_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_black_cartesio_0.25_mm": "ultimaker_pc_black_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_black_cartesio_0.4_mm": "ultimaker_pc_black_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_black_cartesio_0.8_mm": "ultimaker_pc_black_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_transparent_cartesio_0.25_mm": "ultimaker_pc_transparent_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_transparent_cartesio_0.4_mm": "ultimaker_pc_transparent_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_transparent_cartesio_0.8_mm": "ultimaker_pc_transparent_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_white_cartesio_0.25_mm": "ultimaker_pc_white_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_white_cartesio_0.4_mm": "ultimaker_pc_white_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"ultimaker_pc_white_cartesio_0.8_mm": "ultimaker_pc_white_cartesio_0.8mm_thermoplastic_extruder",
|
||||
"ultimaker_pva_cartesio_0.25_mm": "ultimaker_pva_cartesio_0.25mm_thermoplastic_extruder",
|
||||
"ultimaker_pva_cartesio_0.4_mm": "ultimaker_pva_cartesio_0.4mm_thermoplastic_extruder",
|
||||
"ultimaker_pva_cartesio_0.8_mm": "ultimaker_pva_cartesio_0.8mm_thermoplastic_extruder"
|
||||
}
|
||||
|
||||
## Upgrades configurations from the state they were in at version 3.4 to the
|
||||
# state they should be in at version 4.0.
|
||||
@ -53,6 +102,10 @@ class VersionUpgrade34to40(VersionUpgrade):
|
||||
parser["general"]["version"] = "4"
|
||||
parser["metadata"]["setting_version"] = "5"
|
||||
|
||||
#Update the name of the quality profile.
|
||||
if parser["containers"]["3"] in _RENAMED_MATERIAL_PROFILES:
|
||||
parser["containers"]["3"] = _RENAMED_MATERIAL_PROFILES[parser["containers"]["3"]]
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
return [filename], [result.getvalue()]
|
||||
@ -68,6 +121,11 @@ class VersionUpgrade34to40(VersionUpgrade):
|
||||
parser["metadata"]["setting_version"] = "5"
|
||||
|
||||
self._resetConcentric3DInfillPattern(parser)
|
||||
if "values" in parser:
|
||||
for deleted_setting in deleted_settings:
|
||||
if deleted_setting not in parser["values"]:
|
||||
continue
|
||||
del parser["values"][deleted_setting]
|
||||
|
||||
result = io.StringIO()
|
||||
parser.write(result)
|
||||
|
@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To parse the resulting config files.
|
||||
import pytest #To register tests with.
|
||||
|
||||
import VersionUpgrade34to40 #The module we're testing.
|
||||
|
||||
## Creates an instance of the upgrader to test with.
|
||||
@pytest.fixture
|
||||
def upgrader():
|
||||
return VersionUpgrade34to40.VersionUpgrade34to40()
|
||||
|
||||
test_upgrade_version_nr_data = [
|
||||
("Empty config file",
|
||||
"""[general]
|
||||
version = 5
|
||||
[metadata]
|
||||
setting_version = 4
|
||||
"""
|
||||
)
|
||||
]
|
||||
|
||||
## Tests whether the version numbers are updated.
|
||||
@pytest.mark.parametrize("test_name, file_data", test_upgrade_version_nr_data)
|
||||
def test_upgradeVersionNr(test_name, file_data, upgrader):
|
||||
#Perform the upgrade.
|
||||
_, upgraded_instances = upgrader.upgradePreferences(file_data, "<string>")
|
||||
upgraded_instance = upgraded_instances[0]
|
||||
parser = configparser.ConfigParser(interpolation = None)
|
||||
parser.read_string(upgraded_instance)
|
||||
|
||||
#Check the new version.
|
||||
assert parser["general"]["version"] == "6"
|
||||
assert parser["metadata"]["setting_version"] == "5"
|
@ -2,6 +2,7 @@
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from math import pi, sin, cos, sqrt
|
||||
from typing import Dict
|
||||
|
||||
import numpy
|
||||
|
||||
@ -42,7 +43,7 @@ class X3DReader(MeshReader):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._supported_extensions = [".x3d"]
|
||||
self._namespaces = {}
|
||||
self._namespaces = {} # type: Dict[str, str]
|
||||
|
||||
# Main entry point
|
||||
# Reads the file, returns a SceneNode (possibly with nested ones), or None
|
||||
|
@ -662,6 +662,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"VersionUpgrade34to40": {
|
||||
"package_info": {
|
||||
"package_id": "VersionUpgrade34to40",
|
||||
"package_type": "plugin",
|
||||
"display_name": "Version Upgrade 3.4 to 4.0",
|
||||
"description": "Upgrades configurations from Cura 3.4 to Cura 4.0.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 4,
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "Ultimaker",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"X3DReader": {
|
||||
"package_info": {
|
||||
"package_id": "X3DReader",
|
||||
@ -713,6 +730,240 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericABS": {
|
||||
"package_info": {
|
||||
"package_id": "GenericABS",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic ABS",
|
||||
"description": "The generic ABS profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericBAM": {
|
||||
"package_info": {
|
||||
"package_id": "GenericBAM",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic BAM",
|
||||
"description": "The generic BAM profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericCPE": {
|
||||
"package_info": {
|
||||
"package_id": "GenericCPE",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic CPE",
|
||||
"description": "The generic CPE profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericCPEPlus": {
|
||||
"package_info": {
|
||||
"package_id": "GenericCPEPlus",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic CPE+",
|
||||
"description": "The generic CPE+ profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericHIPS": {
|
||||
"package_info": {
|
||||
"package_id": "GenericHIPS",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic HIPS",
|
||||
"description": "The generic HIPS profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericNylon": {
|
||||
"package_info": {
|
||||
"package_id": "GenericNylon",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic Nylon",
|
||||
"description": "The generic Nylon profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericPC": {
|
||||
"package_info": {
|
||||
"package_id": "GenericPC",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic PC",
|
||||
"description": "The generic PC profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericPETG": {
|
||||
"package_info": {
|
||||
"package_id": "GenericPETG",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic PETG",
|
||||
"description": "The generic PETG profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericPLA": {
|
||||
"package_info": {
|
||||
"package_id": "GenericPLA",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic PLA",
|
||||
"description": "The generic PLA profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericPP": {
|
||||
"package_info": {
|
||||
"package_id": "GenericPP",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic PP",
|
||||
"description": "The generic PP profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericPVA": {
|
||||
"package_info": {
|
||||
"package_id": "GenericPVA",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic PVA",
|
||||
"description": "The generic PVA profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericToughPLA": {
|
||||
"package_info": {
|
||||
"package_id": "GenericToughPLA",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic Tough PLA",
|
||||
"description": "The generic Tough PLA profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenericTPU": {
|
||||
"package_info": {
|
||||
"package_id": "GenericTPU",
|
||||
"package_type": "material",
|
||||
"display_name": "Generic TPU",
|
||||
"description": "The generic TPU profile which other profiles can be based upon.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": 6,
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"author": {
|
||||
"author_id": "Generic",
|
||||
"display_name": "Generic",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://github.com/Ultimaker/fdm_materials",
|
||||
"description": "Professional 3D printing made accessible."
|
||||
}
|
||||
}
|
||||
},
|
||||
"DagomaChromatikPLA": {
|
||||
"package_info": {
|
||||
"package_id": "DagomaChromatikPLA",
|
||||
|
@ -24,7 +24,6 @@
|
||||
"machine_depth": { "default_value": 149.86 },
|
||||
"machine_height": { "default_value": 99.822 },
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[ 0, 0 ],
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "3Dator" },
|
||||
"machine_nozzle_size": { "default_value": 0.5 },
|
||||
"speed_travel": { "default_value": 120 },
|
||||
"prime_tower_size": { "default_value": 8.660254037844387 },
|
||||
"infill_sparse_density": { "default_value": 20 },
|
||||
|
@ -26,9 +26,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[75, 18],
|
||||
|
@ -45,10 +45,6 @@
|
||||
{
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size":
|
||||
{
|
||||
"default_value": 0.4
|
||||
},
|
||||
"gantry_height":
|
||||
{
|
||||
"default_value": 0
|
||||
|
@ -21,7 +21,6 @@
|
||||
"prime_tower_size": { "default_value": 7.745966692414834 },
|
||||
"machine_name": { "default_value": "BFB_Test" },
|
||||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_nozzle_size": { "default_value": 0.5 },
|
||||
"speed_layer_0": { "default_value": 25 },
|
||||
"machine_width": { "default_value": 275 },
|
||||
"machine_gcode_flavor": { "default_value": "BFB" },
|
||||
|
@ -54,7 +54,6 @@
|
||||
"prime_tower_position_y": { "default_value": 178 },
|
||||
"prime_tower_wipe_enabled": { "default_value": false },
|
||||
"prime_tower_min_volume": { "default_value": 50 },
|
||||
"dual_pre_wipe": { "default_value": false },
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
|
@ -54,7 +54,6 @@
|
||||
"prime_tower_position_y": { "default_value": 178 },
|
||||
"prime_tower_wipe_enabled": { "default_value": false },
|
||||
"prime_tower_min_volume": { "default_value": 50 },
|
||||
"dual_pre_wipe": { "default_value": false },
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
|
@ -53,7 +53,6 @@
|
||||
"prime_tower_position_y": { "default_value": 178 },
|
||||
"prime_tower_wipe_enabled": { "default_value": false },
|
||||
"prime_tower_min_volume": { "default_value": 50 },
|
||||
"dual_pre_wipe": { "default_value": false },
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
"has_variants": true,
|
||||
|
||||
"variants_name": "Tool",
|
||||
"preferred_variant_name": "0.8 mm",
|
||||
"preferred_variant_name": "0.8mm thermoplastic extruder",
|
||||
"preferred_material": "generic_pla",
|
||||
"preferred_quality_type": "normal",
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
"material_print_temp_wait": { "default_value": false },
|
||||
"material_bed_temp_wait": { "default_value": false },
|
||||
"prime_tower_enable": { "default_value": false },
|
||||
"prime_tower_wall_thickness": { "resolve": 0.7 },
|
||||
"prime_tower_min_volume": { "value": "0.7" },
|
||||
"prime_tower_size": { "value": 24.0 },
|
||||
"prime_tower_position_x": { "value": 125 },
|
||||
"prime_tower_position_y": { "value": 70 },
|
||||
|
@ -31,9 +31,6 @@
|
||||
[30, 34]
|
||||
]
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.2
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Custom FDM printer",
|
||||
"name": "Custom FFF printer",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
|
@ -9,6 +9,8 @@
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "discoeasy200.stl",
|
||||
"platform_offset": [ 105, -59, 280],
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "dagoma_discoeasy200_extruder_0"
|
||||
@ -27,37 +29,46 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[17, 70],
|
||||
[17, -40],
|
||||
[-17, -40],
|
||||
[17, 70]
|
||||
[-17, -70],
|
||||
[-17, 40],
|
||||
[17, 40],
|
||||
[17, -70]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 10
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Gcode by Cura\nG90\nM106 S250\nG28 X Y\nG1 X50\nM109 S180\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n"
|
||||
"default_value": ";Gcode by Cura\nG90\nM106 S255\nG28 X Y\nG1 X50\nM109 R90\nG28\nM104 S{material_print_temperature_layer_0}\nG29\nM107\nG1 X100 Y20 F3000\nG1 Z0.5\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E10\nG92 E0\nG1 Z3\nG1 F6000\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 F3000\nG90\nG28 X Y\nM107\nM84\n"
|
||||
},
|
||||
"default_material_print_temperature": {
|
||||
"default_value": 205
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 60
|
||||
},
|
||||
"speed_travel": {
|
||||
"value": "100"
|
||||
"default_value": 100
|
||||
},
|
||||
"retraction_amount": {
|
||||
"default_value": 3.5
|
||||
},
|
||||
"retraction_speed": {
|
||||
"default_value": 50
|
||||
},
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.26
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"id": "Dagoma_neva",
|
||||
"name": "Dagoma NEVA",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
@ -10,6 +9,8 @@
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "neva.stl",
|
||||
"platform_offset": [ 0, 0, 0],
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "dagoma_neva_extruder_0"
|
||||
@ -28,15 +29,12 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[17, 40],
|
||||
[17, -70],
|
||||
[-17, -70],
|
||||
[17, 40]
|
||||
[-36, -42],
|
||||
[-36, 42],
|
||||
[36, 42],
|
||||
[36, -42]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
@ -46,14 +44,17 @@
|
||||
"default_value": "elliptic"
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (RepRap)"
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Gcode by Cura\nG90\nG28\nM109 S100\nG29\nM104 S{material_print_temperature_layer_0}\nG0 X0 Y-85\nG0 Z0.26\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n"
|
||||
"default_value": ";Gcode by Cura\nG90\nG28\nM107\nM109 R100\nG29\nM109 S{material_print_temperature_layer_0} U-55 X55 V-85 Y-85 W0.26 Z0.26\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n"
|
||||
},
|
||||
"default_material_print_temperature": {
|
||||
"default_value": 205
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 40
|
||||
},
|
||||
@ -65,6 +66,15 @@
|
||||
},
|
||||
"retraction_speed": {
|
||||
"default_value": 60
|
||||
},
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.26
|
||||
}
|
||||
}
|
||||
}
|
||||
|
80
resources/definitions/dagoma_neva_magis.def.json
Normal file
80
resources/definitions/dagoma_neva_magis.def.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "Dagoma NEVA Magis",
|
||||
"version": 2,
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Dagoma",
|
||||
"manufacturer": "Dagoma",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "neva.stl",
|
||||
"platform_offset": [ 0, 0, 0],
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "dagoma_neva_magis_extruder_0"
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"machine_width": {
|
||||
"default_value": 195.55
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 205
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 195.55
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[-36, -42],
|
||||
[-36, 42],
|
||||
[36, 42],
|
||||
[36, -42]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 0
|
||||
},
|
||||
"machine_shape": {
|
||||
"default_value": "elliptic"
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap"
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": ";Gcode by Cura\nG90\nG28\nM107\nM109 R100\nG29\nM109 S{material_print_temperature_layer_0} U-55 X55 V-85 Y-85 W0.26 Z0.26\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "\nM104 S0\nM106 S255\nM140 S0\nG91\nG1 E-1 F300\nG1 Z+3 E-2 F9000\nG90\nG28\n"
|
||||
},
|
||||
"default_material_print_temperature": {
|
||||
"default_value": 205
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 40
|
||||
},
|
||||
"speed_travel": {
|
||||
"default_value": 120
|
||||
},
|
||||
"retraction_amount": {
|
||||
"default_value": 3.8
|
||||
},
|
||||
"retraction_speed": {
|
||||
"default_value": 60
|
||||
},
|
||||
"adhesion_type": {
|
||||
"default_value": "skirt"
|
||||
},
|
||||
"skirt_line_count": {
|
||||
"default_value": 2
|
||||
},
|
||||
"layer_height_0": {
|
||||
"default_value": 0.26
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@
|
||||
"speed_wall_0": { "default_value": 30 },
|
||||
"speed_topbottom": { "default_value": 30 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"machine_nozzle_size": { "default_value": 0.5 },
|
||||
"speed_print": { "default_value": 30 },
|
||||
"speed_infill": { "default_value": 30 },
|
||||
"machine_extruder_count": { "default_value": 1 },
|
||||
|
@ -23,7 +23,6 @@
|
||||
"machine_height": { "default_value": 250 },
|
||||
"machine_depth": { "default_value": 190 },
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"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 ;Home all axes (max endstops)\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."},
|
||||
"machine_end_gcode": { "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG28 ;Home all axes (max endstops)\nM84 ;steppers off\nG90 ;absolute positioning" },
|
||||
@ -35,7 +34,7 @@
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
|
||||
"travel_avoid_distance": { "default_value": 1, "value": "1" },
|
||||
"speed_print" : { "default_value": 60 },
|
||||
"speed_print" : { "default_value": 70 },
|
||||
"speed_travel": { "value": "150.0" },
|
||||
"speed_infill": { "value": "round(speed_print * 1.05, 0)" },
|
||||
"speed_topbottom": { "value": "round(speed_print * 0.95, 0)" },
|
||||
@ -53,6 +52,9 @@
|
||||
"top_bottom_thickness": { "default_value": 0.6 },
|
||||
"support_z_distance": { "value": "layer_height * 2" },
|
||||
"support_bottom_distance": { "value": "layer_height" },
|
||||
"support_use_towers" : { "default_value": false }
|
||||
"support_use_towers" : { "default_value": false },
|
||||
"jerk_wall_0" : { "value": "30" },
|
||||
"jerk_travel" : { "default_value": 20 },
|
||||
"acceleration_travel" : { "value": 10000 }
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,6 @@
|
||||
"bottom_thickness": {
|
||||
"default_value": 1
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"speed_print": {
|
||||
"default_value": 75
|
||||
},
|
||||
|
@ -35,7 +35,6 @@
|
||||
"machine_depth": { "default_value": 234 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-75, 35], [-75, -18], [18, 35], [18, -18]] },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_max_feedrate_x": { "default_value": 250 },
|
||||
|
@ -1074,7 +1074,7 @@
|
||||
"maximum_value_warning": "top_layers - 1",
|
||||
"type": "int",
|
||||
"value": "0",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"limit_to_extruder": "top_bottom_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "top_layers > 0"
|
||||
},
|
||||
@ -1196,6 +1196,16 @@
|
||||
"limit_to_extruder": "top_bottom_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"connect_skin_polygons":
|
||||
{
|
||||
"label": "Connect Top/Bottom Polygons",
|
||||
"description": "Connect top/bottom skin paths where they run next to each other. For the concentric pattern enabling this setting greatly reduces the travel time, but because the connections can happend midway over infill this feature can reduce the top surface quality.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "top_bottom_pattern == 'concentric'",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"skin_angles":
|
||||
{
|
||||
"label": "Top/Bottom Line Directions",
|
||||
@ -1644,7 +1654,18 @@
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": "infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
|
||||
"enabled": "infill_pattern == 'grid' or infill_pattern == 'triangles' or infill_pattern == 'trihexagon' or infill_pattern == 'cubic' or infill_pattern == 'tetrahedral' or infill_pattern == 'quarter_cubic' or infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
|
||||
"enabled": "infill_pattern == 'lines' or infill_pattern == 'grid' or infill_pattern == 'triangles' or infill_pattern == 'trihexagon' or infill_pattern == 'cubic' or infill_pattern == 'tetrahedral' or infill_pattern == 'quarter_cubic' or infill_pattern == 'cross' or infill_pattern == 'cross_3d'",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"connect_infill_polygons":
|
||||
{
|
||||
"label": "Connect Infill Polygons",
|
||||
"description": "Connect infill paths where they run next to each other. For infill patterns which consist of several closed polygons, enabling this setting greatly reduces the travel time.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"value": "infill_pattern == 'cross' or infill_pattern == 'cross_3d' or infill_multiplier % 2 == 0",
|
||||
"enabled": "infill_pattern == 'cross' or infill_pattern == 'cross_3d' or infill_multiplier % 2 == 0",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
@ -1680,6 +1701,18 @@
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_multiplier":
|
||||
{
|
||||
"label": "Infill Line Multiplier",
|
||||
"description": "Convert each infill line to this many lines. The extra lines do not cross over each other, but avoid each other. This makes the infill stiffer, but increases print time and material usage.",
|
||||
"default_value": 1,
|
||||
"type": "int",
|
||||
"minimum_value": "1",
|
||||
"maximum_value_warning": "infill_line_distance / infill_line_width",
|
||||
"enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled and infill_pattern != 'zigzag'",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"sub_div_rad_add":
|
||||
{
|
||||
"label": "Cubic Subdivision Shell",
|
||||
@ -4247,6 +4280,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"support_fan_enable":
|
||||
{
|
||||
"label": "Fan Speed Override",
|
||||
"description": "When enabled, the print cooling fan speed is altered for the skin regions immediately above the support.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "support_enable",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
"support_supported_skin_fan_speed":
|
||||
{
|
||||
"label": "Supported Skin Fan Speed",
|
||||
"description": "Percentage fan speed to use when printing the skin regions immediately above the support. Using a high fan speed can make the support easier to remove.",
|
||||
"unit": "%",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 100,
|
||||
"type": "float",
|
||||
"enabled": "support_enable and support_fan_enable",
|
||||
"settable_per_mesh": false
|
||||
},
|
||||
"support_use_towers":
|
||||
{
|
||||
"label": "Use Towers",
|
||||
@ -4992,32 +5046,12 @@
|
||||
"description": "The minimum volume for each layer of the prime tower in order to purge enough material.",
|
||||
"unit": "mm³",
|
||||
"type": "float",
|
||||
"default_value": 10,
|
||||
"value": "8.48 if prime_tower_circular else 10",
|
||||
"default_value": 5,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "round((resolveOrValue('prime_tower_size') * 0.5) ** 2 * 3.14159 * resolveOrValue('layer_height'), 2) if prime_tower_circular else resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')",
|
||||
"maximum_value_warning": "((resolveOrValue('prime_tower_size') * 0.5) ** 2 * 3.14159 * resolveOrValue('layer_height') if prime_tower_circular else resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')) - sum(extruderValues('prime_tower_min_volume')) + prime_tower_min_volume",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
{
|
||||
"prime_tower_wall_thickness":
|
||||
{
|
||||
"label": "Prime Tower Thickness",
|
||||
"description": "The thickness of the hollow prime tower. A thickness larger than half the Prime Tower Minimum Volume will result in a dense prime tower.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 2,
|
||||
"value": "round(max(2 * prime_tower_line_width, (0.5 * (prime_tower_size - math.sqrt(max(0, prime_tower_size ** 2 - 4 * prime_tower_min_volume / (3.14159 * layer_height))))) if prime_tower_circular else (0.5 * (prime_tower_size - math.sqrt(max(0, prime_tower_size ** 2 - prime_tower_min_volume / layer_height))))), 3)",
|
||||
"resolve": "max(extruderValues('prime_tower_wall_thickness'))",
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "2 * min(extruderValues('prime_tower_line_width')) - 0.0001",
|
||||
"maximum_value_warning": "prime_tower_size / 2",
|
||||
"enabled": "prime_tower_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
}
|
||||
}
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"prime_tower_position_x":
|
||||
{
|
||||
@ -5072,29 +5106,6 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"dual_pre_wipe":
|
||||
{
|
||||
"label": "Wipe Nozzle After Switch",
|
||||
"description": "After switching extruder, wipe the oozed material off of the nozzle on the first thing printed. This performs a safe slow wipe move at a place where the oozed material causes least harm to the surface quality of your print.",
|
||||
"type": "bool",
|
||||
"enabled": "resolveOrValue('prime_tower_enable')",
|
||||
"default_value": true,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"prime_tower_purge_volume":
|
||||
{
|
||||
"label": "Prime Tower Purge Volume",
|
||||
"description": "Amount of filament to be purged when wiping on the prime tower. Purging is useful for compensating the filament lost by oozing during inactivity of the nozzle.",
|
||||
"type": "float",
|
||||
"enabled": "resolveOrValue('prime_tower_enable') and dual_pre_wipe",
|
||||
"unit": "mm³",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "2.5",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"ooze_shield_enabled":
|
||||
{
|
||||
"label": "Enable Ooze Shield",
|
||||
@ -5195,6 +5206,7 @@
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"value": "extruders_enabled_count > 1",
|
||||
"enabled": "all(p != 'surface' for p in extruderValues('magic_mesh_surface_mode'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true
|
||||
@ -5205,7 +5217,7 @@
|
||||
"description": "Switch to which mesh intersecting volumes will belong with every layer, so that the overlapping meshes become interwoven. Turning this setting off will cause one of the meshes to obtain all of the volume in the overlap, while it is removed from the other meshes.",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "carve_multiple_volumes",
|
||||
"enabled": "carve_multiple_volumes and all(p != 'surface' for p in extruderValues('magic_mesh_surface_mode'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": true
|
||||
|
@ -44,7 +44,6 @@
|
||||
"retraction_amount": { "default_value": 1 },
|
||||
"retraction_speed": { "default_value": 50},
|
||||
"material_flow": { "default_value": 87 },
|
||||
"machine_nozzle_size": { "default_value": 0.35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"skirt_brim_minimal_length": { "default_value": 130},
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
"machine_depth": { "default_value": 406 },
|
||||
"machine_height": { "default_value": 533 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_nozzle_size": { "default_value": 0.5 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"layer_height_0": { "default_value": 0.3 },
|
||||
"retraction_amount": { "default_value": 1 },
|
||||
|
@ -28,7 +28,6 @@
|
||||
"machine_depth": { "default_value": 406 },
|
||||
"machine_height": { "default_value": 533 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_nozzle_size": { "default_value": 0.5 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"layer_height_0": { "default_value": 0.3 },
|
||||
"retraction_amount": { "default_value": 1 },
|
||||
|
@ -29,9 +29,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.5
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[-75, -18],
|
||||
|
@ -26,7 +26,6 @@
|
||||
"machine_width": { "default_value": 170 },
|
||||
"machine_height": { "default_value": 145 },
|
||||
"machine_depth": { "default_value": 160 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
|
@ -32,9 +32,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[-43.7, -19.2],
|
||||
|
@ -34,9 +34,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
|
@ -35,9 +35,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 2
|
||||
},
|
||||
|
@ -32,9 +32,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
|
@ -31,9 +31,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": true
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.35
|
||||
},
|
||||
"machine_gcode_flavor": {
|
||||
"default_value": "RepRap (Marlin/Sprinter)"
|
||||
},
|
||||
|
@ -33,9 +33,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[-75, -18],
|
||||
|
@ -33,9 +33,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_polygon": {
|
||||
"default_value": [
|
||||
[-75, -18],
|
||||
|
@ -30,9 +30,6 @@
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user