mirror of
https://github.com/Ultimaker/Cura.git
synced 2026-01-26 15:07:30 -07:00
Merge remote-tracking branch 'origin/main' into CURA-12580_paint-on-support
This commit is contained in:
commit
1145adfc3a
116 changed files with 6100 additions and 1916 deletions
12
.github/workflows/find-packages.yml
vendored
12
.github/workflows/find-packages.yml
vendored
|
|
@ -13,6 +13,14 @@ on:
|
|||
default: true
|
||||
required: false
|
||||
type: boolean
|
||||
cura_conan_version:
|
||||
description: 'Cura Conan Version (optional, overrides discovered packages)'
|
||||
default: ''
|
||||
type: string
|
||||
package_overrides:
|
||||
description: 'List of specific packages to be used (space-separated, in addition to discovered packages)'
|
||||
default: ''
|
||||
type: string
|
||||
conan_args:
|
||||
description: 'Conan args'
|
||||
default: ''
|
||||
|
|
@ -43,8 +51,8 @@ jobs:
|
|||
if: ${{ inputs.start_builds == true && needs.find-packages.outputs.discovered_packages != '' }}
|
||||
uses: ultimaker/cura-workflows/.github/workflows/cura-installers.yml@main
|
||||
with:
|
||||
cura_conan_version: ${{ needs.find-packages.outputs.cura_package }}
|
||||
package_overrides: ${{ needs.find-packages.outputs.package_overrides }}
|
||||
cura_conan_version: ${{ inputs.cura_conan_version != '' && inputs.cura_conan_version || needs.find-packages.outputs.cura_package }}
|
||||
package_overrides: ${{ needs.find-packages.outputs.package_overrides }} ${{ inputs.package_overrides }}
|
||||
conan_args: ${{ inputs.conan_args }}
|
||||
enterprise: ${{ inputs.enterprise }}
|
||||
staging: ${{ inputs.staging }}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
version: "5.11.0"
|
||||
version: "5.12.0-alpha.0"
|
||||
commit: "unknown"
|
||||
requirements:
|
||||
- "cura_resources/5.11.0"
|
||||
- "uranium/5.11.0"
|
||||
- "curaengine/5.11.0"
|
||||
- "cura_binary_data/5.11.0"
|
||||
- "fdm_materials/5.11.0"
|
||||
- "cura_resources/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "uranium/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "curaengine/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "cura_binary_data/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "fdm_materials/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "dulcificum/5.10.0"
|
||||
- "pysavitar/5.11.0-alpha.0"
|
||||
- "pynest2d/5.10.0"
|
||||
requirements_internal:
|
||||
- "fdm_materials/5.11.0"
|
||||
- "cura_private_data/5.11.0-alpha.0@internal/testing"
|
||||
- "fdm_materials/5.12.0-alpha.0@ultimaker/testing"
|
||||
- "cura_private_data/5.12.0-alpha.0@internal/testing"
|
||||
requirements_enterprise:
|
||||
- "native_cad_plugin/2.0.0"
|
||||
urls:
|
||||
|
|
|
|||
127
conanfile.py
127
conanfile.py
|
|
@ -153,71 +153,110 @@ class CuraConan(ConanFile):
|
|||
def _retrieve_pip_license(self, package, sources_url, dependency_description):
|
||||
# Download the sources to get the license file inside
|
||||
self.output.info(f"Retrieving license for {package}")
|
||||
response = requests.get(sources_url)
|
||||
response.raise_for_status()
|
||||
try:
|
||||
response = requests.get(sources_url)
|
||||
response.raise_for_status()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
sources_path = os.path.join(temp_dir, "sources.tar.gz")
|
||||
with open(sources_path, 'wb') as sources_file:
|
||||
sources_file.write(response.content)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
sources_path = os.path.join(temp_dir, "sources.tar.gz")
|
||||
with open(sources_path, 'wb') as sources_file:
|
||||
sources_file.write(response.content)
|
||||
|
||||
with tarfile.open(sources_path, 'r:gz') as sources_archive:
|
||||
license_file = "LICENSE"
|
||||
with tarfile.open(sources_path, 'r:gz') as sources_archive:
|
||||
license_file = "LICENSE"
|
||||
|
||||
for source_file in sources_archive.getnames():
|
||||
if Path(source_file).name == license_file:
|
||||
sources_archive.extract(source_file, temp_dir)
|
||||
for source_file in sources_archive.getnames():
|
||||
if Path(source_file).name == license_file:
|
||||
sources_archive.extract(source_file, temp_dir)
|
||||
|
||||
license_file_path = os.path.join(temp_dir, source_file)
|
||||
with open(license_file_path, 'r', encoding='utf8') as file:
|
||||
dependency_description["license_full"] = file.read()
|
||||
license_file_path = os.path.join(temp_dir, source_file)
|
||||
with open(license_file_path, 'r', encoding='utf8') as file:
|
||||
dependency_description["license_full"] = file.read()
|
||||
break
|
||||
except Exception as e:
|
||||
self.output.warning(f"Failed to retrieve license for {package} from {sources_url}: {e}")
|
||||
# Don't fail the build, just continue without the license
|
||||
|
||||
def _make_pip_dependency_description(self, package, version, dependencies):
|
||||
url = ["https://pypi.org/pypi", package]
|
||||
if version is not None:
|
||||
url.append(version)
|
||||
# Strip local version identifiers (everything after '+') for PyPI API compatibility
|
||||
# e.g., "1.26.1+mkl" becomes "1.26.1"
|
||||
clean_version = version.split('+')[0] if '+' in version else version
|
||||
url.append(clean_version)
|
||||
url.append("json")
|
||||
|
||||
data = requests.get("/".join(url)).json()
|
||||
try:
|
||||
response = requests.get("/".join(url))
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
except (requests.RequestException, ValueError) as e:
|
||||
self.output.warning(f"Failed to retrieve PyPI data for {package}: {e}")
|
||||
# Create minimal dependency description with fallback values
|
||||
dependencies[package] = {
|
||||
"summary": f"Package {package}",
|
||||
"version": version or "unknown",
|
||||
"license": "unknown"
|
||||
}
|
||||
return
|
||||
|
||||
# Check if the response has the expected structure
|
||||
if "info" not in data:
|
||||
self.output.warning(f"PyPI response for {package} missing 'info' field")
|
||||
dependencies[package] = {
|
||||
"summary": f"Package {package}",
|
||||
"version": version or "unknown",
|
||||
"license": "unknown"
|
||||
}
|
||||
return
|
||||
|
||||
info = data["info"]
|
||||
dependency_description = {
|
||||
"summary": data["info"]["summary"],
|
||||
"version": data["info"]["version"],
|
||||
"license": data["info"]["license"]
|
||||
"summary": info.get("summary", f"Package {package}"),
|
||||
"version": version or info.get("version", "unknown"), # Use original version if available
|
||||
"license": info.get("license", "unknown")
|
||||
}
|
||||
|
||||
for url_data in data["urls"]:
|
||||
if url_data["packagetype"] == "sdist":
|
||||
sources_url = url_data["url"]
|
||||
dependency_description["sources_url"] = sources_url
|
||||
# Handle URLs section safely
|
||||
if "urls" in data:
|
||||
for url_data in data["urls"]:
|
||||
if url_data.get("packagetype") == "sdist":
|
||||
sources_url = url_data.get("url")
|
||||
if sources_url:
|
||||
dependency_description["sources_url"] = sources_url
|
||||
|
||||
if not self.options.skip_licenses_download:
|
||||
self._retrieve_pip_license(package, sources_url, dependency_description)
|
||||
if not self.options.skip_licenses_download:
|
||||
try:
|
||||
self._retrieve_pip_license(package, sources_url, dependency_description)
|
||||
except Exception as e:
|
||||
self.output.warning(f"Failed to retrieve license for {package}: {e}")
|
||||
|
||||
for source_url, check_source in [("source", False),
|
||||
("Source", False),
|
||||
("Source Code", False),
|
||||
("Repository", False),
|
||||
("Code", False),
|
||||
("homepage", True),
|
||||
("Homepage", True)]:
|
||||
try:
|
||||
url = data["info"]["project_urls"][source_url]
|
||||
if check_source and not self._is_repository_url(url):
|
||||
# That will not work for ALL open-source projects, but should already get a large majority of them
|
||||
self.output.warning(f"Source URL for {package} ({url}) doesn't seem to be a supported repository")
|
||||
continue
|
||||
dependency_description["sources_url"] = url
|
||||
break
|
||||
except KeyError:
|
||||
pass
|
||||
# Handle project URLs safely
|
||||
if "project_urls" in info:
|
||||
for source_url, check_source in [("source", False),
|
||||
("Source", False),
|
||||
("Source Code", False),
|
||||
("Repository", False),
|
||||
("Code", False),
|
||||
("homepage", True),
|
||||
("Homepage", True)]:
|
||||
try:
|
||||
url = info["project_urls"][source_url]
|
||||
if check_source and not self._is_repository_url(url):
|
||||
# That will not work for ALL open-source projects, but should already get a large majority of them
|
||||
self.output.warning(f"Source URL for {package} ({url}) doesn't seem to be a supported repository")
|
||||
continue
|
||||
dependency_description["sources_url"] = url
|
||||
break
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
|
||||
if dependency_description["license"] is not None and len(dependency_description["license"]) > 32:
|
||||
# Some packages have their full license in this field
|
||||
dependency_description["license_full"] = dependency_description["license"]
|
||||
dependency_description["license"] = data["info"]["name"]
|
||||
dependency_description["license"] = info.get("name", package)
|
||||
|
||||
dependencies[data["info"]["name"]] = dependency_description
|
||||
dependencies[info.get("name", package)] = dependency_description
|
||||
|
||||
@staticmethod
|
||||
def _get_license_from_repository(sources_url, version, license_file_name = None):
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ class BuildVolume(SceneNode):
|
|||
self._application.engineCreatedSignal.connect(self._onEngineCreated)
|
||||
|
||||
self._has_errors = False
|
||||
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||
scene = self._application.getController().getScene()
|
||||
scene.sceneChanged.connect(self._onSceneChanged)
|
||||
|
||||
# Objects loaded at the moment. We are connected to the property changed events of these objects.
|
||||
self._scene_objects = set() # type: Set[SceneNode]
|
||||
|
|
@ -318,16 +319,6 @@ class BuildVolume(SceneNode):
|
|||
if node_bounding_box and node_bounding_box.top < 0 and not node.getParent().callDecoration("isGroup"):
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
# Mark the node as outside build volume if the set extruder is disabled
|
||||
extruder_position = node.callDecoration("getActiveExtruderPosition")
|
||||
try:
|
||||
if not self._global_container_stack.extruderList[int(extruder_position)].isEnabled and not node.callDecoration("isGroup"):
|
||||
node.setOutsideBuildArea(True)
|
||||
continue
|
||||
except IndexError: # Happens when the extruder list is too short. We're not done building the printer in memory yet.
|
||||
continue
|
||||
except TypeError: # Happens when extruder_position is None. This object has no extruder decoration.
|
||||
continue
|
||||
|
||||
node.setOutsideBuildArea(False)
|
||||
|
||||
|
|
@ -655,7 +646,7 @@ class BuildVolume(SceneNode):
|
|||
extra_z = retraction_hop
|
||||
return extra_z
|
||||
|
||||
def _onStackChanged(self):
|
||||
def _onStackChanged(self, *args) -> None:
|
||||
self._stack_change_timer.start()
|
||||
|
||||
def _onStackChangeTimerFinished(self) -> None:
|
||||
|
|
|
|||
|
|
@ -1300,6 +1300,7 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 0, self.getCuraSceneController, "SceneController")
|
||||
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, self.getExtruderManager, "ExtruderManager")
|
||||
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, self.getMachineManager, "MachineManager")
|
||||
qmlRegisterSingletonType(MachineErrorChecker, "Cura", 1, 0, self.getMachineErrorChecker, "MachineErrorChecker")
|
||||
qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
|
||||
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
|
||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from UM.Logger import Logger
|
|||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.CuraApplication import CuraApplication # To find some resource types.
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
|
||||
from UM.PackageManager import PackageManager # The class we're extending.
|
||||
from UM.Resources import Resources # To find storage paths for some resource types.
|
||||
|
|
@ -123,8 +124,7 @@ class CuraPackageManager(PackageManager):
|
|||
"""
|
||||
|
||||
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)]
|
||||
global_stacks = CuraContainerRegistry.getInstance().findGlobalStacks()
|
||||
machine_with_materials = []
|
||||
machine_with_qualities = []
|
||||
for container_id in ids:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Job import Job # For our background task of loading MachineNodes lazily.
|
||||
from UM.JobQueue import JobQueue # For our background task of loading MachineNodes lazily.
|
||||
from UM.Logger import Logger
|
||||
|
|
@ -10,7 +9,7 @@ import cura.CuraApplication # Imported like this to prevent circular dependenci
|
|||
from cura.Machines.MachineNode import MachineNode
|
||||
from cura.Settings.GlobalStack import GlobalStack # To listen only to global stacks being added.
|
||||
|
||||
from typing import Dict, List, Optional, TYPE_CHECKING
|
||||
from typing import Dict, List, Optional, Any, Set, TYPE_CHECKING
|
||||
import time
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -78,9 +77,10 @@ class ContainerTree:
|
|||
|
||||
def _onStartupFinished(self) -> None:
|
||||
"""Ran after completely starting up the application."""
|
||||
|
||||
currently_added = ContainerRegistry.getInstance().findContainerStacks() # Find all currently added global stacks.
|
||||
JobQueue.getInstance().add(self._MachineNodeLoadJob(self, currently_added))
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
currently_added_stacks = CuraContainerRegistry.getInstance().findGlobalStacks()
|
||||
definitions_ids = set([stack.definition.getId() for stack in currently_added_stacks])
|
||||
JobQueue.getInstance().add(self._MachineNodeLoadJob(self, definitions_ids))
|
||||
|
||||
class _MachineNodeMap:
|
||||
"""Dictionary-like object that contains the machines.
|
||||
|
|
@ -153,17 +153,17 @@ class ContainerTree:
|
|||
faster.
|
||||
"""
|
||||
|
||||
def __init__(self, tree_root: "ContainerTree", container_stacks: List["ContainerStack"]) -> None:
|
||||
def __init__(self, tree_root: "ContainerTree", definitions_ids: Set[str]) -> None:
|
||||
"""Creates a new background task.
|
||||
|
||||
:param tree_root: The container tree instance. This cannot be obtained through the singleton static
|
||||
function since the instance may not yet be constructed completely.
|
||||
:param container_stacks: All of the stacks to pre-load the container trees for. This needs to be provided
|
||||
from here because the stacks need to be constructed on the main thread because they are QObject.
|
||||
:param definitions_ids: The IDs of all the definitions to pre-load the container trees for. This needs to be
|
||||
provided from here because the stacks need to be constructed on the main thread because they are QObject.
|
||||
"""
|
||||
|
||||
self.tree_root = tree_root
|
||||
self.container_stacks = container_stacks
|
||||
self.definitions_ids: Set[str] = definitions_ids
|
||||
super().__init__()
|
||||
|
||||
def run(self) -> None:
|
||||
|
|
@ -172,14 +172,12 @@ class ContainerTree:
|
|||
The ``JobQueue`` will schedule this on a different thread.
|
||||
"""
|
||||
Logger.log("d", "Started background loading of MachineNodes")
|
||||
for stack in self.container_stacks: # Load all currently-added containers.
|
||||
if not isinstance(stack, GlobalStack):
|
||||
continue
|
||||
for definition_id in self.definitions_ids: # Load all currently-added containers.
|
||||
# Allow a thread switch after every container.
|
||||
# Experimentally, sleep(0) didn't allow switching. sleep(0.1) or sleep(0.2) neither.
|
||||
# We're in no hurry though. Half a second is fine.
|
||||
time.sleep(0.5)
|
||||
definition_id = stack.definition.getId()
|
||||
|
||||
if not self.tree_root.machines.is_loaded(definition_id):
|
||||
_ = self.tree_root.machines[definition_id]
|
||||
Logger.log("d", "All MachineNode loading completed")
|
||||
|
|
@ -6,7 +6,7 @@ import time
|
|||
from collections import deque
|
||||
|
||||
from PyQt6.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty
|
||||
from typing import Optional, Any, Set
|
||||
from typing import Optional, Any, Set, List
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Settings.SettingDefinition import SettingDefinition
|
||||
|
|
@ -77,21 +77,21 @@ class MachineErrorChecker(QObject):
|
|||
|
||||
def _onMachineChanged(self) -> None:
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged)
|
||||
self._global_stack.propertiesChanged.disconnect(self.startErrorCheckPropertyChanged)
|
||||
self._global_stack.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruderList:
|
||||
extruder.propertyChanged.disconnect(self.startErrorCheckPropertyChanged)
|
||||
extruder.propertiesChanged.disconnect(self.startErrorCheckPropertyChanged)
|
||||
extruder.containersChanged.disconnect(self.startErrorCheck)
|
||||
|
||||
self._global_stack = self._machine_manager.activeMachine
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self.startErrorCheckPropertyChanged)
|
||||
self._global_stack.propertiesChanged.connect(self.startErrorCheckPropertyChanged)
|
||||
self._global_stack.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
for extruder in self._global_stack.extruderList:
|
||||
extruder.propertyChanged.connect(self.startErrorCheckPropertyChanged)
|
||||
extruder.propertiesChanged.connect(self.startErrorCheckPropertyChanged)
|
||||
extruder.containersChanged.connect(self.startErrorCheck)
|
||||
|
||||
hasErrorUpdated = pyqtSignal()
|
||||
|
|
@ -106,15 +106,15 @@ class MachineErrorChecker(QObject):
|
|||
def needToWaitForResult(self) -> bool:
|
||||
return self._need_to_check or self._check_in_progress
|
||||
|
||||
def startErrorCheckPropertyChanged(self, key: str, property_name: str) -> None:
|
||||
def startErrorCheckPropertyChanged(self, key: str, property_names: List[str]) -> None:
|
||||
"""Start the error check for property changed
|
||||
this is separate from the startErrorCheck because it ignores a number property types
|
||||
|
||||
:param key:
|
||||
:param property_name:
|
||||
:param property_names:
|
||||
"""
|
||||
|
||||
if property_name != "value":
|
||||
if "validationState" not in property_names and "enabled" not in property_names:
|
||||
return
|
||||
self._keys_to_check.add(key)
|
||||
self.startErrorCheck()
|
||||
|
|
@ -212,7 +212,6 @@ class MachineErrorChecker(QObject):
|
|||
if result != self._has_errors:
|
||||
self._has_errors = result
|
||||
self.hasErrorUpdated.emit()
|
||||
self._machine_manager.stacksValidationChanged.emit()
|
||||
self._keys_to_check = keys_to_recheck if keys_to_recheck else set()
|
||||
self._need_to_check = False
|
||||
self._check_in_progress = False
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
if getattr(node, "_outside_buildarea", False):
|
||||
continue
|
||||
|
||||
if node.callDecoration("isAssignedToDisabledExtruder"):
|
||||
continue
|
||||
|
||||
if node.callDecoration("getConvexHull"):
|
||||
node_list.append(node)
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class PlatformPhysics:
|
|||
nodes = list(BreadthFirstIterator(root))
|
||||
|
||||
# Only check nodes inside build area.
|
||||
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
|
||||
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea and not node.callDecoration("isAssignedToDisabledExtruder"))]
|
||||
|
||||
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
|
||||
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ class PreviewPass(RenderPass):
|
|||
|
||||
# Fill up the batch with objects that can be sliced.
|
||||
for node in DepthFirstIterator(self._root):
|
||||
if hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea"):
|
||||
if (hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea") and
|
||||
not node.callDecoration("isAssignedToDisabledExtruder")):
|
||||
if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible():
|
||||
per_mesh_stack = node.callDecoration("getStack")
|
||||
if node.callDecoration("isNonThumbnailVisibleMesh"):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import copy
|
||||
import json
|
||||
import numpy
|
||||
|
||||
from typing import Optional, Dict
|
||||
from typing import Optional, Dict, List
|
||||
|
||||
from PyQt6.QtCore import QBuffer
|
||||
from PyQt6.QtCore import QBuffer, QTimer
|
||||
from PyQt6.QtGui import QImage, QImageWriter
|
||||
|
||||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
|
|
@ -18,8 +21,18 @@ class SliceableObjectDecorator(SceneNodeDecorator):
|
|||
self._paint_texture = None
|
||||
self._texture_data_mapping: Dict[str, tuple[int, int]] = {}
|
||||
|
||||
self._is_assigned_to_disabled_extruder: bool = False
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
application = CuraApplication.getInstance()
|
||||
if application is not None:
|
||||
application.getMachineManager().extruderChanged.connect(self._updateIsAssignedToDisabledExtruder)
|
||||
self._painted_extruders: Optional[List[int]] = None
|
||||
|
||||
self.paintTextureChanged = Signal()
|
||||
|
||||
self._texture_change_timer: Optional[QTimer] = None
|
||||
|
||||
def isSliceable(self) -> bool:
|
||||
return True
|
||||
|
||||
|
|
@ -29,6 +42,39 @@ class SliceableObjectDecorator(SceneNodeDecorator):
|
|||
def getPaintTextureChangedSignal(self) -> Signal:
|
||||
return self.paintTextureChanged
|
||||
|
||||
def setPaintedExtrudersCountDirty(self) -> None:
|
||||
if self._texture_change_timer is None:
|
||||
# Lazy initialize the timer because constructor can be called from non-Qt thread
|
||||
self._texture_change_timer = QTimer()
|
||||
self._texture_change_timer.setInterval(500) # Long interval to avoid triggering during painting
|
||||
self._texture_change_timer.setSingleShot(True)
|
||||
self._texture_change_timer.timeout.connect(self._onTextureChangeTimerFinished)
|
||||
|
||||
self._texture_change_timer.start()
|
||||
|
||||
def _onTextureChangeTimerFinished(self) -> None:
|
||||
self._painted_extruders = None
|
||||
|
||||
if (self._paint_texture is None or self._paint_texture.getImage() is None or
|
||||
"extruder" not in self._texture_data_mapping):
|
||||
return
|
||||
|
||||
image = self._paint_texture.getImage()
|
||||
image_bits = image.constBits()
|
||||
image_bits.setsize(image.sizeInBytes())
|
||||
image_array = numpy.frombuffer(image_bits, dtype=numpy.uint32)
|
||||
|
||||
bit_range_start, bit_range_end = self._texture_data_mapping["extruder"]
|
||||
full_int32 = 0xffffffff
|
||||
bit_mask = (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> (
|
||||
32 - 1 - bit_range_end))
|
||||
|
||||
texel_counts = numpy.bincount((image_array & bit_mask) >> bit_range_start)
|
||||
self._painted_extruders = [extruder_nr for extruder_nr, count in enumerate(texel_counts) if count > 0]
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
CuraApplication.getInstance().globalContainerStackChanged.emit()
|
||||
|
||||
def setPaintTexture(self, texture: Texture) -> None:
|
||||
self._paint_texture = texture
|
||||
self.paintTextureChanged.emit()
|
||||
|
|
@ -63,6 +109,24 @@ class SliceableObjectDecorator(SceneNodeDecorator):
|
|||
|
||||
return texture_buffer.data()
|
||||
|
||||
def isAssignedToDisabledExtruder(self) -> bool:
|
||||
return self._is_assigned_to_disabled_extruder
|
||||
|
||||
def _updateIsAssignedToDisabledExtruder(self) -> None:
|
||||
new_is_assigned_to_disabled_extruder = False
|
||||
try:
|
||||
extruder_stack = self.getNode().getPrintingExtruder()
|
||||
new_is_assigned_to_disabled_extruder = ((extruder_stack is None or not extruder_stack.isEnabled) and
|
||||
not self.getNode().callDecoration("isGroup"))
|
||||
except IndexError: # Happens when the extruder list is too short. We're not done building the printer in memory yet.
|
||||
pass
|
||||
except TypeError: # Happens when extruder_position is None. This object has no extruder decoration.
|
||||
pass
|
||||
|
||||
self._is_assigned_to_disabled_extruder = new_is_assigned_to_disabled_extruder
|
||||
def getPaintedExtruders(self) -> Optional[List[int]]:
|
||||
return self._painted_extruders
|
||||
|
||||
def __deepcopy__(self, memo) -> "SliceableObjectDecorator":
|
||||
copied_decorator = SliceableObjectDecorator()
|
||||
copied_decorator.setPaintTexture(copy.deepcopy(self.getPaintTexture()))
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ from UM.Resources import Resources
|
|||
from UM.Util import parseBool
|
||||
from cura.ReaderWriters.ProfileWriter import ProfileWriter
|
||||
|
||||
from . import ExtruderStack
|
||||
from . import GlobalStack
|
||||
from cura.Settings import ExtruderStack
|
||||
from cura.Settings import GlobalStack
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Settings.cura_empty_instance_containers import empty_quality_container
|
||||
|
|
@ -52,6 +52,31 @@ class CuraContainerRegistry(ContainerRegistry):
|
|||
self._database_handlers["quality"] = QualityDatabaseHandler()
|
||||
self._database_handlers["intent"] = IntentDatabaseHandler()
|
||||
|
||||
def findGlobalStacks(self, **kwargs: Any) -> List[GlobalStack.GlobalStack]:
|
||||
"""Find all GlobalStack objects matching certain criteria.
|
||||
|
||||
:param kwargs: A dictionary of keyword arguments containing
|
||||
keys and values that need to match the metadata of the GlobalStack.
|
||||
An asterisk in the values can be used to denote a wildcard.
|
||||
:note There are currently a lot of calls to findContainerStacks with a type="machine" filter, which technically
|
||||
returns the very same result, except that the result has to be explicitly converted to GlobalStack class and
|
||||
is slightly less self-explicit. They can be converted by calling this method instead.
|
||||
"""
|
||||
|
||||
return cast(List[GlobalStack.GlobalStack], self.findContainers(container_type = GlobalStack.GlobalStack, **kwargs))
|
||||
|
||||
def findGlobalStacksMetadata(self, **kwargs: Any) -> List[Dict[str, Any]]:
|
||||
"""Find the metadata of all global stacks matching certain criteria.
|
||||
|
||||
:param kwargs: A dictionary of keyword arguments containing keys and
|
||||
values that need to match the metadata. An asterisk in the values can be
|
||||
used to denote a wildcard.
|
||||
:return: A list of metadata dictionaries matching the search criteria, or
|
||||
an empty list if nothing was found.
|
||||
"""
|
||||
|
||||
return self.findContainersMetadata(container_type = GlobalStack.GlobalStack, **kwargs)
|
||||
|
||||
@override(ContainerRegistry)
|
||||
def addContainer(self, container: ContainerInterface) -> bool:
|
||||
"""Overridden from ContainerRegistry
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt.
|
||||
|
|
@ -248,11 +248,19 @@ class ExtruderManager(QObject):
|
|||
stack_to_use = container_registry.findContainerStacks(id = extruder_stack_id)[0]
|
||||
|
||||
if not support_enabled:
|
||||
support_enabled |= stack_to_use.getProperty("support_enable", "value")
|
||||
support_enabled |= parseBool(stack_to_use.getProperty("support_enable", "value"))
|
||||
if not support_bottom_enabled:
|
||||
support_bottom_enabled |= stack_to_use.getProperty("support_bottom_enable", "value")
|
||||
support_bottom_enabled |= parseBool(stack_to_use.getProperty("support_bottom_enable", "value"))
|
||||
if not support_roof_enabled:
|
||||
support_roof_enabled |= stack_to_use.getProperty("support_roof_enable", "value")
|
||||
support_roof_enabled |= parseBool(stack_to_use.getProperty("support_roof_enable", "value"))
|
||||
|
||||
painted_extruders = node.callDecoration("getPaintedExtruders")
|
||||
if painted_extruders is not None:
|
||||
for extruder_nr in painted_extruders:
|
||||
try:
|
||||
used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Check limit to extruders
|
||||
limit_to_extruder_feature_list = ["wall_0_extruder_nr",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast, Set
|
|||
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Decorators import deprecated
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
|
|
@ -87,8 +88,6 @@ class MachineManager(QObject):
|
|||
self.globalContainerChanged.connect(self.activeQualityChangesGroupChanged)
|
||||
self.globalContainerChanged.connect(self.activeQualityGroupChanged)
|
||||
|
||||
self._stacks_have_errors = None # type: Optional[bool]
|
||||
|
||||
extruder_manager = self._application.getExtruderManager()
|
||||
|
||||
extruder_manager.activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||
|
|
@ -447,31 +446,6 @@ class MachineManager(QObject):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _checkStacksHaveErrors(self) -> bool:
|
||||
time_start = time.time()
|
||||
if self._global_container_stack is None: #No active machine.
|
||||
return False
|
||||
|
||||
if self._global_container_stack.hasErrors():
|
||||
Logger.log("d", "Checking global stack for errors took %0.2f s and we found an error" % (time.time() - time_start))
|
||||
return True
|
||||
|
||||
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
|
||||
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||
extruder_stacks = self._global_container_stack.extruderList
|
||||
count = 1 # We start with the global stack
|
||||
for stack in extruder_stacks:
|
||||
md = stack.getMetaData()
|
||||
if "position" in md and int(md["position"]) >= machine_extruder_count:
|
||||
continue
|
||||
count += 1
|
||||
if stack.hasErrors():
|
||||
Logger.log("d", "Checking %s stacks for errors took %.2f s and we found an error in stack [%s]" % (count, time.time() - time_start, str(stack)))
|
||||
return True
|
||||
|
||||
Logger.log("d", "Checking %s stacks for errors took %.2f s" % (count, time.time() - time_start))
|
||||
return False
|
||||
|
||||
@pyqtProperty(bool, notify = numUserSettingsChanged)
|
||||
def hasUserSettings(self) -> bool:
|
||||
return self._num_user_settings != 0
|
||||
|
|
@ -515,13 +489,10 @@ class MachineManager(QObject):
|
|||
container.sendPostponedEmits()
|
||||
|
||||
@pyqtProperty(bool, notify = stacksValidationChanged)
|
||||
@deprecated("This property was already inactive and will now be removed, use MachineErrorChecker.hasError instead.", since="5.12.0")
|
||||
def stacksHaveErrors(self) -> bool:
|
||||
"""Check if none of the stacks contain error states
|
||||
|
||||
Note that the _stacks_have_errors is cached due to performance issues
|
||||
Calling _checkStack(s)ForErrors on every change is simply too expensive
|
||||
"""
|
||||
return bool(self._stacks_have_errors)
|
||||
"""Check if none of the stacks contain error states"""
|
||||
return False
|
||||
|
||||
@pyqtProperty(str, notify = globalContainerChanged)
|
||||
def activeMachineFirmwareVersion(self) -> str:
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class Snapshot:
|
|||
def isNodeRenderable(node):
|
||||
return not getattr(node, "_outside_buildarea", False) and node.callDecoration(
|
||||
"isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration(
|
||||
"isNonThumbnailVisibleMesh")
|
||||
"isNonThumbnailVisibleMesh") and not node.callDecoration("isAssignedToDisabledExtruder")
|
||||
|
||||
@staticmethod
|
||||
def nodeBounds(root_node: SceneNode) -> Optional[AxisAlignedBox]:
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ class PrintInformation(QObject):
|
|||
|
||||
self._pre_sliced = False
|
||||
|
||||
self._user_time_estimation_adjusted = False
|
||||
|
||||
self._backend = self._application.getBackend()
|
||||
if self._backend:
|
||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||
|
|
@ -128,6 +130,12 @@ class PrintInformation(QObject):
|
|||
def preSliced(self) -> bool:
|
||||
return self._pre_sliced
|
||||
|
||||
userTimeAdjustedChanged = pyqtSignal()
|
||||
|
||||
@pyqtProperty(bool, notify=userTimeAdjustedChanged)
|
||||
def userTimeAdjusted(self) -> bool:
|
||||
return self._user_time_estimation_adjusted
|
||||
|
||||
def setPreSliced(self, pre_sliced: bool) -> None:
|
||||
if self._pre_sliced != pre_sliced:
|
||||
self._pre_sliced = pre_sliced
|
||||
|
|
@ -166,6 +174,47 @@ class PrintInformation(QObject):
|
|||
def printTimes(self) -> Dict[str, Duration]:
|
||||
return self._print_times_per_feature[self._active_build_plate]
|
||||
|
||||
def _getTimeEstimationFactor(self) -> Optional[float]:
|
||||
"""Returns the time estimation factor as set by the user in the machine settings, or None if not set.
|
||||
If a factor is found and differs from 1.0 (100%), the userTimeAdjusted property is set to True.
|
||||
This factor is used to adjust the estimated print time received from the backend.
|
||||
Note that the factor is converted from percentage to decimal (e.g., 80% becomes 0.8).
|
||||
"""
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
self._resetUserTimeAdjustment()
|
||||
return None
|
||||
|
||||
factor_value = global_stack.getProperty("machine_time_estimation_factor", "value")
|
||||
if factor_value is None:
|
||||
self._resetUserTimeAdjustment()
|
||||
return None
|
||||
|
||||
try:
|
||||
factor = float(factor_value) / 100.0
|
||||
except (ValueError, TypeError):
|
||||
Logger.warning(f"Invalid time estimation factor value: {factor_value}")
|
||||
self._resetUserTimeAdjustment()
|
||||
return None
|
||||
|
||||
# Check if factor is significantly different from 1.0 to avoid floating point precision issues
|
||||
if abs(factor - 1.0) > 1e-6:
|
||||
self._setUserTimeAdjustment(True)
|
||||
return factor
|
||||
|
||||
self._resetUserTimeAdjustment()
|
||||
return None
|
||||
|
||||
def _setUserTimeAdjustment(self, adjusted: bool) -> None:
|
||||
"""Sets the user time adjustment state and emits signal if changed."""
|
||||
if self._user_time_estimation_adjusted != adjusted:
|
||||
self._user_time_estimation_adjusted = adjusted
|
||||
self.userTimeAdjustedChanged.emit()
|
||||
|
||||
def _resetUserTimeAdjustment(self) -> None:
|
||||
"""Resets the user time adjustment state to False."""
|
||||
self._setUserTimeAdjustment(False)
|
||||
|
||||
def _onPrintDurationMessage(self, build_plate_number: int, print_times_per_feature: Dict[str, int], material_amounts: List[float]) -> None:
|
||||
self._updateTotalPrintTimePerFeature(build_plate_number, print_times_per_feature)
|
||||
self.currentPrintTimeChanged.emit()
|
||||
|
|
@ -175,6 +224,7 @@ class PrintInformation(QObject):
|
|||
|
||||
def _updateTotalPrintTimePerFeature(self, build_plate_number: int, print_times_per_feature: Dict[str, int]) -> None:
|
||||
total_estimated_time = 0
|
||||
time_estimation_factor = self._getTimeEstimationFactor()
|
||||
|
||||
if build_plate_number not in self._print_times_per_feature:
|
||||
self._initPrintTimesPerFeature(build_plate_number)
|
||||
|
|
@ -189,6 +239,9 @@ class PrintInformation(QObject):
|
|||
Logger.warning("Received NaN for print duration message")
|
||||
continue
|
||||
|
||||
if time_estimation_factor is not None:
|
||||
time = int(time * time_estimation_factor)
|
||||
|
||||
total_estimated_time += time
|
||||
duration.setDuration(time)
|
||||
|
||||
|
|
|
|||
28
doc/settings_stacks.puml
Normal file
28
doc/settings_stacks.puml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
@startuml
|
||||
|
||||
ExtruderStack -up-|> CuraContainerStack
|
||||
CuraContainerStack -up-|> ContainerStack
|
||||
ContainerStack -up-|> ContainerInterface
|
||||
|
||||
ContainerStack *-up- "*" ContainerInterface : _containers
|
||||
ContainerStack *-up- "1" ContainerInterface : _next_stack
|
||||
class ContainerStack {
|
||||
<<signal>> propertyChanged
|
||||
<<signal>> propertiesChanged
|
||||
}
|
||||
|
||||
DefinitionContainer -up-|> DefinitionContainerInterface
|
||||
|
||||
DefinitionContainerInterface -up-|> ContainerInterface
|
||||
|
||||
InstanceContainer -up-|> ContainerInterface
|
||||
|
||||
InstanceContainer *-- "1" DefinitionContainerInterface
|
||||
InstanceContainer *-- "*" SettingInstance
|
||||
|
||||
SettingInstance o-- "1" SettingDefinition
|
||||
SettingInstance o-- "1" ContainerInterface
|
||||
|
||||
DefinitionContainer *-- "*" SettingDefinition
|
||||
|
||||
@enduml
|
||||
|
|
@ -157,7 +157,7 @@ class RestoreBackupJob(Job):
|
|||
Logger.info("All packages redownloaded!")
|
||||
self._job_done.set()
|
||||
else:
|
||||
msgs = "\n - ".join(redownload_errors)
|
||||
msgs = "\n".join([" - " + str(x) for x in redownload_errors])
|
||||
Logger.error(f"Couldn't re-install at least one package(s) because: {msgs}")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._backend_log_max_lines: int = 20000 # Maximum number of lines to buffer
|
||||
self._error_message: Optional[Message] = None # Pop-up message that shows errors.
|
||||
self._unused_extruders: list[int] = [] # Extruder numbers of found unused extruders
|
||||
self._required_extruders: list[int] = [] # Extruder numbers of found required but disabled extruders
|
||||
|
||||
# Count number of objects to see if there is something changed
|
||||
self._last_num_objects: Dict[int, int] = defaultdict(int)
|
||||
|
|
@ -558,11 +559,25 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.setState(BackendState.NotStarted)
|
||||
|
||||
if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
|
||||
self._error_message = Message(catalog.i18nc("@info:status",
|
||||
"Unable to slice because there are objects associated with disabled Extruder %s.") % job.getAssociatedDisabledExtruders(),
|
||||
title = catalog.i18nc("@info:title", "Unable to slice"),
|
||||
message_type = Message.MessageType.WARNING)
|
||||
self._required_extruders = job.getAssociatedDisabledExtruders()
|
||||
extruder_names = [self._global_container_stack.extruderList[int(idx)].definition.getName() for idx in
|
||||
self._required_extruders]
|
||||
disabled_extruders = [f"<li>{extruder_name}</li>" for extruder_name in extruder_names]
|
||||
self._error_message = Message(
|
||||
text=catalog.i18nc("@message", "<html>Unable to slice because there are objects associated with at least one disabled extruder:"
|
||||
f"<ul><b>{"".join(disabled_extruders)}</b></ul></html>"),
|
||||
title=catalog.i18nc("@info:title", "Unable to slice"),
|
||||
message_type=Message.MessageType.WARNING
|
||||
)
|
||||
self._error_message.addAction("enable_extruders",
|
||||
name=catalog.i18nc("@button", "Enable required extruder(s)"),
|
||||
icon="",
|
||||
description=catalog.i18nc("@label",
|
||||
"Automatically enable the required extruder(s)")
|
||||
)
|
||||
self._error_message.actionTriggered.connect(self._onMessageActionTriggered)
|
||||
self._error_message.show()
|
||||
|
||||
self.setState(BackendState.Error)
|
||||
self.backendError.emit(job)
|
||||
return
|
||||
|
|
@ -571,7 +586,6 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if application.platformActivity:
|
||||
self._error_message = Message(catalog.i18nc("@info:status", "Please review settings and check if your models:"
|
||||
"\n- Fit within the build volume"
|
||||
"\n- Are assigned to an enabled extruder"
|
||||
"\n- Are not all set as modifier meshes"),
|
||||
title = catalog.i18nc("@info:title", "Unable to slice"),
|
||||
message_type = Message.MessageType.WARNING)
|
||||
|
|
@ -999,6 +1013,10 @@ class CuraEngineBackend(QObject, Backend):
|
|||
message.hide()
|
||||
for unused_extruder in self._unused_extruders:
|
||||
CuraApplication.getInstance().getMachineManager().setExtruderEnabled(unused_extruder, False)
|
||||
elif message_action == "enable_extruders":
|
||||
message.hide()
|
||||
for required_extruder in self._required_extruders:
|
||||
CuraApplication.getInstance().getMachineManager().setExtruderEnabled(required_extruder, True)
|
||||
|
||||
def _parseMessagePrintTimes(self, message: Arcus.PythonMessage) -> Dict[str, float]:
|
||||
"""Called for parsing message to retrieve estimated time per feature
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ class StartSliceJob(Job):
|
|||
self._slice_message: Arcus.PythonMessage = slice_message
|
||||
self._is_cancelled: bool = False
|
||||
self._build_plate_number: Optional[int] = None
|
||||
self._associated_disabled_extruders: Optional[str] = None
|
||||
self._associated_disabled_extruders: List[int] = []
|
||||
|
||||
# cache for all setting values from all stacks (global & extruder) for the current machine
|
||||
self._all_extruders_settings: Optional[Dict[str, Any]] = None
|
||||
|
|
@ -242,7 +242,7 @@ class StartSliceJob(Job):
|
|||
def getSliceMessage(self) -> Arcus.PythonMessage:
|
||||
return self._slice_message
|
||||
|
||||
def getAssociatedDisabledExtruders(self) -> Optional[str]:
|
||||
def getAssociatedDisabledExtruders(self) -> List[int]:
|
||||
return self._associated_disabled_extruders
|
||||
|
||||
def setBuildPlate(self, build_plate_number: int) -> None:
|
||||
|
|
@ -296,11 +296,6 @@ class StartSliceJob(Job):
|
|||
self.setResult(StartJobResult.Error)
|
||||
return
|
||||
|
||||
# Don't slice if there is a setting with an error value.
|
||||
if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
|
||||
self.setResult(StartJobResult.SettingError)
|
||||
return
|
||||
|
||||
if CuraApplication.getInstance().getBuildVolume().hasErrors():
|
||||
self.setResult(StartJobResult.BuildPlateError)
|
||||
return
|
||||
|
|
@ -309,6 +304,7 @@ class StartSliceJob(Job):
|
|||
while CuraApplication.getInstance().getMachineErrorChecker().needToWaitForResult:
|
||||
time.sleep(0.1)
|
||||
|
||||
# Don't slice if there is a setting with an error value.
|
||||
if CuraApplication.getInstance().getMachineErrorChecker().hasError:
|
||||
self.setResult(StartJobResult.SettingError)
|
||||
return
|
||||
|
|
@ -416,7 +412,7 @@ class StartSliceJob(Job):
|
|||
# Only check if the printing extruder is enabled for printing meshes
|
||||
is_non_printing_mesh = node.callDecoration("evaluateIsNonPrintingMesh")
|
||||
if not is_non_printing_mesh:
|
||||
for used_extruder in StartSliceJob._getUsedExtruders(node):
|
||||
for used_extruder in StartSliceJob._getMainExtruders(node):
|
||||
if not extruders_enabled[used_extruder]:
|
||||
skip_group = True
|
||||
has_model_with_disabled_extruders = True
|
||||
|
|
@ -426,8 +422,7 @@ class StartSliceJob(Job):
|
|||
|
||||
if has_model_with_disabled_extruders:
|
||||
self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
|
||||
associated_disabled_extruders = {p + 1 for p in associated_disabled_extruders}
|
||||
self._associated_disabled_extruders = ", ".join(map(str, sorted(associated_disabled_extruders)))
|
||||
self._associated_disabled_extruders = sorted(list(associated_disabled_extruders))
|
||||
return
|
||||
|
||||
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
|
||||
|
|
@ -763,28 +758,11 @@ class StartSliceJob(Job):
|
|||
self._addRelations(relations_set, relation.target.relations)
|
||||
|
||||
@staticmethod
|
||||
def _getUsedExtruders(node: SceneNode) -> List[int]:
|
||||
used_extruders = []
|
||||
|
||||
# Look at extruders used in painted texture
|
||||
node_texture = node.callDecoration("getPaintTexture")
|
||||
texture_data_mapping = node.callDecoration("getTextureDataMapping")
|
||||
if node_texture is not None and texture_data_mapping is not None and "extruder" in texture_data_mapping:
|
||||
texture_image = node_texture.getImage()
|
||||
image_ptr = texture_image.constBits()
|
||||
image_ptr.setsize(texture_image.sizeInBytes())
|
||||
image_size = texture_image.height(), texture_image.width()
|
||||
image_array = numpy.frombuffer(image_ptr, dtype=numpy.uint32).reshape(image_size)
|
||||
|
||||
bit_range_start, bit_range_end = texture_data_mapping["extruder"]
|
||||
full_int32 = 0xffffffff
|
||||
bit_mask = (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> (
|
||||
32 - 1 - bit_range_end))
|
||||
|
||||
used_extruders = (numpy.unique(image_array & bit_mask) >> bit_range_start).tolist()
|
||||
def _getMainExtruders(node: SceneNode) -> List[int]:
|
||||
used_extruders = node.callDecoration("getPaintedExtruders")
|
||||
|
||||
# There is no relevant painting data, just take the extruder associated to the model
|
||||
if not used_extruders:
|
||||
used_extruders = [int(node.callDecoration("getActiveExtruderPosition"))]
|
||||
|
||||
return used_extruders
|
||||
return used_extruders
|
||||
|
|
|
|||
|
|
@ -167,6 +167,23 @@ Item
|
|||
// This has something to do with UM2 and UM2+ regarding "has_material" and the gcode flavor settings.
|
||||
// I don't remember exactly what.
|
||||
afterOnEditingFinishedFunction: manager.updateHasMaterialsMetadata
|
||||
}
|
||||
|
||||
Cura.NumericTextFieldWithUnit // "Print Time Estimation Factor"
|
||||
{
|
||||
id: machineTimeEstimationFactorField
|
||||
containerStackId: machineStackId
|
||||
settingKey: "machine_time_estimation_factor"
|
||||
settingStoreIndex: propertyStoreIndex
|
||||
labelText: catalog.i18nc("@label", "Print Time Estimation Factor")
|
||||
labelFont: base.labelFont
|
||||
labelWidth: base.labelWidth
|
||||
controlWidth: base.controlWidth
|
||||
unitText: catalog.i18nc("@label", "%")
|
||||
decimals: 1
|
||||
minimum: 1
|
||||
maximum: 1000
|
||||
forceUpdateOnChangeFunction: forceUpdateFunction
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class MakerbotWriter(MeshWriter):
|
|||
if not getattr(node, "_outside_buildarea", False):
|
||||
if node.callDecoration(
|
||||
"isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration(
|
||||
"isNonThumbnailVisibleMesh"):
|
||||
"isNonThumbnailVisibleMesh") and not node.callDecoration("isAssignedToDisabledExtruder"):
|
||||
nodes.append(node)
|
||||
|
||||
meta = dict()
|
||||
|
|
|
|||
|
|
@ -109,4 +109,6 @@ class MultiMaterialExtruderConverter:
|
|||
|
||||
texture.updateImagePart(image.rect())
|
||||
|
||||
node.callDecoration("setPaintedExtrudersCountDirty")
|
||||
|
||||
self.mainExtruderChanged.emit(node)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from typing import Optional
|
|||
|
||||
from PyQt6.QtGui import QUndoCommand, QImage, QPainter, QBrush
|
||||
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from UM.View.GL.Texture import Texture
|
||||
|
||||
from .PaintCommand import PaintCommand
|
||||
|
|
@ -13,8 +14,12 @@ from .PaintCommand import PaintCommand
|
|||
class PaintClearCommand(PaintCommand):
|
||||
"""Provides the command that clears all the painting for the current mode"""
|
||||
|
||||
def __init__(self, texture: Texture, bit_range: tuple[int, int], set_value: int) -> None:
|
||||
super().__init__(texture, bit_range)
|
||||
def __init__(self,
|
||||
texture: Texture,
|
||||
bit_range: tuple[int, int],
|
||||
set_value: int,
|
||||
sliceable_object_decorator: Optional[SliceableObjectDecorator] = None) -> None:
|
||||
super().__init__(texture, bit_range, sliceable_object_decorator=sliceable_object_decorator)
|
||||
self._set_value = set_value
|
||||
|
||||
def id(self) -> int:
|
||||
|
|
@ -22,13 +27,12 @@ class PaintClearCommand(PaintCommand):
|
|||
|
||||
def redo(self) -> None:
|
||||
painter = self._makeClearedTexture()
|
||||
|
||||
if self._set_value > 0:
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceOrDestination)
|
||||
painter.fillRect(self._texture.getImage().rect(), QBrush(self._set_value))
|
||||
|
||||
painter.end()
|
||||
|
||||
self._setPaintedExtrudersCountDirty()
|
||||
self._texture.updateImagePart(self._bounding_rect)
|
||||
|
||||
def mergeWith(self, command: QUndoCommand) -> bool:
|
||||
|
|
@ -38,6 +42,6 @@ class PaintClearCommand(PaintCommand):
|
|||
# There is actually nothing more to do here, both clear commands already have the same original texture
|
||||
return True
|
||||
|
||||
def _clearTextureBits(self, painter: QPainter):
|
||||
def _clearTextureBits(self, painter: QPainter, extended = False):
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_NotSourceAndDestination)
|
||||
painter.fillRect(self._texture.getImage().rect(), QBrush(self._getBitRangeMask()))
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Tuple, Optional
|
||||
from typing import Tuple, Optional, Dict
|
||||
|
||||
from PyQt6.QtCore import QRect
|
||||
import numpy
|
||||
from PyQt6.QtGui import QUndoCommand, QImage, QPainter, QBrush
|
||||
|
||||
from UM.View.GL.Texture import Texture
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
|
||||
|
||||
class PaintCommand(QUndoCommand):
|
||||
|
|
@ -14,7 +16,11 @@ class PaintCommand(QUndoCommand):
|
|||
|
||||
FULL_INT32 = 0xffffffff
|
||||
|
||||
def __init__(self, texture: Texture, bit_range: tuple[int, int], make_original_image = True) -> None:
|
||||
def __init__(self,
|
||||
texture: Texture,
|
||||
bit_range: tuple[int, int],
|
||||
make_original_image = True,
|
||||
sliceable_object_decorator: Optional[SliceableObjectDecorator] = None) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._texture: Texture = texture
|
||||
|
|
@ -22,6 +28,8 @@ class PaintCommand(QUndoCommand):
|
|||
self._original_texture_image = None
|
||||
self._bounding_rect = texture.getImage().rect()
|
||||
|
||||
self._sliceable_object_decorator: Optional[SliceableObjectDecorator] = sliceable_object_decorator
|
||||
|
||||
if make_original_image:
|
||||
self._original_texture_image = self._texture.getImage().copy()
|
||||
painter = QPainter(self._original_texture_image)
|
||||
|
|
@ -35,21 +43,26 @@ class PaintCommand(QUndoCommand):
|
|||
if self._original_texture_image is None:
|
||||
return
|
||||
|
||||
painter = self._makeClearedTexture()
|
||||
painter = self._makeClearedTexture(extended=True)
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceOrDestination)
|
||||
painter.drawImage(0, 0, self._original_texture_image)
|
||||
painter.end()
|
||||
|
||||
self._setPaintedExtrudersCountDirty()
|
||||
self._texture.updateImagePart(self._bounding_rect)
|
||||
|
||||
def _makeClearedTexture(self) -> QPainter:
|
||||
def _setPaintedExtrudersCountDirty(self) -> None:
|
||||
if self._sliceable_object_decorator is not None:
|
||||
self._sliceable_object_decorator.setPaintedExtrudersCountDirty()
|
||||
|
||||
def _makeClearedTexture(self, extended = False) -> QPainter:
|
||||
painter = QPainter(self._texture.getImage())
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
|
||||
|
||||
self._clearTextureBits(painter)
|
||||
self._clearTextureBits(painter, extended)
|
||||
return painter
|
||||
|
||||
def _clearTextureBits(self, painter: QPainter):
|
||||
def _clearTextureBits(self, painter: QPainter, extended = False):
|
||||
raise NotImplementedError()
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import math
|
|||
from PyQt6.QtCore import QRect, QRectF, QPoint
|
||||
from PyQt6.QtGui import QUndoCommand, QImage, QPainter, QPainterPath, QPen, QBrush
|
||||
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
from UM.View.GL.Texture import Texture
|
||||
from UM.Math.Polygon import Polygon
|
||||
|
||||
|
|
@ -16,14 +17,16 @@ class PaintStrokeCommand(PaintCommand):
|
|||
"""Provides the command that does the actual painting on objects with undo/redo mechanisms"""
|
||||
|
||||
PEN_OVERLAP_WIDTH = 2.5
|
||||
PEN_OVERLAP_WIDTH_EXTENDED = PEN_OVERLAP_WIDTH + 0.5
|
||||
|
||||
def __init__(self,
|
||||
texture: Texture,
|
||||
stroke_polygons: List[Polygon],
|
||||
set_value: int,
|
||||
bit_range: tuple[int, int],
|
||||
mergeable: bool) -> None:
|
||||
super().__init__(texture, bit_range, make_original_image = not mergeable)
|
||||
mergeable: bool,
|
||||
sliceable_object_decorator: Optional[SliceableObjectDecorator] = None) -> None:
|
||||
super().__init__(texture, bit_range, make_original_image = not mergeable, sliceable_object_decorator=sliceable_object_decorator)
|
||||
self._stroke_polygons: List[Polygon] = stroke_polygons
|
||||
self._calculateBoundingRect()
|
||||
self._set_value: int = set_value
|
||||
|
|
@ -40,6 +43,7 @@ class PaintStrokeCommand(PaintCommand):
|
|||
painter.drawPath(self._makePainterPath())
|
||||
painter.end()
|
||||
|
||||
self._setPaintedExtrudersCountDirty()
|
||||
self._texture.updateImagePart(self._bounding_rect)
|
||||
|
||||
def mergeWith(self, command: QUndoCommand) -> bool:
|
||||
|
|
@ -55,9 +59,9 @@ class PaintStrokeCommand(PaintCommand):
|
|||
|
||||
return True
|
||||
|
||||
def _clearTextureBits(self, painter: QPainter):
|
||||
def _clearTextureBits(self, painter: QPainter, extended = False):
|
||||
painter.setBrush(QBrush(self._getBitRangeMask()))
|
||||
painter.setPen(QPen(painter.brush(), self.PEN_OVERLAP_WIDTH))
|
||||
painter.setPen(QPen(painter.brush(), self.PEN_OVERLAP_WIDTH_EXTENDED if extended else self.PEN_OVERLAP_WIDTH))
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_NotSourceAndDestination)
|
||||
painter.drawPath(self._makePainterPath())
|
||||
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ class PaintTool(Tool):
|
|||
Logger.logException("e", "Error when adding paint stroke")
|
||||
|
||||
self._last_world_coords = world_coords
|
||||
self._updateScene(painted_object, update_node = self._mouse_held)
|
||||
self._updateScene(painted_object, update_node = event_caught)
|
||||
return event_caught
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ Item
|
|||
UM.Label
|
||||
{
|
||||
anchors.fill: parent
|
||||
text: catalog.i18nc("@label", "Select a single model to start painting")
|
||||
text: catalog.i18nc("@label", "Select a single ungrouped model to start painting")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ from UM.View.GL.OpenGL import OpenGL
|
|||
from UM.i18n import i18nCatalog
|
||||
from UM.Math.Color import Color
|
||||
from UM.Math.Polygon import Polygon
|
||||
from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
||||
|
||||
from .PaintStrokeCommand import PaintStrokeCommand
|
||||
from .PaintClearCommand import PaintClearCommand
|
||||
|
|
@ -242,7 +243,14 @@ class PaintView(CuraView):
|
|||
stroke_path,
|
||||
set_value,
|
||||
self._current_bits_ranges,
|
||||
merge_with_previous))
|
||||
merge_with_previous,
|
||||
self._getSliceableObjectDecorator()))
|
||||
|
||||
def _getSliceableObjectDecorator(self) -> Optional[SliceableObjectDecorator]:
|
||||
if self._painted_object is None or self._current_paint_type != "extruder":
|
||||
return None
|
||||
|
||||
return self._painted_object.getDecorator(SliceableObjectDecorator)
|
||||
|
||||
def _makeClearCommand(self) -> Optional[PaintClearCommand]:
|
||||
if self._painted_object is None or self._paint_texture is None or self._current_bits_ranges is None:
|
||||
|
|
@ -254,7 +262,10 @@ class PaintView(CuraView):
|
|||
if extruder_stack is not None:
|
||||
set_value = extruder_stack.getValue("extruder_nr")
|
||||
|
||||
return PaintClearCommand(self._paint_texture, self._current_bits_ranges, set_value)
|
||||
return PaintClearCommand(self._paint_texture,
|
||||
self._current_bits_ranges,
|
||||
self._shiftTextureValue(set_value),
|
||||
self._getSliceableObjectDecorator())
|
||||
|
||||
def clearPaint(self):
|
||||
self._prepareDataMapping()
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "Hold the bed temp at the 'Bed Start Out Temperature' for this amount of time (in decimal hours). When this time expires then the Annealing cool down will start. This is also the 'Drying Time' used when 'Drying Filament'.",
|
||||
"type": "float",
|
||||
"default_value": 0.0,
|
||||
"unit": "Decimal Hrs ",
|
||||
"unit": "Hrs ",
|
||||
"enabled": "enable_script and cycle_type == 'anneal_cycle'"
|
||||
},
|
||||
"dry_time":
|
||||
|
|
@ -100,7 +100,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "Hold the bed temp at the 'Bed Start Out Temperature' for this amount of time (in decimal hours). When this time expires the bed will shut off.",
|
||||
"type": "float",
|
||||
"default_value": 4.0,
|
||||
"unit": "Decimal Hrs ",
|
||||
"unit": "Hrs ",
|
||||
"enabled": "enable_script and cycle_type == 'dry_cycle'"
|
||||
},
|
||||
"pause_cmd":
|
||||
|
|
@ -117,7 +117,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "Enter the temperature to start at. This is typically the bed temperature during the print but can be changed here. This is also the temperature used when drying filament.",
|
||||
"type": "int",
|
||||
"value": 30,
|
||||
"unit": "Degrees ",
|
||||
"unit": "°C/F ",
|
||||
"minimum_value": 30,
|
||||
"maximum_value": 110,
|
||||
"maximum_value_warning": 100,
|
||||
|
|
@ -129,7 +129,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "Enter the lowest temperature to control the cool down. This is the shut-off temperature for the build plate and (when applicable) the Heated Chamber. The minimum value is 30",
|
||||
"type": "int",
|
||||
"default_value": 30,
|
||||
"unit": "Degrees ",
|
||||
"unit": "°C/F ",
|
||||
"minimum_value": 30,
|
||||
"enabled": "enable_script and cycle_type == 'anneal_cycle'"
|
||||
},
|
||||
|
|
@ -139,7 +139,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "Enter the temperature for the Build Volume (Heated Chamber). This is typically the temperature during the print but can be changed here.",
|
||||
"type": "int",
|
||||
"value": 24,
|
||||
"unit": "Degrees ",
|
||||
"unit": "°C/F ",
|
||||
"minimum_value": 0,
|
||||
"maximum_value": 90,
|
||||
"maximum_value_warning": 75,
|
||||
|
|
@ -170,7 +170,7 @@ class AnnealingOrDrying(Script):
|
|||
"description": "The total amount of time (in decimal hours) to control the cool down. The build plate temperature will be dropped in 3° increments across this time span. 'Cool Down Time' starts at the end of the 'Hold Time' if you entered one.",
|
||||
"type": "float",
|
||||
"default_value": 1.0,
|
||||
"unit": "Decimal Hrs ",
|
||||
"unit": "Hrs ",
|
||||
"minimum_value_warning": 0.25,
|
||||
"enabled": "enable_script and cycle_type == 'anneal_cycle'"
|
||||
},
|
||||
|
|
@ -203,7 +203,7 @@ class AnnealingOrDrying(Script):
|
|||
"label": "Beep Duration",
|
||||
"description": "The length of the buzzer sound. Units are in milliseconds so 1000ms = 1 second.",
|
||||
"type": "int",
|
||||
"unit": "milliseconds ",
|
||||
"unit": "msec ",
|
||||
"default_value": 1000,
|
||||
"enabled": "beep_when_done and enable_script"
|
||||
},
|
||||
|
|
@ -471,9 +471,9 @@ class AnnealingOrDrying(Script):
|
|||
|
||||
def _dry_filament_only(
|
||||
self,
|
||||
drydata: str,
|
||||
bed_temperature: int,
|
||||
chamber_temp: int,
|
||||
drydata: str,
|
||||
heated_chamber: bool,
|
||||
heating_zone: str,
|
||||
max_y: str,
|
||||
|
|
@ -562,10 +562,10 @@ class AnnealingOrDrying(Script):
|
|||
back_txt = lines[index].split(";")[1]
|
||||
lines[index] = front_txt + str(" " * (30 - len(front_txt))) +";" + back_txt
|
||||
drydata[1] = "\n".join(lines) + "\n"
|
||||
dry_txt = "; Drying time ...................... " + str(self.getSettingValueByKey("dry_time")) + " hrs\n"
|
||||
dry_txt = "; Drying time ............... " + str(self.getSettingValueByKey("dry_time")) + " hrs\n"
|
||||
dry_txt += "; Drying temperature ........ " + str(bed_temperature) + "°\n"
|
||||
if heated_chamber and heating_zone == "bed_chamber":
|
||||
dry_txt += "; Chamber temperature ... " + str(chamber_temp) + "°\n"
|
||||
dry_txt += "; Chamber temperature ....... " + str(chamber_temp) + "°\n"
|
||||
Message(title = "[Dry Filament]", text = dry_txt).show()
|
||||
drydata[0] = "; <<< This is a filament drying file only. There is no actual print. >>>\n;\n" + dry_txt + ";\n"
|
||||
return drydata
|
||||
|
|
|
|||
|
|
@ -486,9 +486,9 @@ class PurgeLinesAndUnload(Script):
|
|||
data[0] += "; [Purge Lines and Unload] 'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets.\n"
|
||||
Message(title = "[Purge Lines and Unload]", text = "'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets").show()
|
||||
return data
|
||||
|
||||
self.purge_line_hgt = round(float(self.global_stack.getProperty("layer_height_0", "value")),2)
|
||||
def calculate_purge_volume(line_width, purge_length, volume_per_mm):
|
||||
return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5)
|
||||
return round((line_width * self.purge_line_hgt * purge_length) * 1.25 / volume_per_mm, 5)
|
||||
|
||||
def adjust_for_prime_blob_gcode(retract_speed, retract_distance):
|
||||
"""Generates G-code lines for prime blob adjustment."""
|
||||
|
|
@ -500,7 +500,7 @@ class PurgeLinesAndUnload(Script):
|
|||
|
||||
purge_location = self.getSettingValueByKey("purge_line_location")
|
||||
purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False
|
||||
purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n"
|
||||
purge_str = ";---------------------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n"
|
||||
purge_str += self._get_blob_code()
|
||||
# Normal cartesian printer with origin at the left front corner
|
||||
if self.bed_shape == "rectangular" and not self.origin_at_center:
|
||||
|
|
@ -511,7 +511,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str = purge_str.replace("Lines", "Lines at MinX")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -522,7 +522,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
|
|
@ -532,7 +532,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str = purge_str.replace("Lines", "Lines at MaxX")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} ; Move\nG0 Y{self.machine_back - 10} ; Move\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -543,7 +543,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
|
|
@ -554,7 +554,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str = purge_str.replace("Lines", "Lines at MinY")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front + self.border_distance} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -565,7 +565,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
|
|
@ -577,7 +577,7 @@ class PurgeLinesAndUnload(Script):
|
|||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} ; Ortho Move to back\n"
|
||||
purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -588,7 +588,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
# Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center
|
||||
|
|
@ -600,7 +600,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -611,7 +611,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
|
|
@ -621,7 +621,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -632,7 +632,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
|
|
@ -642,7 +642,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front + self.border_distance} Z2 ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -653,7 +653,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
|
|
@ -664,7 +664,7 @@ class PurgeLinesAndUnload(Script):
|
|||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} Z2; Ortho Move to back\n"
|
||||
purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
|
|
@ -675,7 +675,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z{self.purge_line_hgt} ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
# Elliptic printers with Origin at Center
|
||||
|
|
@ -689,7 +689,7 @@ class PurgeLinesAndUnload(Script):
|
|||
if purge_location == Location.LEFT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G2 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
|
|
@ -699,13 +699,13 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z{self.purge_line_hgt} ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
|
|
@ -715,13 +715,13 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z{self.purge_line_hgt} ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
|
|
@ -731,13 +731,13 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z{self.purge_line_hgt} ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
purge_str += f"G0 F600 Z{self.purge_line_hgt} ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
|
|
@ -747,7 +747,7 @@ class PurgeLinesAndUnload(Script):
|
|||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z{self.purge_line_hgt} ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ Item
|
|||
value: sliderRoot.upperValue
|
||||
busy: UM.SimulationView.busy
|
||||
setValue: upperHandle.setValueManually // connect callback functions
|
||||
layerHeight: UM.SimulationView.currentLayerHeight
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,6 +385,7 @@ Item
|
|||
value: sliderRoot.lowerValue
|
||||
busy: UM.SimulationView.busy
|
||||
setValue: lowerHandle.setValueManually // connect callback functions
|
||||
layerHeight: UM.SimulationView.minimumLayerHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,8 @@ class SimulationPass(RenderPass):
|
|||
nozzle_node = node
|
||||
nozzle_node.setVisible(False) # Don't set to true, we render it separately!
|
||||
|
||||
elif getattr(node, "_outside_buildarea", False) and isinstance(node, SceneNode) and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonPrintingMesh"):
|
||||
elif ((getattr(node, "_outside_buildarea", False) or node.callDecoration("isAssignedToDisabledExtruder")) and
|
||||
isinstance(node, SceneNode) and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonPrintingMesh")):
|
||||
disabled_batch.addItem(node.getWorldTransformation(copy=False), node.getMeshData())
|
||||
|
||||
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ UM.PointingRectangle
|
|||
property var setValue // Function
|
||||
property bool busy: false
|
||||
property int startFrom: 1
|
||||
property real layerHeight: 0.0 // Height in mm to display
|
||||
|
||||
target: Qt.point(parent.width, y + height / 2)
|
||||
arrowSize: UM.Theme.getSize("button_tooltip_arrow").height
|
||||
|
|
@ -84,6 +85,29 @@ UM.PointingRectangle
|
|||
bottom: startFrom
|
||||
top: sliderLabelRoot.maximumValue + startFrom // +startFrom because maybe we want to start in a different value rather than 0
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: layerHeightBackground
|
||||
x: -(width + UM.Theme.getSize("narrow_margin").width)
|
||||
y: (parent.height - height) / 2
|
||||
width: layerHeightText.width + 2 * UM.Theme.getSize("narrow_margin").width
|
||||
height: layerHeightText.height + 2 * UM.Theme.getSize("narrow_margin").height
|
||||
color: UM.Theme.getColor("tool_panel_background")
|
||||
radius: UM.Theme.getSize("default_radius").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
|
||||
Text
|
||||
{
|
||||
id: layerHeightText
|
||||
anchors.centerIn: parent
|
||||
text: sliderLabelRoot.layerHeight.toFixed(2) + "mm"
|
||||
color: UM.Theme.getColor("text")
|
||||
font: UM.Theme.getFont("default")
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
}
|
||||
BusyIndicator
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,6 +101,10 @@ class SimulationView(CuraView):
|
|||
self._cumulative_line_duration_layer: Optional[int] = None
|
||||
self._cumulative_line_duration: List[float] = []
|
||||
|
||||
# Cache for layer heights to avoid recalculating on every query
|
||||
self._layer_heights_cache: dict[int, float] = {}
|
||||
self._layer_heights_cache_node_id: Optional[int] = None # Track which node's data is cached
|
||||
|
||||
self._global_container_stack: Optional[ContainerStack] = None
|
||||
self._proxy = None
|
||||
|
||||
|
|
@ -289,6 +293,88 @@ class SimulationView(CuraView):
|
|||
return layer_data.getLayer(self.getCurrentLayer())
|
||||
return None
|
||||
|
||||
def _calculateLayerHeightsCache(self) -> None:
|
||||
"""Calculate and cache heights for all layers.
|
||||
|
||||
This method iterates through all layers once and stores their heights in a cache.
|
||||
Handles both sliced data (microns) and loaded gcode (millimeters).
|
||||
For layer 0 from gcode, uses thickness instead of height due to incorrect Z coordinates.
|
||||
Only recalculates if the layer data source has changed.
|
||||
"""
|
||||
scene = self.getController().getScene()
|
||||
from cura.Scene.GCodeListDecorator import GCodeListDecorator
|
||||
|
||||
for node in DepthFirstIterator(scene.getRoot()): # type: ignore
|
||||
layer_data = node.callDecoration("getLayerData")
|
||||
if not layer_data:
|
||||
continue
|
||||
|
||||
# Check if we already have cached data for this layer_data object
|
||||
# Use id of the layer_data itself, not the node, since node might be reused
|
||||
current_layer_data_id = id(layer_data)
|
||||
if self._layer_heights_cache_node_id == current_layer_data_id and self._layer_heights_cache:
|
||||
# Cache is still valid, no need to recalculate
|
||||
return
|
||||
# Cache is invalid or empty, recalculate
|
||||
self._layer_heights_cache.clear()
|
||||
self._layer_heights_cache_node_id = current_layer_data_id
|
||||
|
||||
has_gcode_decorator = node.getDecorator(GCodeListDecorator) is not None
|
||||
|
||||
# Process all layers at once
|
||||
for layer_id in layer_data.getLayers():
|
||||
layer = layer_data.getLayer(layer_id)
|
||||
if not layer:
|
||||
continue
|
||||
|
||||
# If node has GCodeListDecorator, heights are already in millimeters (from gcode)
|
||||
if has_gcode_decorator:
|
||||
# Special case for layer 0: FlavorParser may get wrong Z coordinate (startup position)
|
||||
# Use thickness instead, which represents the actual layer height
|
||||
if layer_id == 0 and layer.thickness > 0:
|
||||
self._layer_heights_cache[layer_id] = layer.thickness
|
||||
else:
|
||||
self._layer_heights_cache[layer_id] = layer.height
|
||||
# Otherwise, heights are in microns (backend/slicing), convert to mm
|
||||
else:
|
||||
self._layer_heights_cache[layer_id] = layer.height / 1000.0
|
||||
|
||||
# We found layer data and cached it, no need to continue searching
|
||||
return
|
||||
|
||||
# No layer data found - clear the cache
|
||||
if self._layer_heights_cache_node_id is not None:
|
||||
self._layer_heights_cache.clear()
|
||||
self._layer_heights_cache_node_id = None
|
||||
|
||||
def _getLayerHeight(self, layer_number: int) -> float:
|
||||
"""Helper method to get the height of a specific layer in millimeters from cache.
|
||||
|
||||
:param layer_number: The layer number to get the height for.
|
||||
:return: The layer height in millimeters, or 0.0 if no data is available.
|
||||
"""
|
||||
return self._layer_heights_cache.get(layer_number, 0.0)
|
||||
|
||||
def getCurrentLayerHeight(self) -> float:
|
||||
"""Get the height (z-coordinate) of the current layer in millimeters.
|
||||
|
||||
This returns the actual height from the layer data, which already takes into account:
|
||||
- Initial layer height (layer_height_0)
|
||||
- Adaptive layer heights
|
||||
- Regular layer height
|
||||
- Raft layers
|
||||
|
||||
Returns 0.0 if no layer data is available.
|
||||
"""
|
||||
return self._layer_heights_cache.get(self.getCurrentLayer(), 0.0)
|
||||
|
||||
def getMinimumLayerHeight(self) -> float:
|
||||
"""Get the height (z-coordinate) of the minimum layer in millimeters.
|
||||
|
||||
Returns 0.0 if no layer data is available.
|
||||
"""
|
||||
return self._layer_heights_cache.get(self.getMinimumLayer(), 0.0)
|
||||
|
||||
def getMinimumPath(self) -> int:
|
||||
return self._minimum_path_num
|
||||
|
||||
|
|
@ -304,6 +390,7 @@ class SimulationView(CuraView):
|
|||
if node.getMeshData() is None:
|
||||
return
|
||||
self.setActivity(False)
|
||||
self._calculateLayerHeightsCache()
|
||||
self.calculateColorSchemeLimits()
|
||||
self.calculateMaxLayers()
|
||||
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||
|
|
@ -718,6 +805,7 @@ class SimulationView(CuraView):
|
|||
Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
||||
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
|
||||
|
||||
self._calculateLayerHeightsCache()
|
||||
self.calculateColorSchemeLimits()
|
||||
self.calculateMaxLayers()
|
||||
self.calculateMaxPathsOnLayer(self._current_layer_num)
|
||||
|
|
|
|||
|
|
@ -53,17 +53,40 @@ Cura.ExpandableComponent
|
|||
|
||||
UM.Label
|
||||
{
|
||||
id: schemeTypeLabel
|
||||
text: layerTypeCombobox.currentText
|
||||
anchors
|
||||
{
|
||||
left: colorSchemeLabel.right
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
right: parent.right
|
||||
}
|
||||
anchors.left: colorSchemeLabel.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
height: parent.height
|
||||
elide: Text.ElideRight
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
|
||||
UM.ColorImage
|
||||
{
|
||||
id: warningIcon
|
||||
anchors
|
||||
{
|
||||
left: schemeTypeLabel.right
|
||||
leftMargin: UM.Theme.getSize("narrow_margin").width
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
width: UM.Theme.getSize("section_icon").width
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
source: UM.Theme.getIcon("Warning")
|
||||
color: UM.Theme.getColor("warning")
|
||||
visible: {
|
||||
// Check if any enabled extruder is unchecked
|
||||
var extrudersModel = CuraApplication.getExtrudersModel();
|
||||
for (var i = 0; i < extrudersModel.count; i++) {
|
||||
var extruder = extrudersModel.getItem(i);
|
||||
if (extruder.enabled && viewSettings.extruder_opacities[i] <= 0.5 && viewSettings.extruder_opacities[i] !== undefined && viewSettings.extruder_opacities[i] !== "") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Column
|
||||
|
|
@ -212,11 +235,27 @@ Cura.ExpandableComponent
|
|||
{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: extrudersModelCheckBox.left
|
||||
right: extrudersModelCheckBox.right
|
||||
right: extruderWarningIcon.visible ? extruderWarningIcon.left : swatch.left
|
||||
leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2)
|
||||
rightMargin: UM.Theme.getSize("default_margin").width * 2
|
||||
rightMargin: UM.Theme.getSize("narrow_margin").width
|
||||
}
|
||||
}
|
||||
|
||||
UM.ColorImage
|
||||
{
|
||||
id: extruderWarningIcon
|
||||
anchors
|
||||
{
|
||||
verticalCenter: parent.verticalCenter
|
||||
right: swatch.left
|
||||
rightMargin: UM.Theme.getSize("narrow_margin").width
|
||||
}
|
||||
width: UM.Theme.getSize("section_icon").width
|
||||
height: UM.Theme.getSize("section_icon").height
|
||||
source: UM.Theme.getIcon("Warning")
|
||||
color: UM.Theme.getColor("warning")
|
||||
visible: model.enabled && !extrudersModelCheckBox.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@ class SimulationViewProxy(QObject):
|
|||
def minimumLayer(self):
|
||||
return self._simulation_view.getMinimumLayer()
|
||||
|
||||
@pyqtProperty(float, notify=currentLayerChanged)
|
||||
def currentLayerHeight(self):
|
||||
return self._simulation_view.getCurrentLayerHeight()
|
||||
|
||||
@pyqtProperty(float, notify=currentLayerChanged)
|
||||
def minimumLayerHeight(self):
|
||||
return self._simulation_view.getMinimumLayerHeight()
|
||||
|
||||
@pyqtProperty(int, notify=maxPathsChanged)
|
||||
def numPaths(self):
|
||||
return self._simulation_view.getMaxPaths()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "Simulation View",
|
||||
"author": "Ultimaker B.V.",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"description": "Provides the preview of sliced layerdata.",
|
||||
"api": 8,
|
||||
"i18n-catalog": "cura"
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ class SolidView(View):
|
|||
renderer.queueNode(node, shader = self._non_printing_shader, uniforms = uniforms, transparent = True)
|
||||
else:
|
||||
renderer.queueNode(node, shader = self._non_printing_shader, transparent = True)
|
||||
elif getattr(node, "_outside_buildarea", False):
|
||||
elif getattr(node, "_outside_buildarea", False) or node.callDecoration("isAssignedToDisabledExtruder"):
|
||||
disabled_batch.addItem(node.getWorldTransformation(copy = False), node.getMeshData(), normal_transformation = node.getCachedNormalMatrix())
|
||||
elif per_mesh_stack and node.callDecoration("isSupportMesh"):
|
||||
# Render support meshes with a vertical stripe that is darker
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Message import Message
|
||||
|
||||
|
||||
class AuthorizationRequiredMessage:
|
||||
|
||||
_inner_message_instance = None
|
||||
class InnerMessage(Message):
|
||||
def __init__(self, printer_name: str, err_message: str) -> None:
|
||||
super().__init__(
|
||||
text = printer_name,
|
||||
title = err_message,
|
||||
message_type = Message.MessageType.WARNING,
|
||||
lifetime = 0
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _getInstance(cls) -> Message:
|
||||
if cls._inner_message_instance is None:
|
||||
cls._inner_message_instance = cls.InnerMessage("", "")
|
||||
return cls._inner_message_instance
|
||||
|
||||
@classmethod
|
||||
def show(cls, printer_name: str, err_message: str) -> None:
|
||||
msg = cls._getInstance()
|
||||
msg.setText(printer_name)
|
||||
msg.setTitle(err_message)
|
||||
msg.show()
|
||||
|
||||
@classmethod
|
||||
def hide(cls) -> None:
|
||||
cls._getInstance().hide()
|
||||
|
|
@ -16,6 +16,7 @@ from UM.Logger import Logger
|
|||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage
|
||||
from ..Models.BaseModel import BaseModel
|
||||
from ..Models.Http.ClusterPrintJobStatus import ClusterPrintJobStatus
|
||||
from ..Models.Http.ClusterPrinterStatus import ClusterPrinterStatus
|
||||
|
|
@ -56,7 +57,7 @@ class ClusterApiClient:
|
|||
# In order to avoid garbage collection we keep the callbacks in this list.
|
||||
_anti_gc_callbacks = [] # type: List[Callable[[], None]]
|
||||
|
||||
def __init__(self, address: str, on_error: Callable) -> None:
|
||||
def __init__(self, address: str, on_error: Callable, on_auth_required: Callable) -> None:
|
||||
"""Initializes a new cluster API client.
|
||||
|
||||
:param address: The network address of the cluster to call.
|
||||
|
|
@ -68,6 +69,7 @@ class ClusterApiClient:
|
|||
self._on_error = on_error
|
||||
|
||||
self._auth_tries = 0
|
||||
self._on_auth_required = on_auth_required
|
||||
|
||||
prefs = CuraApplication.getInstance().getPreferences()
|
||||
prefs.addPreference("cluster_api/auth_ids", "{}")
|
||||
|
|
@ -302,6 +304,10 @@ class ClusterApiClient:
|
|||
|
||||
if reply.error() != QNetworkReply.NetworkError.NoError:
|
||||
if reply.error() == QNetworkReply.NetworkError.AuthenticationRequiredError:
|
||||
self._auth_id = None
|
||||
self._auth_key = None
|
||||
|
||||
self._on_auth_required(reply.errorString())
|
||||
nonce_match = re.search(r'nonce="([^"]+)', str(reply.rawHeader(b"WWW-Authenticate")))
|
||||
if nonce_match:
|
||||
self._nonce = nonce_match.group(1)
|
||||
|
|
@ -309,9 +315,13 @@ class ClusterApiClient:
|
|||
self._setLocalValueToPrefDict("cluster_api/nonce_counts", self._nonce_count)
|
||||
self._setLocalValueToPrefDict("cluster_api/nonces", self._nonce)
|
||||
CuraApplication.getInstance().savePreferences()
|
||||
self._on_error(reply.errorString())
|
||||
else:
|
||||
self._on_error(reply.errorString())
|
||||
return
|
||||
|
||||
if self._auth_id and self._auth_key and self._nonce_count > 1:
|
||||
AuthorizationRequiredMessage.hide()
|
||||
|
||||
# If no parse model is given, simply return the raw data in the callback.
|
||||
if not model:
|
||||
on_finished(reply.readAll())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import os
|
||||
import platform
|
||||
from typing import Optional, Dict, List, Callable, Any
|
||||
|
||||
from time import time
|
||||
|
|
@ -21,6 +22,7 @@ from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
|||
from .ClusterApiClient import ClusterApiClient
|
||||
from .SendMaterialJob import SendMaterialJob
|
||||
from ..ExportFileJob import ExportFileJob
|
||||
from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage
|
||||
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
|
||||
from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage
|
||||
from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage
|
||||
|
|
@ -194,7 +196,7 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
|||
return
|
||||
self._progress.show()
|
||||
parts = [
|
||||
self._createFormPart("name=owner", bytes(self._getUserName(), "utf-8"), "text/plain"),
|
||||
self._createFormPart("name=owner", bytes(f"user@{platform.node()}", "utf-8"), "text/plain"),
|
||||
self._createFormPart("name=\"file\"; filename=\"%s\"" % self._active_exported_job.getFileName(),
|
||||
self._active_exported_job.getOutput())
|
||||
]
|
||||
|
|
@ -239,9 +241,19 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
|||
if print_job.getPreviewImage() is None:
|
||||
self.getApiClient().getPrintJobPreviewImage(print_job.key, print_job.updatePreviewImageData)
|
||||
|
||||
def _onAuthRequired(self, error_msg: str) -> None:
|
||||
active_name = CuraApplication.getInstance().getOutputDeviceManager().getActiveDevice().getName()
|
||||
if self._name == active_name:
|
||||
Logger.info(f"Authorization required for {self._name}: {error_msg}")
|
||||
AuthorizationRequiredMessage.show(self._name, error_msg)
|
||||
|
||||
def getApiClient(self) -> ClusterApiClient:
|
||||
"""Get the API client instance."""
|
||||
|
||||
if not self._cluster_api:
|
||||
self._cluster_api = ClusterApiClient(self.address, on_error = lambda error: Logger.log("e", str(error)))
|
||||
self._cluster_api = ClusterApiClient(
|
||||
self._address,
|
||||
on_error = lambda error: Logger.log("e", str(error)),
|
||||
on_auth_required = self._onAuthRequired
|
||||
)
|
||||
return self._cluster_api
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from cura.Settings.GlobalStack import GlobalStack
|
|||
from .ZeroConfClient import ZeroConfClient
|
||||
from .ClusterApiClient import ClusterApiClient
|
||||
from .LocalClusterOutputDevice import LocalClusterOutputDevice
|
||||
from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage
|
||||
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
|
||||
from ..Messages.CloudFlowMessage import CloudFlowMessage
|
||||
from ..Messages.LegacyDeviceNoLongerSupportedMessage import LegacyDeviceNoLongerSupportedMessage
|
||||
|
|
@ -44,6 +45,7 @@ class LocalClusterOutputDeviceManager:
|
|||
# Persistent dict containing the networked clusters.
|
||||
self._discovered_devices = {} # type: Dict[str, LocalClusterOutputDevice]
|
||||
self._output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
self._output_device_manager.activeDeviceChanged.connect(self._onActiveDeviceChanged)
|
||||
|
||||
# Hook up ZeroConf client.
|
||||
self._zero_conf_client = ZeroConfClient()
|
||||
|
|
@ -69,10 +71,17 @@ class LocalClusterOutputDeviceManager:
|
|||
self.stop()
|
||||
self.start()
|
||||
|
||||
def _onActiveDeviceChanged(self):
|
||||
AuthorizationRequiredMessage.hide()
|
||||
|
||||
def _onAuthRequired(self, error_msg: str) -> None:
|
||||
Logger.info(f"Authorization required: {error_msg}")
|
||||
AuthorizationRequiredMessage.show("", error_msg)
|
||||
|
||||
def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None:
|
||||
"""Add a networked printer manually by address."""
|
||||
|
||||
api_client = ClusterApiClient(address, lambda error: Logger.log("e", str(error)))
|
||||
api_client = ClusterApiClient(address, lambda error: Logger.log("e", str(error)), self._onAuthRequired)
|
||||
api_client.getSystem(lambda status: self._onCheckManualDeviceResponse(address, status, callback))
|
||||
|
||||
def removeManualDevice(self, device_id: str, address: Optional[str] = None) -> None:
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
"machine_acceleration": { "value": 3000 },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_depth": { "default_value": 210 },
|
||||
"machine_end_gcode": { "default_value": "M104 S0 ; Extruder off \nM140 S0 ; Heatbed off \nM107 ; Fan off \nG91 ; relative positioning \nG1 E-5 F300 ; retract a little \nG1 Z+10 E-5 ; X-20 Y-20 F{travel_xy_speed} ; lift print head \nG28 X0 Y0 ; homing \nG1 Y180 F2000 ; reset feedrate \nM84 ; disable stepper motors \nG90 ; absolute positioning \nM300 S440 P200 ; Make Print Completed Tones \nM300 S660 P250 ; beep \nM300 S880 P300 ; beep" },
|
||||
"machine_end_gcode": { "default_value": "M104 S0 ; Extruder off \nM140 S0 ; Heatbed off \nM107 ; Fan off \nG91 ; relative positioning \nG1 E-5 F300 ; retract a little \nG1 Z+10 E-5 ; X-20 Y-20 F{speed_travel} ; lift print head \nG28 X0 Y0 ; homing \nG1 Y180 F2000 ; reset feedrate \nM84 ; disable stepper motors \nG90 ; absolute positioning \nM300 S440 P200 ; Make Print Completed Tones \nM300 S660 P250 ; beep \nM300 S880 P300 ; beep" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_height": { "default_value": 205 },
|
||||
|
|
|
|||
|
|
@ -486,6 +486,19 @@
|
|||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_time_estimation_factor":
|
||||
{
|
||||
"label": "Print Time Estimation Factor",
|
||||
"description": "Multiplier for adjusting print time estimates. 100% shows original estimate. Higher values indicate longer actual print times.",
|
||||
"default_value": 100,
|
||||
"minimum_value": 1,
|
||||
"maximum_value": 1000,
|
||||
"type": "float",
|
||||
"unit": "%",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"machine_nozzle_id":
|
||||
{
|
||||
"label": "Nozzle ID",
|
||||
|
|
@ -1543,15 +1556,15 @@
|
|||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"roofing_extension":
|
||||
"roofing_expansion":
|
||||
{
|
||||
"label": "Top Surface Extension",
|
||||
"description": "Determines how much the top surfaces are extended beneath overlapping surfaces. By adjusting this value, you can ensure that the outer edges of the top surfaces are concealed by the layers above, resulting in a better visual quality, particularly for models with curved surfaces.",
|
||||
"label": "Top Surface Expansion",
|
||||
"description": "Determines how much the top surfaces are expanded beneath overlapping surfaces. By adjusting this value, you can ensure that the outer edges of the top surfaces are concealed by the layers above, resulting in a better visual quality, particularly for models with curved surfaces.",
|
||||
"type": "float",
|
||||
"default_value": "0",
|
||||
"value": "0",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "roofing_line_width * 10",
|
||||
"maximum_value_warning": "roofing_line_width * 3",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
|
|
@ -2554,6 +2567,53 @@
|
|||
"value": "lightning_infill_support_angle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"infill_move_inwards_length":
|
||||
{
|
||||
"label": "Infill Start/End Move Inwards Length",
|
||||
"description": "When starting or ending infill print, add an inwards extrusion move so that the tips of the infill won't impact the outer wall. This can be useful when the infill is printed at very high speed.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "line_width * 10",
|
||||
"default_value": 0,
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"enabled": "infill_sparse_density > 0",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
{
|
||||
"infill_start_move_inwards_length":
|
||||
{
|
||||
"label": "Infill Start Move Inwards Length",
|
||||
"description": "When starting infill print, add an inwards extrusion move so that the tips of the infill won't impact the outer wall. This can be useful when the infill is printed at very high speed.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "line_width * 10",
|
||||
"default_value": 0,
|
||||
"value": "infill_move_inwards_length",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"enabled": "infill_sparse_density > 0",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"infill_end_move_inwards_length":
|
||||
{
|
||||
"label": "Infill End Move Inwards Length",
|
||||
"description": "When starting or ending infill print, add an inwards extrusion move so that the tips of the infill won't impact the outer wall. This can be useful when the infill is printed at very high speed.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "line_width * 10",
|
||||
"default_value": 0,
|
||||
"value": "infill_move_inwards_length",
|
||||
"limit_to_extruder": "infill_extruder_nr",
|
||||
"enabled": "infill_sparse_density > 0",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -7939,13 +7999,13 @@
|
|||
"minimum_value": "0.05",
|
||||
"maximum_value": "5",
|
||||
"maximum_value_warning": "line_width * 2",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"multi_material_paint_deepness":
|
||||
"multi_material_paint_depth":
|
||||
{
|
||||
"label": "Multi-material Deepness",
|
||||
"description": "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary.",
|
||||
"label": "Multi-material Depth",
|
||||
"description": "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
|
|
@ -7953,8 +8013,8 @@
|
|||
"value": "line_width * 10",
|
||||
"minimum_value": "line_width",
|
||||
"minimum_value_warning": "line_width * 2",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -8964,6 +9024,15 @@
|
|||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_interlace_lines":
|
||||
{
|
||||
"label": "Interlace Bridge Lines",
|
||||
"description": "When enabled, bridging lines will be printed interlaced, i.e. in 2 monotonic passes so that adjacent lines won't be printed just after each other. The lines of the first pass then have more time to cool down and are stronger when the second pass is added.",
|
||||
"default_value": false,
|
||||
"type": "bool",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_enable_more_layers":
|
||||
{
|
||||
"label": "Bridge Has Multiple Layers",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Toybox Alpha One/Two",
|
||||
"name": "Toybox Alpha One/Two/Three",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata":
|
||||
{
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
"machine_depth": { "default_value": 80 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_height": { "default_value": 90 },
|
||||
"machine_name": { "default_value": "Toybox Alpha One/Two" },
|
||||
"machine_name": { "default_value": "Toybox Alpha One/Two/Three" },
|
||||
"machine_start_gcode": { "default_value": "G90\nM82" },
|
||||
"machine_width": { "default_value": 70 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
|
|
|
|||
|
|
@ -240,11 +240,12 @@
|
|||
"gradual_flow_enabled": { "value": true },
|
||||
"hole_xy_offset": { "value": 0.075 },
|
||||
"infill_material_flow": { "value": "material_flow if infill_sparse_density < 95 else 95" },
|
||||
"infill_move_inwards_length": { "value": "3*machine_nozzle_size" },
|
||||
"infill_overlap": { "value": 10 },
|
||||
"infill_pattern": { "value": "'zigzag' if infill_sparse_density > 50 else 'grid'" },
|
||||
"infill_sparse_density": { "value": 15 },
|
||||
"infill_wall_line_count": { "value": "1 if infill_sparse_density > 80 else 0" },
|
||||
"initial_bottom_layers": { "value": "2 if extruderValueFromContainer(extruder_nr, 'bottom_layers', 2) == bottom_layers else bottom_layers" },
|
||||
"initial_bottom_layers": { "value": "2 if extruderValueFromContainer(top_bottom_extruder_nr, 'bottom_layers', 2) == bottom_layers else bottom_layers" },
|
||||
"jerk_flooring":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
|
|
@ -432,6 +433,11 @@
|
|||
"max_flow_acceleration": { "value": 1.5 },
|
||||
"max_skin_angle_for_expansion": { "value": 45 },
|
||||
"meshfix_maximum_resolution": { "value": 0.4 },
|
||||
"min_bead_width":
|
||||
{
|
||||
"maximum_value_warning": "machine_nozzle_size * 1.25",
|
||||
"value": "machine_nozzle_size * 1.15"
|
||||
},
|
||||
"min_infill_area": { "default_value": 10 },
|
||||
"optimize_wall_printing_order": { "value": false },
|
||||
"prime_during_travel_ratio": { "enabled": false },
|
||||
|
|
@ -446,7 +452,7 @@
|
|||
"retraction_hop_after_extruder_switch_height": { "value": 2 },
|
||||
"retraction_hop_enabled": { "value": true },
|
||||
"retraction_min_travel": { "value": "2.5 if support_enable and support_structure=='tree' else line_width * 2.5" },
|
||||
"roofing_extension": { "value": 1.2 },
|
||||
"roofing_expansion": { "value": 1.2 },
|
||||
"roofing_pattern": { "value": "'lines'" },
|
||||
"seam_overhang_angle": { "value": 35 },
|
||||
"skin_edge_support_thickness": { "value": 0.8 },
|
||||
|
|
@ -607,6 +613,7 @@
|
|||
"support_xy_distance": { "value": 1.2 },
|
||||
"support_xy_distance_overhang": { "value": "1.5*machine_nozzle_size" },
|
||||
"support_z_distance": { "value": "2*layer_height" },
|
||||
"switch_extruder_retraction_amount": { "value": 16 },
|
||||
"top_bottom_thickness": { "value": "wall_thickness" },
|
||||
"travel_avoid_other_parts": { "value": true },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@
|
|||
"cool_fan_speed_0": { "value": 0 },
|
||||
"cool_min_layer_time": { "value": 8 },
|
||||
"extruder_prime_pos_abs": { "default_value": true },
|
||||
"fill_outline_gaps": { "value": false },
|
||||
"fill_outline_gaps": { "value": true },
|
||||
"gantry_height": { "value": "60" },
|
||||
"infill_angles": { "value": "[45,45,45,45,45,135,135,135,135,135]" },
|
||||
"infill_before_walls": { "value": false },
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
"enabled": false,
|
||||
"value": false
|
||||
},
|
||||
"layer_height_0": { "value": "layer_height * 1.25" },
|
||||
"layer_height_0": { "value": "layer_height * 2" },
|
||||
"layer_start_x": { "value": "sum(extruderValues('machine_extruder_start_pos_x')) / len(extruderValues('machine_extruder_start_pos_x'))" },
|
||||
"layer_start_y": { "value": "sum(extruderValues('machine_extruder_start_pos_y')) / len(extruderValues('machine_extruder_start_pos_y'))" },
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
|
|
@ -146,7 +146,7 @@
|
|||
{
|
||||
"minimum_value": "line_width * 0.5",
|
||||
"minimum_value_warning": "line_width * 0.75",
|
||||
"value": "line_width"
|
||||
"value": "line_width * 1.15"
|
||||
},
|
||||
"min_wall_line_width":
|
||||
{
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
"maximum_value": 260,
|
||||
"maximum_value_warning": 240
|
||||
},
|
||||
"min_bead_width": { "value": 0.3 },
|
||||
"min_bead_width": { "value": "line_width * 1.15" },
|
||||
"multiple_mesh_overlap": { "value": "0" },
|
||||
"print_sequence": { "enabled": false },
|
||||
"raft_airgap": { "value": 0.35 },
|
||||
|
|
|
|||
|
|
@ -3151,12 +3151,10 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
"Zkontrolujte nastavení a zda vaše modely:\n"
|
||||
"- Vejdou se na pracovní prostor\n"
|
||||
"- Jsou přiřazeny k povolenému extruderu\n"
|
||||
"- Nejsou nastavené jako modifikační sítě"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -2565,8 +2565,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr "Monotónní pořadí horních / dolních povrchů"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4329,8 +4329,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
|
|
@ -3002,7 +3002,6 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -3035,10 +3035,9 @@ msgid "Please remove the print"
|
|||
msgstr "Bitte den Ausdruck entfernen"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Bitte überprüfen Sie die Einstellungen und prüfen Sie, ob Ihre Modelle:"
|
||||
"- Mit der Druckraumgröße kompatibel sind"
|
||||
"- Einem aktiven Extruder zugewiesen sind"
|
||||
"- Nicht alle als Modifier Meshes eingerichtet sind"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Die maximale Flow Rate, mit der der Drucker mit dem verwendeten Material extrudieren kann"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Multi-Material-Tiefe"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Erste Schicht scannen"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "Die Tiefe der bemalten Details innerhalb des Modells. Eine höhere Tiefe ermöglicht eine bessere Verzahnung, steigert jedoch die Slicing-Zeit und den Speicherbedarf. Wählen Sie einen sehr hohen Wert, um eine möglichst große Tiefe zu erreichen. Die tatsächlich berechnete Tiefe kann variieren."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3036,10 +3036,9 @@ msgid "Please remove the print"
|
|||
msgstr "Retire la impresión"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Revise la configuración y compruebe si sus modelos:"
|
||||
"- Se integran en el volumen de impresión"
|
||||
"- Están asignados a un extrusor activado"
|
||||
"- No están todos definidos como mallas modificadoras"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Tasa de flujo máximo que la impresora puede extruir para el material"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Profundidad multimaterial"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Escanear la primera capa"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "La profundidad de los detalles pintados dentro del modelo. Una profundidad más alta proporcionará un mejor enclavamiento, pero aumentará el tiempo de trozeado y la memoria. Establezca un valor muy alto para que vaya lo más profundo posible. La profundidad calculada exactamente puede variar."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -5104,12 +5104,12 @@ msgctxt "multi_material_paint_resolution description"
|
|||
msgid "The precision of the details when generating multi-material shapes based on painting data. A lower precision will provide more details, but increase the slicing time and memory."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "meshfix label"
|
||||
|
|
|
|||
|
|
@ -3121,7 +3121,6 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2560,8 +2560,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4324,8 +4324,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
|
|
@ -3034,10 +3034,9 @@ msgid "Please remove the print"
|
|||
msgstr "Supprimez l'imprimante"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Veuillez vérifier les paramètres et si vos modèles:"
|
||||
"- S'intègrent dans le volume de fabrication"
|
||||
"- Sont affectés à un extrudeur activé"
|
||||
"- N sont pas tous définis comme des mailles de modificateur"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Débit maximum que l'imprimante peut extruder pour le matériau."
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Profondeur multi-matériaux"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Numériser la première couche"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "La profondeur des détails peints à l'intérieur du modèle. Une plus grande profondeur permettra un meilleur emboîtement, mais augmentera le temps de découpage et la mémoire. Définissez une valeur très élevée pour aller aussi loin que possible. La profondeur réellement calculée peut varier."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3135,7 +3135,6 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2567,8 +2567,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4331,8 +4331,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
|
|
@ -3036,10 +3036,9 @@ msgid "Please remove the print"
|
|||
msgstr "Rimuovere la stampa"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Verificare le impostazioni e controllare se i modelli:"
|
||||
"- Rientrano nel volume di stampa"
|
||||
"- Sono assegnati a un estrusore abilitato"
|
||||
"- Non sono tutti impostati come maglie modificatore"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6290,8 +6290,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Il flusso massimo estrudibile per il materiale"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Profondità multimateriale"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6310,8 +6310,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Scansiona il primo strato"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "La profondità del dettaglio dipinto nel modello. Una profondità maggiore fornirà un incastro migliore ma aumenterà il tempo di slicing e la memoria utilizzata. Imposta un valore molto alto per andare più possibile in profondità. La profondità reale calcolata può variare."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3139,12 +3139,10 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
"設定を見直し、モデルが次の状態かどうかを確認してください。\n"
|
||||
"- 造形サイズに合っている\n"
|
||||
"- 有効なエクストルーダーに割り当てられている\n"
|
||||
"- すべてのセットが修飾子メッシュとして設定されていない"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -2560,8 +2560,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr "上面/底面の方向一貫性"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "複数材料の深さ"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4324,8 +4324,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr "使用されている材料のブランドです。"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "ペイントした部分に対するモデル内部の深さです。より深くすることでより強く結合しますが、メモリー使用量とスライスにかかる時間が増加します。出来るだけ深くなるよう高い値を設定してください。実際に計算される深さは変わる場合があります。"
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
|
|
@ -3028,10 +3028,9 @@ msgid "Please remove the print"
|
|||
msgstr "프린트물을 제거하십시오"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "설정을 검토하고 모델이 다음 사항에 해당하는지 확인하십시오."
|
||||
"- 출력 사이즈 내에 맞춤화됨"
|
||||
"- 활성화된 익스트루더로 할당됨"
|
||||
"- 수정자 메쉬로 전체 설정되지 않음"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "프린터가 재료를 압출할 때 허용할 최대 유량"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "다중 재료 깊이"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "첫 레이어 스캔"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "모델 내부에 페인트되는 디테일의 깊이. 깊이 값이 높을수록 맞물림 정도가 향상되지만, 슬라이싱 시간과 메모리는 늘어납니다. 깊이를 최대한으로 표현하려면 아주 높은 값을 설정하세요. 실제 계산되는 깊이는 다를 수 있습니다."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3034,10 +3034,9 @@ msgid "Please remove the print"
|
|||
msgstr "Verwijder de print"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Controleer de instellingen en zorg ervoor dat uw modellen:"
|
||||
"- binnen het werkvolume passen"
|
||||
"- zijn toegewezen aan een ingeschakelde extruder"
|
||||
"- niet allemaal zijn ingesteld als modificatierasters"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Maximale stroomsnelheid die de printer voor het materiaal kan extruderen"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Multi-materiële diepte"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Scan de eerste laag"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "De diepte van de geverfde details in het model. Een hogere diepte zorgt voor een betere interlocking, maar verhoogt de snijtijd en het geheugen. Stel een zeer hoge waarde in om zo diep mogelijk te gaan. De werkelijk berekende diepte kan variëren."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3138,7 +3138,6 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2566,8 +2566,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4330,8 +4330,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3037,10 +3037,9 @@ msgid "Please remove the print"
|
|||
msgstr "Remova a impressão"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Reveja as definições e verifique se os seus modelos:"
|
||||
"- Cabem dentro do volume de construção"
|
||||
"- Estão atribuídos a uma extrusora ativada"
|
||||
"- Não estão todos definidos como objetos modificadores"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6290,8 +6290,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "A taxa de fluxo máxima que a impressora pode extrudir para o material"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Profundidade em múltiplos materiais"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6310,8 +6310,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Fazer a varredura da primeira camada"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "A profundidade dos detalhes pintados no interior do modelo. Uma profundidade maior proporcionará uma melhor interligação, mas aumentará o tempo de corte e a memória. Defina um valor muito alto para obter a maior profundidade possível. A profundidade calculada efetiva pode variar."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3048,10 +3048,9 @@ msgid "Please remove the print"
|
|||
msgstr "Пожалуйста, удалите напечатанное"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Проверьте настройки и убедитесь в том, что ваши модели:"
|
||||
"- соответствуют допустимой области печати"
|
||||
"- назначены активированному экструдеру"
|
||||
"- не заданы как объекты-модификаторы"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Максимальная скорость подачи, с которой принтер может выдавливать материал"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Мультиматериальная глубина"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "Сканировать первый слой"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "Глубина покрашенных деталей внутри модели. Чем больше глубина, тем лучше сцепление, но при этом увеличивается время нарезки и объем памяти. Установите очень высокое значение, чтобы глубина была максимальной. Фактически рассчитанная глубина может отличаться."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3036,10 +3036,9 @@ msgid "Please remove the print"
|
|||
msgstr "Lütfen yazıcıyı çıkarın"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "Lütfen ayarları gözden geçirin ve modellerinizi şu durumlara karşı kontrol edin:"
|
||||
"- Yapı hacmine sığma"
|
||||
"- Etkin bir ekstrüdere atanma"
|
||||
"- Değiştirici kafesler olarak ayarlanmama"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "Materyal için yazıcının sıkabileceği maksimum akış oranı"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "Çoklu Materyal Derinliği"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "İlk katmanı tara"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "Model içerisindeki boyalı detayların derinliği. Daha yüksek bir derinlik, daha iyi bir kenetlenme sağlar, fakat dilimleme süresini ve bellek kullanımını artırır. Mümkün olduğunca derinleştirmek için çok yüksek bir değer seçin. Asıl hesaplanan derinlik farklılık gösterebilir."
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3030,10 +3030,9 @@ msgid "Please remove the print"
|
|||
msgstr "请取出打印件"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are assigned to an enabled extruder\n- Are not all set as modifier meshes"
|
||||
msgid "Please review settings and check if your models:\n- Fit within the build volume\n- Are not all set as modifier meshes"
|
||||
msgstr "请检查设置并检查您的模型是否:"
|
||||
"- 适合构建体积"
|
||||
"- 分配给了已启用的挤出器"
|
||||
"- 尚未全部设置为修改器网格"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -6289,8 +6289,8 @@ msgctxt "material_max_flowrate description"
|
|||
msgid "Maximum flow rate that the printer can extrude for the material"
|
||||
msgstr "打印机可挤出该材料的最大流量率"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr "多材料打印深度"
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -6309,8 +6309,8 @@ msgctxt "machine_scan_first_layer label"
|
|||
msgid "Scan the first layer"
|
||||
msgstr "扫描首层"
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr "模型内部喷涂细节的深度。较高的深度值可提供更好的咬合效果,但会增加切片时间和内存占用。设置极高的数值可实现最大深度(实际计算深度可能存在浮动)。"
|
||||
|
||||
msgctxt "build_volume_fan_speed description"
|
||||
|
|
|
|||
|
|
@ -3135,12 +3135,10 @@ msgctxt "@info:status"
|
|||
msgid ""
|
||||
"Please review settings and check if your models:\n"
|
||||
"- Fit within the build volume\n"
|
||||
"- Are assigned to an enabled extruder\n"
|
||||
"- Are not all set as modifier meshes"
|
||||
msgstr ""
|
||||
"請檢查設定並檢查你的模型是否:\n"
|
||||
"- 適合列印範圍\n"
|
||||
"- 分配了一個已啟用的擠出機\n"
|
||||
"- 沒有全部設定成修改網格"
|
||||
|
||||
msgctxt "@label"
|
||||
|
|
|
|||
|
|
@ -2567,8 +2567,8 @@ msgctxt "skin_monotonic label"
|
|||
msgid "Monotonic Top/Bottom Order"
|
||||
msgstr "單一化列印 頂層/底層 順序"
|
||||
|
||||
msgctxt "multi_material_paint_deepness label"
|
||||
msgid "Multi-material Deepness"
|
||||
msgctxt "multi_material_paint_depth label"
|
||||
msgid "Multi-material Depth"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_resolution label"
|
||||
|
|
@ -4331,8 +4331,8 @@ msgctxt "material_brand description"
|
|||
msgid "The brand of material used."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "multi_material_paint_deepness description"
|
||||
msgid "The deepness of the painted details inside the model. A higher deepness will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated deepness may vary."
|
||||
msgctxt "multi_material_paint_depth description"
|
||||
msgid "The depth of the painted details inside the model. A higher depth will provide a better interlocking, but increase slicing time and memory. Set a very high value to go as deep as possible. The actually calculated depth may vary."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "machine_acceleration description"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ Column
|
|||
|
||||
spacing: UM.Theme.getSize("thin_margin").height
|
||||
property bool preSlicedData: PrintInformation.preSliced
|
||||
property bool userTimeAdjusted: PrintInformation.userTimeAdjusted
|
||||
property alias hasPreviewButton: previewStageShortcut.visible
|
||||
|
||||
UM.I18nCatalog
|
||||
|
|
@ -54,9 +55,22 @@ Column
|
|||
Cura.IconWithText
|
||||
{
|
||||
id: estimatedTime
|
||||
width: parent.width
|
||||
width: parent.width - printInformationPanel.width
|
||||
|
||||
text:
|
||||
{
|
||||
if (preSlicedData)
|
||||
{
|
||||
return catalog.i18nc("@label", "No time estimation available")
|
||||
}
|
||||
|
||||
text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
|
||||
if (userTimeAdjusted)
|
||||
{
|
||||
return PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) + " " + catalog.i18nc("@label", "*User Adjusted")
|
||||
}
|
||||
|
||||
return PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long)
|
||||
}
|
||||
source: UM.Theme.getIcon("Clock")
|
||||
font: UM.Theme.getFont("medium_bold")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ Item
|
|||
Action
|
||||
{
|
||||
id: updateProfileAction
|
||||
enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings && Cura.MachineManager.activeQualityChangesGroup != null
|
||||
enabled: !Cura.MachineErrorChecker.hasError && Cura.MachineManager.hasUserSettings && Cura.MachineManager.activeQualityChangesGroup != null
|
||||
text: catalog.i18nc("@action:inmenu menubar:profile", "&Update profile with current settings/overrides");
|
||||
onTriggered: Cura.ContainerManager.updateQualityChanges()
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ Item
|
|||
Action
|
||||
{
|
||||
id: addProfileAction
|
||||
enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings
|
||||
enabled: !Cura.MachineErrorChecker.hasError && Cura.MachineManager.hasUserSettings
|
||||
text: catalog.i18nc("@action:inmenu menubar:profile", "&Create profile from current settings/overrides...")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ UM.ManagementPage
|
|||
id: createMenuButton
|
||||
text: catalog.i18nc("@action:button", "Create new")
|
||||
|
||||
enabled: !Cura.MachineManager.stacksHaveErrors
|
||||
enabled: !Cura.MachineErrorChecker.hasError
|
||||
visible: base.canCreateProfile
|
||||
tooltip: catalog.i18nc("@action:tooltip", "Create new profile from current settings/overrides")
|
||||
onClicked:
|
||||
|
|
|
|||
|
|
@ -258,6 +258,10 @@ Item
|
|||
border.width: UM.Theme.getSize("default_lining").width
|
||||
|
||||
color: UM.Theme.getColor("main_background")
|
||||
|
||||
// Reduce opacity of the settings when the selected extruder is disabled
|
||||
opacity: Cura.MachineManager.activeStack != null && !Cura.MachineManager.activeStack.isEnabled ? 0.4 : 1.0
|
||||
|
||||
Cura.SettingView
|
||||
{
|
||||
anchors
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ speed_print = 60.0
|
|||
speed_travel_layer_0 = 125.0
|
||||
speed_wall_x = 50.0
|
||||
top_bottom_thickness = =layer_height_0+layer_height*3
|
||||
top_layers = =0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))
|
||||
wall_0_material_flow = 95
|
||||
wall_0_wipe_dist = 0.4
|
||||
wall_thickness = 1.2
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ speed_print = 60.0
|
|||
speed_travel_layer_0 = 125.0
|
||||
speed_wall_x = 50.0
|
||||
top_bottom_thickness = =layer_height_0+layer_height*3
|
||||
top_layers = =0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))
|
||||
wall_0_material_flow = 95
|
||||
wall_0_wipe_dist = 0.4
|
||||
wall_thickness = 1.2
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ support_xy_distance = 0.5
|
|||
support_xy_overrides_z = xy_overrides_z
|
||||
support_z_distance = 0.2
|
||||
top_bottom_thickness = 0.6
|
||||
top_layers = =0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))
|
||||
top_thickness = 1
|
||||
travel_avoid_other_parts = True
|
||||
travel_avoid_supports = True
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cool_min_temperature = =material_print_temperature - 20
|
|||
hole_xy_offset = 0.1
|
||||
material_final_print_temperature = =material_print_temperature - 15
|
||||
material_initial_print_temperature = =material_print_temperature - 15
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_x_roofing = =speed_wall * 0.8
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cool_min_temperature = =material_print_temperature - 20
|
|||
hole_xy_offset = 0.1
|
||||
material_final_print_temperature = =material_print_temperature - 15
|
||||
material_initial_print_temperature = =material_print_temperature - 15
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_x_roofing = =speed_wall * 0.8
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ cool_min_temperature = =material_print_temperature - 20
|
|||
hole_xy_offset = 0.1
|
||||
material_final_print_temperature = =material_print_temperature - 15
|
||||
material_initial_print_temperature = =material_print_temperature - 15
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_x_roofing = =speed_wall * 0.8
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ weight = -1
|
|||
[values]
|
||||
cool_min_temperature = =material_print_temperature - 20
|
||||
hole_xy_offset = 0.1
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
retraction_speed = 25
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ weight = 0
|
|||
[values]
|
||||
cool_min_temperature = =material_print_temperature - 20
|
||||
hole_xy_offset = 0.1
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_x_roofing = =speed_wall * 0.8
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ weight = -2
|
|||
[values]
|
||||
cool_min_temperature = =material_print_temperature - 20
|
||||
hole_xy_offset = 0.1
|
||||
retraction_prime_speed = =retraction_speed
|
||||
retraction_prime_speed = 30
|
||||
speed_roofing = =speed_topbottom * 1/3
|
||||
speed_wall_x = =speed_wall
|
||||
speed_wall_x_roofing = =speed_wall * 0.8
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ prime_tower_position_x
|
|||
prime_tower_position_y
|
||||
prime_tower_brim_enable
|
||||
interlocking_enable
|
||||
multi_material_paint_deepness
|
||||
multi_material_paint_depth
|
||||
multi_material_paint_resolution
|
||||
|
||||
[meshfix]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue