Merge remote-tracking branch 'origin/main' into CURA-11406_fix_auth_refresh_after_sleep

This commit is contained in:
Remco Burema 2024-02-14 10:31:17 +01:00
commit fc0d1a54f3
577 changed files with 7380 additions and 910 deletions

View file

@ -5,6 +5,13 @@ body:
- type: markdown
attributes:
value: |
### 💥 Slicing Crash Analysis Tool 💥
We are taking steps to analyze an increase in reported crashes more systematically. We'll need some help with that. 😇
Before filling out the report below, we want you to try a special Cura 5.7 Alpha.
This version of Cura has an updated slicing engine that will automatically send a report to the Cura Team for analysis.
#### [You can find the downloads here](https://github.com/Ultimaker/Cura/discussions/18080) ####
If you still encounter a crash you are still welcome to report the issue so we can use your model as a test case, you can find instructions on how to do that below.
### Project File
**⚠️ Before you continue, we need your project file to troubleshoot a slicing crash.**
It contains the printer and settings we need for troubleshooting.
@ -68,4 +75,3 @@ body:
description: You can add the zip file and additional information that is relevant to the issue in the comments below.
validations:
required: true

View file

@ -58,7 +58,7 @@ jobs:
enterprise: ${{ github.event.inputs.enterprise == 'true' }}
staging: ${{ github.event.inputs.staging == 'true' }}
architecture: X64
operating_system: windows-2022
operating_system: self-hosted-Windows-X64
secrets: inherit
linux-installer:

View file

@ -34,9 +34,10 @@ on:
operating_system:
description: 'OS'
required: true
default: 'windows-2022'
default: 'self-hosted-Windows-X64'
type: choice
options:
- self-hosted-Windows-X64
- windows-2022
jobs:

View file

@ -55,7 +55,8 @@ exe = EXE(
target_arch={{ target_arch }},
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
entitlements_file={{ entitlements_file }},
icon={{ icon }}
icon={{ icon }},
contents_directory='.'
)
coll = COLLECT(
@ -70,188 +71,7 @@ coll = COLLECT(
)
{% if macos == true %}
# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
# The folder structure should adhere to the one specified in Table 2-5
# https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1
# The class below is basically ducktyping the BUNDLE class of PyInstaller and using our own `assemble` method for more fine-grain and specific
# control. Some code of the method below is copied from:
# https://github.com/pyinstaller/pyinstaller/blob/22d1d2a5378228744cc95f14904dae1664df32c4/PyInstaller/building/osx.py#L115
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2022, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
import plistlib
import shutil
import PyInstaller.utils.osx as osxutils
from pathlib import Path
from PyInstaller.building.osx import BUNDLE
from PyInstaller.building.utils import (_check_path_overlap, _rmtree, add_suffix_to_extension, checkCache)
from PyInstaller.building.datastruct import logger
from PyInstaller.building.icon import normalize_icon_type
class UMBUNDLE(BUNDLE):
def assemble(self):
from PyInstaller.config import CONF
if _check_path_overlap(self.name) and os.path.isdir(self.name):
_rmtree(self.name)
logger.info("Building BUNDLE %s", self.tocbasename)
# Create a minimal Mac bundle structure.
macos_path = Path(self.name, "Contents", "MacOS")
resources_path = Path(self.name, "Contents", "Resources")
frameworks_path = Path(self.name, "Contents", "Frameworks")
os.makedirs(macos_path)
os.makedirs(resources_path)
os.makedirs(frameworks_path)
# Makes sure the icon exists and attempts to convert to the proper format if applicable
self.icon = normalize_icon_type(self.icon, ("icns",), "icns", CONF["workpath"])
# Ensure icon path is absolute
self.icon = os.path.abspath(self.icon)
# Copy icns icon to Resources directory.
shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources'))
# Key/values for a minimal Info.plist file
info_plist_dict = {
"CFBundleDisplayName": self.appname,
"CFBundleName": self.appname,
# Required by 'codesign' utility.
# The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing
# purposes. It even identifies the APP for access to restricted OS X areas like Keychain.
#
# The identifier used for signing must be globally unique. The usual form for this identifier is a
# hierarchical name in reverse DNS notation, starting with the toplevel domain, followed by the company
# name, followed by the department within the company, and ending with the product name. Usually in the
# form: com.mycompany.department.appname
# CLI option --osx-bundle-identifier sets this value.
"CFBundleIdentifier": self.bundle_identifier,
"CFBundleExecutable": os.path.basename(self.exename),
"CFBundleIconFile": os.path.basename(self.icon),
"CFBundleInfoDictionaryVersion": "6.0",
"CFBundlePackageType": "APPL",
"CFBundleVersionString": self.version,
"CFBundleShortVersionString": self.version,
}
# Set some default values. But they still can be overwritten by the user.
if self.console:
# Setting EXE console=True implies LSBackgroundOnly=True.
info_plist_dict['LSBackgroundOnly'] = True
else:
# Let's use high resolution by default.
info_plist_dict['NSHighResolutionCapable'] = True
# Merge info_plist settings from spec file
if isinstance(self.info_plist, dict) and self.info_plist:
info_plist_dict.update(self.info_plist)
plist_filename = os.path.join(self.name, "Contents", "Info.plist")
with open(plist_filename, "wb") as plist_fh:
plistlib.dump(info_plist_dict, plist_fh)
links = []
_QT_BASE_PATH = {'PySide2', 'PySide6', 'PyQt5', 'PyQt6', 'PySide6'}
for inm, fnm, typ in self.toc:
# Adjust name for extensions, if applicable
inm, fnm, typ = add_suffix_to_extension(inm, fnm, typ)
inm = Path(inm)
fnm = Path(fnm)
# Copy files from cache. This ensures that are used files with relative paths to dynamic library
# dependencies (@executable_path)
if typ in ('EXTENSION', 'BINARY') or (typ == 'DATA' and inm.suffix == '.so'):
if any(['.' in p for p in inm.parent.parts]):
inm = Path(inm.name)
fnm = Path(checkCache(
str(fnm),
strip = self.strip,
upx = self.upx,
upx_exclude = self.upx_exclude,
dist_nm = str(inm),
target_arch = self.target_arch,
codesign_identity = self.codesign_identity,
entitlements_file = self.entitlements_file,
strict_arch_validation = (typ == 'EXTENSION'),
))
frame_dst = frameworks_path.joinpath(inm)
if not frame_dst.exists():
if frame_dst.is_dir():
os.makedirs(frame_dst, exist_ok = True)
else:
os.makedirs(frame_dst.parent, exist_ok = True)
shutil.copy(fnm, frame_dst, follow_symlinks = True)
macos_dst = macos_path.joinpath(inm)
if not macos_dst.exists():
if macos_dst.is_dir():
os.makedirs(macos_dst, exist_ok = True)
else:
os.makedirs(macos_dst.parent, exist_ok = True)
# Create relative symlink to the framework
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Frameworks").joinpath(
frame_dst.relative_to(frameworks_path))
try:
macos_dst.symlink_to(symlink_to)
except FileExistsError:
pass
else:
if typ == 'DATA':
if any(['.' in p for p in inm.parent.parts]) or inm.suffix == '.so':
# Skip info dist egg and some not needed folders in tcl and tk, since they all contain dots in their files
logger.warning(f"Skipping DATA file {inm}")
continue
res_dst = resources_path.joinpath(inm)
if not res_dst.exists():
if res_dst.is_dir():
os.makedirs(res_dst, exist_ok = True)
else:
os.makedirs(res_dst.parent, exist_ok = True)
shutil.copy(fnm, res_dst, follow_symlinks = True)
macos_dst = macos_path.joinpath(inm)
if not macos_dst.exists():
if macos_dst.is_dir():
os.makedirs(macos_dst, exist_ok = True)
else:
os.makedirs(macos_dst.parent, exist_ok = True)
# Create relative symlink to the resource
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Resources").joinpath(
res_dst.relative_to(resources_path))
try:
macos_dst.symlink_to(symlink_to)
except FileExistsError:
pass
else:
macos_dst = macos_path.joinpath(inm)
if not macos_dst.exists():
if macos_dst.is_dir():
os.makedirs(macos_dst, exist_ok = True)
else:
os.makedirs(macos_dst.parent, exist_ok = True)
shutil.copy(fnm, macos_dst, follow_symlinks = True)
# Sign the bundle
logger.info('Signing the BUNDLE...')
try:
osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file, deep = True)
except Exception as e:
logger.warning(f"Error while signing the bundle: {e}")
logger.warning("You will need to sign the bundle manually!")
logger.info(f"Building BUNDLE {self.tocbasename} completed successfully.")
app = UMBUNDLE(
app = BUNDLE(
coll,
name='{{ display_name }}.app',
icon={{ icon }},
@ -272,8 +92,9 @@ app = UMBUNDLE(
}],
'CFBundleDocumentTypes': [{
'CFBundleTypeRole': 'Viewer',
'CFBundleTypeExtensions': ['*'],
'CFBundleTypeExtensions': ['stl', 'obj', '3mf', 'gcode', 'ufp'],
'CFBundleTypeName': 'Model Files',
}]
},
){% endif %}
)
{% endif %}

View file

@ -1,12 +1,11 @@
version: "5.7.0-alpha.0"
version: "5.7.0-alpha.1"
requirements:
- "uranium/(latest)@ultimaker/testing"
- "curaengine/(latest)@ultimaker/testing"
- "cura_binary_data/(latest)@ultimaker/testing"
- "fdm_materials/(latest)@ultimaker/testing"
- "curaengine_plugin_gradual_flow/(latest)@ultimaker/stable"
- "curaengine_plugin_gradual_flow/0.1.0-beta.2"
- "dulcificum/latest@ultimaker/testing"
- "pyarcus/5.3.0"
- "pysavitar/5.3.0"
- "pynest2d/5.3.0"
- "curaengine_grpc_definitions/(latest)@ultimaker/testing"
@ -119,7 +118,6 @@ pyinstaller:
- "sqlite3"
- "trimesh"
- "win32ctypes"
- "PyQt6"
- "PyQt6.QtNetwork"
- "PyQt6.sip"
- "stl"
@ -161,6 +159,10 @@ pycharm_targets:
module_name: Cura
name: pytest in TestGCodeListDecorator.py
script_name: tests/TestGCodeListDecorator.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestHitChecker.py
script_name: tests/TestHitChecker.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestIntentManager.py
@ -189,6 +191,10 @@ pycharm_targets:
module_name: Cura
name: pytest in TestPrintInformation.py
script_name: tests/TestPrintInformation.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestPrintOrderManager.py
script_name: tests/TestPrintOrderManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestProfileRequirements.py

View file

@ -1,4 +1,5 @@
import os
from io import StringIO
from pathlib import Path
from jinja2 import Template
@ -150,6 +151,7 @@ class CuraConan(ConanFile):
return "None"
def _conan_installs(self):
self.output.info("Collecting conan installs")
conan_installs = {}
# list of conan installs
@ -161,13 +163,22 @@ class CuraConan(ConanFile):
return conan_installs
def _python_installs(self):
self.output.info("Collecting python installs")
python_installs = {}
# list of python installs
python_ins_cmd = f"python -c \"import pkg_resources; print(';'.join([(s.key+','+ s.version) for s in pkg_resources.working_set]))\""
from six import StringIO
run_env = VirtualRunEnv(self)
env = run_env.environment()
env.prepend_path("PYTHONPATH", str(self._site_packages.as_posix()))
venv_vars = env.vars(self, scope = "run")
outer = '"' if self.settings.os == "Windows" else "'"
inner = "'" if self.settings.os == "Windows" else '"'
buffer = StringIO()
self.run(python_ins_cmd, run_environment= True, env = "conanrun", output=buffer)
with venv_vars.apply():
self.run(f"""python -c {outer}import pkg_resources; print({inner};{inner}.join([(s.key+{inner},{inner}+ s.version) for s in pkg_resources.working_set])){outer}""",
env = "conanrun",
output = buffer)
packages = str(buffer.getvalue()).split("-----------------\n")
packages = packages[1].strip('\r\n').split(";")
@ -242,7 +253,7 @@ class CuraConan(ConanFile):
self.output.warning(f"Source path for binary {binary['binary']} does not exist")
continue
for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.|.pdb]*"):
for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.]*"):
binaries.append((str(bin), binary["dst"]))
for bin in Path(src_path).glob(binary["binary"]):
binaries.append((str(bin), binary["dst"]))
@ -320,6 +331,8 @@ class CuraConan(ConanFile):
self.options["openssl"].shared = True
if self.conf.get("user.curaengine:sentry_url", "", check_type=str) != "":
self.options["curaengine"].enable_sentry = True
self.options["arcus"].enable_sentry = True
self.options["clipper"].enable_sentry = True
def validate(self):
version = self.conf.get("user.cura:version", default = self.version, check_type = str)
@ -335,6 +348,7 @@ class CuraConan(ConanFile):
for req in self.conan_data["requirements_internal"]:
self.requires(req)
self.requires("cpython/3.10.4@ultimaker/stable")
self.requires("clipper/6.4.2@ultimaker/stable")
self.requires("openssl/3.2.0")
self.requires("boost/1.82.0")
self.requires("spdlog/1.12.0")
@ -417,7 +431,6 @@ class CuraConan(ConanFile):
)
if self.options.get_safe("enable_i18n", False) and self._i18n_options["extract"]:
# Update the po and pot files
vb = VirtualBuildEnv(self)
vb.generate()
@ -502,10 +515,14 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
if self.in_local_cache:
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "site-packages"))
self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "site-packages"))
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "plugins"))
self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "plugins"))
else:
self.runenv_info.append_path("PYTHONPATH", self.source_folder)
self.env_info.PYTHONPATH.append(self.source_folder)
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.source_folder, "plugins"))
self.env_info.PYTHONPATH.append(os.path.join(self.source_folder, "plugins"))
def package_id(self):
self.info.clear()
@ -518,7 +535,8 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
del self.info.options.cloud_api_version
del self.info.options.display_name
del self.info.options.cura_debug_mode
self.options.rm_safe("enable_i18n")
if self.options.get_safe("enable_i18n", False):
del self.info.options.enable_i18n
# TODO: Use the hash of requirements.txt and requirements-ultimaker.txt, Because changing these will actually result in a different
# Cura. This is needed because the requirements.txt aren't managed by Conan and therefor not resolved in the package_id. This isn't

View file

@ -3,10 +3,11 @@
from typing import List, cast
from PyQt6.QtCore import QObject, QUrl, QMimeData
from PyQt6.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty
from PyQt6.QtGui import QDesktopServices
from PyQt6.QtWidgets import QApplication
from UM.Application import Application
from UM.Event import CallFunctionEvent
from UM.FlameProfiler import pyqtSlot
from UM.Math.Vector import Vector
@ -37,6 +38,10 @@ class CuraActions(QObject):
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
self._operation_stack = Application.getInstance().getOperationStack()
self._operation_stack.changed.connect(self._onUndoStackChanged)
undoStackChanged = pyqtSignal()
@pyqtSlot()
def openDocumentation(self) -> None:
# Starting a web browser from a signal handler connected to a menu will crash on windows.
@ -45,6 +50,25 @@ class CuraActions(QObject):
event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/resources/manuals/software?utm_source=cura&utm_medium=software&utm_campaign=dropdown-documentation")], {})
cura.CuraApplication.CuraApplication.getInstance().functionEvent(event)
@pyqtProperty(bool, notify=undoStackChanged)
def canUndo(self):
return self._operation_stack.canUndo()
@pyqtProperty(bool, notify=undoStackChanged)
def canRedo(self):
return self._operation_stack.canRedo()
@pyqtSlot()
def undo(self):
self._operation_stack.undo()
@pyqtSlot()
def redo(self):
self._operation_stack.redo()
def _onUndoStackChanged(self):
self.undoStackChanged.emit()
@pyqtSlot()
def openBugReportPage(self) -> None:
event = CallFunctionEvent(self._openUrl, [QUrl("https://github.com/Ultimaker/Cura/issues/new/choose")], {})

View file

@ -15,13 +15,13 @@ import numpy
from PyQt6.QtCore import QObject, QTimer, QUrl, QUrlQuery, pyqtSignal, pyqtProperty, QEvent, pyqtEnum, QCoreApplication, \
QByteArray
from PyQt6.QtGui import QColor, QIcon
from PyQt6.QtQml import qmlRegisterUncreatableType, qmlRegisterUncreatableMetaObject, qmlRegisterSingletonType, qmlRegisterType
from PyQt6.QtQml import qmlRegisterUncreatableMetaObject, qmlRegisterSingletonType, qmlRegisterType
from PyQt6.QtWidgets import QMessageBox
import UM.Util
import cura.Settings.cura_empty_instance_containers
from UM.Application import Application
from UM.Decorators import override
from UM.Decorators import override, deprecated
from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger
from UM.Math.AxisAlignedBox import AxisAlignedBox
@ -104,7 +104,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
from cura.UI import CuraSplashScreen, MachineActionManager, PrintInformation
from cura.UI import CuraSplashScreen, PrintInformation
from cura.UI.MachineActionManager import MachineActionManager
from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
from cura.UI.MachineSettingsManager import MachineSettingsManager
from cura.UI.ObjectsModel import ObjectsModel
@ -125,6 +126,7 @@ from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel
from .Machines.Models.MachineListModel import MachineListModel
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
from .PrintOrderManager import PrintOrderManager
from .SingleInstance import SingleInstance
if TYPE_CHECKING:
@ -179,6 +181,7 @@ class CuraApplication(QtApplication):
# Variables set from CLI
self._files_to_open = []
self._urls_to_open = []
self._use_single_instance = False
self._single_instance = None
@ -186,7 +189,7 @@ class CuraApplication(QtApplication):
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager]
self._machine_action_manager: Optional[MachineActionManager] = None
self.empty_container = None # type: EmptyInstanceContainer
self.empty_definition_changes_container = None # type: EmptyInstanceContainer
@ -202,6 +205,7 @@ class CuraApplication(QtApplication):
self._container_manager = None
self._object_manager = None
self._print_order_manager = None
self._extruders_model = None
self._extruders_model_with_optional = None
self._build_plate_model = None
@ -333,7 +337,7 @@ class CuraApplication(QtApplication):
for filename in self._cli_args.file:
url = QUrl(filename)
if url.scheme() in self._supported_url_schemes:
self._open_url_queue.append(url)
self._urls_to_open.append(url)
else:
self._files_to_open.append(os.path.abspath(filename))
@ -352,11 +356,11 @@ class CuraApplication(QtApplication):
self.__addAllEmptyContainers()
self.__setLatestResouceVersionsForVersionUpgrade()
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
self._machine_action_manager = MachineActionManager(self)
self._machine_action_manager.initialize()
def __sendCommandToSingleInstance(self):
self._single_instance = SingleInstance(self, self._files_to_open)
self._single_instance = SingleInstance(self, self._files_to_open, self._urls_to_open)
# If we use single instance, try to connect to the single instance server, send commands, and then exit.
# If we cannot find an existing single instance server, this is the only instance, so just keep going.
@ -373,9 +377,15 @@ class CuraApplication(QtApplication):
Resources.addExpectedDirNameInData(dir_name)
app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
if platform.system() == "Darwin":
Resources.addSecureSearchPath(os.path.join(app_root, "Resources", "share", "cura", "resources"))
Resources.addSecureSearchPath(
os.path.join(self._app_install_dir, "Resources", "share", "cura", "resources"))
else:
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
if not hasattr(sys, "frozen"):
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
if cura_data_root:
@ -899,6 +909,7 @@ class CuraApplication(QtApplication):
# initialize info objects
self._print_information = PrintInformation.PrintInformation(self)
self._cura_actions = CuraActions.CuraActions(self)
self._print_order_manager = PrintOrderManager(self.getObjectsModel().getNodes)
self.processEvents()
# Initialize setting visibility presets model.
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self.getPreferences(), parent = self)
@ -956,6 +967,8 @@ class CuraApplication(QtApplication):
self.callLater(self._openFile, file_name)
for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading.
self.callLater(self._openFile, file_name)
for url in self._urls_to_open:
self.callLater(self._openUrl, url)
for url in self._open_url_queue:
self.callLater(self._openUrl, url)
@ -979,6 +992,7 @@ class CuraApplication(QtApplication):
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])
Selection.selectionChanged.connect(self.onSelectionChanged)
self._print_order_manager.printOrderChanged.connect(self._onPrintOrderChanged)
# Set default background color for scene
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
@ -1094,6 +1108,10 @@ class CuraApplication(QtApplication):
self._object_manager = ObjectsModel(self)
return self._object_manager
@pyqtSlot(str, result = "QVariantList")
def getSupportedActionMachineList(self, definition_id: str) -> List["MachineAction"]:
return self._machine_action_manager.getSupportedActions(self._machine_manager.getDefinitionByMachineId(definition_id))
@pyqtSlot(result = QObject)
def getExtrudersModel(self, *args) -> "ExtrudersModel":
if self._extruders_model is None:
@ -1129,14 +1147,16 @@ class CuraApplication(QtApplication):
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
return self._setting_inheritance_manager
def getMachineActionManager(self, *args: Any) -> MachineActionManager.MachineActionManager:
@pyqtSlot(result = QObject)
def getMachineActionManager(self, *args: Any) -> MachineActionManager:
"""Get the machine action manager
We ignore any *args given to this, as we also register the machine manager as qml singleton.
It wants to give this function an engine and script engine, but we don't care about that.
"""
return cast(MachineActionManager.MachineActionManager, self._machine_action_manager)
return self._machine_action_manager
@pyqtSlot(result = QObject)
def getMaterialManagementModel(self) -> MaterialManagementModel:
@ -1150,7 +1170,8 @@ class CuraApplication(QtApplication):
self._quality_management_model = QualityManagementModel(parent = self)
return self._quality_management_model
def getSimpleModeSettingsManager(self, *args):
@pyqtSlot(result=QObject)
def getSimpleModeSettingsManager(self)-> SimpleModeSettingsManager:
if self._simple_mode_settings_manager is None:
self._simple_mode_settings_manager = SimpleModeSettingsManager()
return self._simple_mode_settings_manager
@ -1193,16 +1214,43 @@ class CuraApplication(QtApplication):
return self._print_information
def getQualityProfilesDropDownMenuModel(self, *args, **kwargs):
@pyqtSlot(result=QObject)
def getQualityProfilesDropDownMenuModel(self, *args, **kwargs)-> QualityProfilesDropDownMenuModel:
if self._quality_profile_drop_down_menu_model is None:
self._quality_profile_drop_down_menu_model = QualityProfilesDropDownMenuModel(self)
return self._quality_profile_drop_down_menu_model
def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs):
@pyqtSlot(result=QObject)
def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs)->CustomQualityProfilesDropDownMenuModel:
if self._custom_quality_profile_drop_down_menu_model is None:
self._custom_quality_profile_drop_down_menu_model = CustomQualityProfilesDropDownMenuModel(self)
return self._custom_quality_profile_drop_down_menu_model
@deprecated("SimpleModeSettingsManager is deprecated and will be removed in major SDK release, Use getSimpleModeSettingsManager() instead", since = "5.7.0")
def getSimpleModeSettingsManagerWrapper(self, *args, **kwargs):
return self.getSimpleModeSettingsManager()
@deprecated("MachineActionManager is deprecated and will be removed in major SDK release, Use getMachineActionManager() instead", since="5.7.0")
def getMachineActionManagerWrapper(self, *args, **kwargs):
return self.getMachineActionManager()
@deprecated("QualityManagementModel is deprecated and will be removed in major SDK release, Use getQualityManagementModel() instead", since="5.7.0")
def getQualityManagementModelWrapper(self, *args, **kwargs):
return self.getQualityManagementModel()
@deprecated("MaterialManagementModel is deprecated and will be removed in major SDK release, Use getMaterialManagementModel() instead", since = "5.7.0")
def getMaterialManagementModelWrapper(self, *args, **kwargs):
return self.getMaterialManagementModel()
@deprecated("QualityProfilesDropDownMenuModel is deprecated and will be removed in major SDK release, Use getQualityProfilesDropDownMenuModel() instead", since = "5.7.0")
def getQualityProfilesDropDownMenuModelWrapper(self, *args, **kwargs):
return self.getQualityProfilesDropDownMenuModel()
@deprecated("CustomQualityProfilesDropDownMenuModel is deprecated and will be removed in major SDK release, Use getCustomQualityProfilesDropDownMenuModel() instead", since = "5.7.0")
def getCustomQualityProfilesDropDownMenuModelWrapper(self, *args, **kwargs):
return self.getCustomQualityProfilesDropDownMenuModel()
def getCuraAPI(self, *args, **kwargs) -> "CuraAPI":
return self._cura_API
@ -1218,6 +1266,7 @@ class CuraApplication(QtApplication):
self.processEvents()
engine.rootContext().setContextProperty("Printer", self)
engine.rootContext().setContextProperty("CuraApplication", self)
engine.rootContext().setContextProperty("PrintOrderManager", self._print_order_manager)
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
@ -1231,8 +1280,8 @@ class CuraApplication(QtApplication):
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, self.getMachineManager, "MachineManager")
qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManager, "SimpleModeSettingsManager")
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, self.getMachineActionManager, "MachineActionManager")
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
qmlRegisterSingletonType(MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
self.processEvents()
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
@ -1257,16 +1306,14 @@ class CuraApplication(QtApplication):
qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")
qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel")
qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel")
qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, self.getQualityManagementModel, "QualityManagementModel")
qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, self.getMaterialManagementModel, "MaterialManagementModel")
qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, self.getQualityManagementModelWrapper,"QualityManagementModel")
qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, self.getMaterialManagementModelWrapper,"MaterialManagementModel")
self.processEvents()
qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel")
qmlRegisterType(DiscoveredCloudPrintersModel, "Cura", 1, 7, "DiscoveredCloudPrintersModel")
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0,
self.getQualityProfilesDropDownMenuModel, "QualityProfilesDropDownMenuModel")
qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0,
self.getCustomQualityProfilesDropDownMenuModel, "CustomQualityProfilesDropDownMenuModel")
qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, self.getQualityProfilesDropDownMenuModelWrapper, "QualityProfilesDropDownMenuModel")
qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, self.getCustomQualityProfilesDropDownMenuModelWrapper, "CustomQualityProfilesDropDownMenuModel")
qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")
qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel")
qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel")
@ -1715,8 +1762,12 @@ class CuraApplication(QtApplication):
Selection.remove(node)
Selection.add(group_node)
all_nodes = self.getObjectsModel().getNodes()
PrintOrderManager.updatePrintOrdersAfterGroupOperation(all_nodes, group_node, selected_nodes)
@pyqtSlot()
def ungroupSelected(self) -> None:
all_nodes = self.getObjectsModel().getNodes()
selected_objects = Selection.getAllSelectedObjects().copy()
for node in selected_objects:
if node.callDecoration("isGroup"):
@ -1724,21 +1775,30 @@ class CuraApplication(QtApplication):
group_parent = node.getParent()
children = node.getChildren().copy()
for child in children:
# Ungroup only 1 level deep
if child.getParent() != node:
continue
# Ungroup only 1 level deep
children_to_ungroup = list(filter(lambda child: child.getParent() == node, children))
for child in children_to_ungroup:
# Set the parent of the children to the parent of the group-node
op.addOperation(SetParentOperation(child, group_parent))
# Add all individual nodes to the selection
Selection.add(child)
PrintOrderManager.updatePrintOrdersAfterUngroupOperation(all_nodes, node, children_to_ungroup)
op.push()
# Note: The group removes itself from the scene once all its children have left it,
# see GroupDecorator._onChildrenChanged
def _onPrintOrderChanged(self) -> None:
# update object list
scene = self.getController().getScene()
scene.sceneChanged.emit(scene.getRoot())
# reset if already was sliced
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
def _createSplashScreen(self) -> Optional[CuraSplashScreen.CuraSplashScreen]:
if self._is_headless:
return None

88
cura/HitChecker.py Normal file
View file

@ -0,0 +1,88 @@
from typing import List, Dict
from cura.Scene.CuraSceneNode import CuraSceneNode
class HitChecker:
"""Checks if nodes can be printed without causing any collisions and interference"""
def __init__(self, nodes: List[CuraSceneNode]) -> None:
self._hit_map = self._buildHitMap(nodes)
def anyTwoNodesBlockEachOther(self, nodes: List[CuraSceneNode]) -> bool:
"""Returns True if any 2 nodes block each other"""
for a in nodes:
for b in nodes:
if self._hit_map[a][b] and self._hit_map[b][a]:
return True
return False
def canPrintBefore(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
"""Returns True if node doesn't block other_nodes and can be printed before them"""
no_hits = all(not self._hit_map[node][other_node] for other_node in other_nodes)
return no_hits
def canPrintAfter(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
"""Returns True if node doesn't hit other nodes and can be printed after them"""
no_hits = all(not self._hit_map[other_node][node] for other_node in other_nodes)
return no_hits
def calculateScore(self, a: CuraSceneNode, b: CuraSceneNode) -> int:
"""Calculate score simply sums the number of other objects it 'blocks'
:param a: node
:param b: node
:return: sum of the number of other objects
"""
score_a = sum(self._hit_map[a].values())
score_b = sum(self._hit_map[b].values())
return score_a - score_b
def canPrintNodesInProvidedOrder(self, ordered_nodes: List[CuraSceneNode]) -> bool:
"""Returns True If nodes don't have any hits in provided order"""
for node_index, node in enumerate(ordered_nodes):
nodes_before = ordered_nodes[:node_index - 1] if node_index - 1 >= 0 else []
nodes_after = ordered_nodes[node_index + 1:] if node_index + 1 < len(ordered_nodes) else []
if not self.canPrintBefore(node, nodes_after) or not self.canPrintAfter(node, nodes_before):
return False
return True
@staticmethod
def _buildHitMap(nodes: List[CuraSceneNode]) -> Dict[CuraSceneNode, CuraSceneNode]:
"""Pre-computes all hits between all objects
:nodes: nodes that need to be checked for collisions
:return: dictionary where hit_map[node1][node2] is False if there node1 can be printed before node2
"""
hit_map = {j: {i: HitChecker._checkHit(j, i) for i in nodes} for j in nodes}
return hit_map
@staticmethod
def _checkHit(a: CuraSceneNode, b: CuraSceneNode) -> bool:
"""Checks if a can be printed before b
:param a: node
:param b: node
:return: False if a can be printed before b
"""
if a == b:
return False
a_hit_hull = a.callDecoration("getConvexHullBoundary")
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
# Adhesion areas must never overlap, regardless of printing order
# This would cause over-extrusion
a_hit_hull = a.callDecoration("getAdhesionArea")
b_hit_hull = b.callDecoration("getAdhesionArea")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
else:
return False

View file

@ -227,7 +227,7 @@ class ExtrudersModel(ListModel):
"material_brand": "",
"color_name": "",
"material_type": "",
"material_label": ""
"material_name": ""
}
items.append(item)
if self._items != items:

View file

@ -41,6 +41,7 @@ class AuthorizationHelpers:
"""
data = {
"client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
"client_secret": self._settings.CLIENT_SECRET if self._settings.CLIENT_SECRET is not None else "",
"redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
"grant_type": "authorization_code",
"code": authorization_code,
@ -66,6 +67,7 @@ class AuthorizationHelpers:
Logger.log("d", "Refreshing the access token for [%s]", self._settings.OAUTH_SERVER_URL)
data = {
"client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
"client_secret": self._settings.CLIENT_SECRET if self._settings.CLIENT_SECRET is not None else "",
"redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
"grant_type": "refresh_token",
"refresh_token": refresh_token,

View file

@ -32,20 +32,24 @@ class AuthorizationService:
account information.
"""
def __init__(self,
settings: "OAuth2Settings",
preferences: Optional["Preferences"] = None,
get_user_profile: bool = True) -> None:
# Emit signal when authentication is completed.
onAuthStateChanged = Signal()
self.onAuthStateChanged = Signal()
# Emit signal when authentication failed.
onAuthenticationError = Signal()
self.onAuthenticationError = Signal()
accessTokenChanged = Signal()
self.accessTokenChanged = Signal()
def __init__(self, settings: "OAuth2Settings", preferences: Optional["Preferences"] = None) -> None:
self._settings = settings
self._auth_helpers = AuthorizationHelpers(settings)
self._auth_url = "{}/authorize".format(self._settings.OAUTH_SERVER_URL)
self._auth_data: Optional[AuthenticationResponse] = None
self._user_profile: Optional["UserProfile"] = None
self._get_user_profile: bool = get_user_profile
self._preferences = preferences
self._server = LocalAuthorizationServer(self._auth_helpers, self._onAuthStateChanged, daemon=True)
self._currently_refreshing_token = False # Whether we are currently in the process of refreshing auth. Don't make new requests while busy.
@ -314,6 +318,7 @@ class AuthorizationService:
self._auth_data = auth_data
self._currently_refreshing_token = False
if auth_data:
if self._get_user_profile:
self.getUserProfile()
self._preferences.setValue(self._settings.AUTH_DATA_PREFERENCE_KEY, json.dumps(auth_data.dump()))
else:

View file

@ -16,6 +16,7 @@ class OAuth2Settings(BaseModel):
CALLBACK_PORT = None # type: Optional[int]
OAUTH_SERVER_URL = None # type: Optional[str]
CLIENT_ID = None # type: Optional[str]
CLIENT_SECRET = None # type: Optional[str]
CLIENT_SCOPES = None # type: Optional[str]
CALLBACK_URL = None # type: Optional[str]
AUTH_DATA_PREFERENCE_KEY = "" # type: str

View file

@ -7,6 +7,11 @@ from UM.Scene.Iterator import Iterator
from UM.Scene.SceneNode import SceneNode
from functools import cmp_to_key
from cura.HitChecker import HitChecker
from cura.PrintOrderManager import PrintOrderManager
from cura.Scene.CuraSceneNode import CuraSceneNode
class OneAtATimeIterator(Iterator.Iterator):
"""Iterator that returns a list of nodes in the order that they need to be printed
@ -16,8 +21,6 @@ class OneAtATimeIterator(Iterator.Iterator):
def __init__(self, scene_node) -> None:
super().__init__(scene_node) # Call super to make multiple inheritance work.
self._hit_map = [[]] # type: List[List[bool]] # For each node, which other nodes this hits. A grid of booleans on which nodes hit which.
self._original_node_list = [] # type: List[SceneNode] # The nodes that need to be checked for collisions.
def _fillStack(self) -> None:
"""Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
@ -38,104 +41,50 @@ class OneAtATimeIterator(Iterator.Iterator):
self._node_stack = node_list[:]
return
# Copy the list
self._original_node_list = node_list[:]
hit_checker = HitChecker(node_list)
# Initialise the hit map (pre-compute all hits between all objects)
self._hit_map = [[self._checkHit(i, j) for i in node_list] for j in node_list]
if PrintOrderManager.isUserDefinedPrintOrderEnabled():
self._node_stack = self._getNodesOrderedByUser(hit_checker, node_list)
else:
self._node_stack = self._getNodesOrderedAutomatically(hit_checker, node_list)
# Check if we have to files that block each other. If this is the case, there is no solution!
for a in range(0, len(node_list)):
for b in range(0, len(node_list)):
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
return
# update print orders so that user can try to arrange the nodes automatically first
# and if result is not satisfactory he/she can switch to manual mode and change it
for index, node in enumerate(self._node_stack):
node.printOrder = index + 1
@staticmethod
def _getNodesOrderedByUser(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
nodes_ordered_by_user = sorted(node_list, key=lambda n: n.printOrder)
if hit_checker.canPrintNodesInProvidedOrder(nodes_ordered_by_user):
return nodes_ordered_by_user
return [] # No solution
@staticmethod
def _getNodesOrderedAutomatically(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
# Check if we have two files that block each other. If this is the case, there is no solution!
if hit_checker.anyTwoNodesBlockEachOther(node_list):
return [] # No solution
# Sort the original list so that items that block the most other objects are at the beginning.
# This does not decrease the worst case running time, but should improve it in most cases.
sorted(node_list, key = cmp_to_key(self._calculateScore))
node_list = sorted(node_list, key = cmp_to_key(hit_checker.calculateScore))
todo_node_list = [_ObjectOrder([], node_list)]
while len(todo_node_list) > 0:
current = todo_node_list.pop()
for node in current.todo:
# Check if the object can be placed with what we have and still allows for a solution in the future
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
if hit_checker.canPrintAfter(node, current.order) and hit_checker.canPrintBefore(node, current.todo):
# We found a possible result. Create new todo & order list.
new_todo_list = current.todo[:]
new_todo_list.remove(node)
new_order = current.order[:] + [node]
if len(new_todo_list) == 0:
# We have no more nodes to check, so quit looking.
self._node_stack = new_order
return
return new_order # Solution found!
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
self._node_stack = [] #No result found!
# Check if first object can be printed before the provided list (using the hit map)
def _checkHitMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
node_index = self._original_node_list.index(node)
for other_node in other_nodes:
other_node_index = self._original_node_list.index(other_node)
if self._hit_map[node_index][other_node_index]:
return True
return False
def _checkBlockMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
"""Check for a node whether it hits any of the other nodes.
:param node: The node to check whether it collides with the other nodes.
:param other_nodes: The nodes to check for collisions.
:return: returns collision between nodes
"""
node_index = self._original_node_list.index(node)
for other_node in other_nodes:
other_node_index = self._original_node_list.index(other_node)
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
return True
return False
def _calculateScore(self, a: SceneNode, b: SceneNode) -> int:
"""Calculate score simply sums the number of other objects it 'blocks'
:param a: node
:param b: node
:return: sum of the number of other objects
"""
score_a = sum(self._hit_map[self._original_node_list.index(a)])
score_b = sum(self._hit_map[self._original_node_list.index(b)])
return score_a - score_b
def _checkHit(self, a: SceneNode, b: SceneNode) -> bool:
"""Checks if a can be printed before b
:param a: node
:param b: node
:return: true if a can be printed before b
"""
if a == b:
return False
a_hit_hull = a.callDecoration("getConvexHullBoundary")
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
# Adhesion areas must never overlap, regardless of printing order
# This would cause over-extrusion
a_hit_hull = a.callDecoration("getAdhesionArea")
b_hit_hull = b.callDecoration("getAdhesionArea")
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
if overlap:
return True
else:
return False
return [] # No result found!
class _ObjectOrder:

171
cura/PrintOrderManager.py Normal file
View file

@ -0,0 +1,171 @@
from typing import List, Callable, Optional, Any
from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject, pyqtSlot
from UM.Application import Application
from UM.Scene.Selection import Selection
from cura.Scene.CuraSceneNode import CuraSceneNode
class PrintOrderManager(QObject):
"""Allows to order the object list to set the print sequence manually"""
def __init__(self, get_nodes: Callable[[], List[CuraSceneNode]]) -> None:
super().__init__()
self._get_nodes = get_nodes
self._configureEvents()
_settingsChanged = pyqtSignal()
_uiActionsOutdated = pyqtSignal()
printOrderChanged = pyqtSignal()
@pyqtSlot()
def swapSelectedAndPreviousNodes(self) -> None:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
self._swapPrintOrders(selected_node, previous_node)
@pyqtSlot()
def swapSelectedAndNextNodes(self) -> None:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
self._swapPrintOrders(selected_node, next_node)
@pyqtProperty(str, notify=_uiActionsOutdated)
def previousNodeName(self) -> str:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
return self._getNodeName(previous_node)
@pyqtProperty(str, notify=_uiActionsOutdated)
def nextNodeName(self) -> str:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
return self._getNodeName(next_node)
@pyqtProperty(bool, notify=_uiActionsOutdated)
def shouldEnablePrintBeforeAction(self) -> bool:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
can_swap_with_previous_node = selected_node is not None and previous_node is not None
return can_swap_with_previous_node
@pyqtProperty(bool, notify=_uiActionsOutdated)
def shouldEnablePrintAfterAction(self) -> bool:
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
can_swap_with_next_node = selected_node is not None and next_node is not None
return can_swap_with_next_node
@pyqtProperty(bool, notify=_settingsChanged)
def shouldShowEditPrintOrderActions(self) -> bool:
return PrintOrderManager.isUserDefinedPrintOrderEnabled()
@staticmethod
def isUserDefinedPrintOrderEnabled() -> bool:
stack = Application.getInstance().getGlobalContainerStack()
is_enabled = stack and \
stack.getProperty("print_sequence", "value") == "one_at_a_time" and \
stack.getProperty("user_defined_print_order_enabled", "value")
return bool(is_enabled)
@staticmethod
def initializePrintOrders(nodes: List[CuraSceneNode]) -> None:
"""Just created (loaded from file) nodes have print order 0.
This method initializes print orders with max value to put nodes at the end of object list"""
max_print_order = max(map(lambda n: n.printOrder, nodes), default=0)
for node in nodes:
if node.printOrder == 0:
max_print_order += 1
node.printOrder = max_print_order
@staticmethod
def updatePrintOrdersAfterGroupOperation(
all_nodes: List[CuraSceneNode],
group_node: CuraSceneNode,
grouped_nodes: List[CuraSceneNode]
) -> None:
group_node.printOrder = min(map(lambda n: n.printOrder, grouped_nodes))
all_nodes.append(group_node)
for node in grouped_nodes:
all_nodes.remove(node)
# reassign print orders so there won't be gaps like 1 2 5 6 7
sorted_nodes = sorted(all_nodes, key=lambda n: n.printOrder)
for i, node in enumerate(sorted_nodes):
node.printOrder = i + 1
@staticmethod
def updatePrintOrdersAfterUngroupOperation(
all_nodes: List[CuraSceneNode],
group_node: CuraSceneNode,
ungrouped_nodes: List[CuraSceneNode]
) -> None:
all_nodes.remove(group_node)
nodes_to_update_print_order = filter(lambda n: n.printOrder > group_node.printOrder, all_nodes)
for node in nodes_to_update_print_order:
node.printOrder += len(ungrouped_nodes) - 1
for i, child in enumerate(ungrouped_nodes):
child.printOrder = group_node.printOrder + i
all_nodes.append(child)
def _swapPrintOrders(self, node1: CuraSceneNode, node2: CuraSceneNode) -> None:
if node1 and node2:
node1.printOrder, node2.printOrder = node2.printOrder, node1.printOrder # swap print orders
self.printOrderChanged.emit() # update object list first
self._uiActionsOutdated.emit() # then update UI actions
def _getSelectedAndNeighborNodes(self
) -> (Optional[CuraSceneNode], Optional[CuraSceneNode], Optional[CuraSceneNode]):
nodes = self._get_nodes()
ordered_nodes = sorted(nodes, key=lambda n: n.printOrder)
selected_node = PrintOrderManager._getSingleSelectedNode()
if selected_node and selected_node in ordered_nodes:
selected_node_index = ordered_nodes.index(selected_node)
else:
selected_node_index = None
if selected_node_index is not None and selected_node_index - 1 >= 0:
previous_node = ordered_nodes[selected_node_index - 1]
else:
previous_node = None
if selected_node_index is not None and selected_node_index + 1 < len(ordered_nodes):
next_node = ordered_nodes[selected_node_index + 1]
else:
next_node = None
return selected_node, previous_node, next_node
@staticmethod
def _getNodeName(node: CuraSceneNode, max_length: int = 30) -> str:
node_name = node.getName() if node else ""
truncated_node_name = node_name[:max_length]
return truncated_node_name
@staticmethod
def _getSingleSelectedNode() -> Optional[CuraSceneNode]:
if len(Selection.getAllSelectedObjects()) == 1:
selected_node = Selection.getSelectedObject(0)
return selected_node
return None
def _configureEvents(self) -> None:
Selection.selectionChanged.connect(self._onSelectionChanged)
self._global_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
def _onGlobalStackChanged(self) -> None:
if self._global_stack:
self._global_stack.propertyChanged.disconnect(self._onSettingsChanged)
self._global_stack.containersChanged.disconnect(self._onSettingsChanged)
self._global_stack = Application.getInstance().getGlobalContainerStack()
if self._global_stack:
self._global_stack.propertyChanged.connect(self._onSettingsChanged)
self._global_stack.containersChanged.connect(self._onSettingsChanged)
def _onSettingsChanged(self, *args: Any) -> None:
self._settingsChanged.emit()
def _onSelectionChanged(self) -> None:
self._uiActionsOutdated.emit()

View file

@ -25,10 +25,19 @@ class CuraSceneNode(SceneNode):
if not no_setting_override:
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
self._outside_buildarea = False
self._print_order = 0
def setOutsideBuildArea(self, new_value: bool) -> None:
self._outside_buildarea = new_value
@property
def printOrder(self):
return self._print_order
@printOrder.setter
def printOrder(self, new_value):
self._print_order = new_value
def isOutsideBuildArea(self) -> bool:
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
@ -157,3 +166,6 @@ class CuraSceneNode(SceneNode):
def transformChanged(self) -> None:
self._transformChanged()
def __repr__(self) -> str:
return "{print_order}. {name}".format(print_order = self._print_order, name = self.getName())

View file

@ -316,7 +316,13 @@ class ExtruderManager(QObject):
# Starts with the adhesion extruder.
adhesion_type = global_stack.getProperty("adhesion_type", "value")
if adhesion_type in {"skirt", "brim"}:
return max(0, int(global_stack.getProperty("skirt_brim_extruder_nr", "value"))) # optional skirt/brim extruder defaults to zero
skirt_brim_extruder_nr = global_stack.getProperty("skirt_brim_extruder_nr", "value")
# if the skirt_brim_extruder_nr is -1, then we use the first used extruder
if skirt_brim_extruder_nr == -1:
used_extruders = self.getUsedExtruderStacks()
return used_extruders[0].position
else:
return skirt_brim_extruder_nr
if adhesion_type == "raft":
return global_stack.getProperty("raft_base_extruder_nr", "value")

View file

@ -5,16 +5,18 @@ import json
import os
from typing import List, Optional
from PyQt6.QtCore import QUrl
from PyQt6.QtNetwork import QLocalServer, QLocalSocket
from UM.Qt.QtApplication import QtApplication #For typing.
from UM.Qt.QtApplication import QtApplication # For typing.
from UM.Logger import Logger
class SingleInstance:
def __init__(self, application: QtApplication, files_to_open: Optional[List[str]]) -> None:
def __init__(self, application: QtApplication, files_to_open: Optional[List[str]], url_to_open: Optional[List[str]]) -> None:
self._application = application
self._files_to_open = files_to_open
self._url_to_open = url_to_open
self._single_instance_server = None
@ -33,7 +35,7 @@ class SingleInstance:
return False
# We only send the files that need to be opened.
if not self._files_to_open:
if not self._files_to_open and not self._url_to_open:
Logger.log("i", "No file need to be opened, do nothing.")
return True
@ -55,8 +57,12 @@ class SingleInstance:
payload = {"command": "open", "filePath": os.path.abspath(filename)}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
for url in self._url_to_open:
payload = {"command": "open-url", "urlPath": url.toString()}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii"))
payload = {"command": "close-connection"}
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii"))
single_instance_socket.flush()
single_instance_socket.waitForDisconnected()
@ -72,7 +78,7 @@ class SingleInstance:
def _onClientConnected(self) -> None:
Logger.log("i", "New connection received on our single-instance server")
connection = None #type: Optional[QLocalSocket]
connection = None # type: Optional[QLocalSocket]
if self._single_instance_server:
connection = self._single_instance_server.nextPendingConnection()
@ -94,6 +100,12 @@ class SingleInstance:
elif command == "open":
self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f))
#command: Load a url link in Cura
elif command == "open-url":
url = QUrl(payload["urlPath"])
self._application.callLater(lambda: self._application._openUrl(url))
# Command: Activate the window and bring it to the top.
elif command == "focus":
# Operating systems these days prevent windows from moving around by themselves.

View file

@ -14,6 +14,9 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection
from UM.i18n import i18nCatalog
from cura.PrintOrderManager import PrintOrderManager
from cura.Scene.CuraSceneNode import CuraSceneNode
catalog = i18nCatalog("cura")
@ -76,6 +79,9 @@ class ObjectsModel(ListModel):
self._build_plate_number = nr
self._update()
def getNodes(self) -> List[CuraSceneNode]:
return list(map(lambda n: n["node"], self.items))
def _updateSceneDelayed(self, source) -> None:
if not isinstance(source, Camera):
self._update_timer.start()
@ -175,6 +181,10 @@ class ObjectsModel(ListModel):
all_nodes = self._renameNodes(name_to_node_info_dict)
user_defined_print_order_enabled = PrintOrderManager.isUserDefinedPrintOrderEnabled()
if user_defined_print_order_enabled:
PrintOrderManager.initializePrintOrders(all_nodes)
for node in all_nodes:
if hasattr(node, "isOutsideBuildArea"):
is_outside_build_area = node.isOutsideBuildArea() # type: ignore
@ -223,8 +233,13 @@ class ObjectsModel(ListModel):
# for anti overhang meshes and groups the extruder nr is irrelevant
extruder_number = -1
if not user_defined_print_order_enabled:
name = node.getName()
else:
name = "{print_order}. {name}".format(print_order = node.printOrder, name = node.getName())
nodes.append({
"name": node.getName(),
"name": name,
"selected": Selection.isSelected(node),
"outside_build_area": is_outside_build_area,
"buildplate_number": node_build_plate_number,
@ -234,5 +249,5 @@ class ObjectsModel(ListModel):
"node": node
})
nodes = sorted(nodes, key=lambda n: n["name"])
nodes = sorted(nodes, key=lambda n: n["name"] if not user_defined_print_order_enabled else n["node"].printOrder)
self.setItems(nodes)

View file

@ -15,6 +15,10 @@ if "" in sys.path:
import argparse
import faulthandler
import os
# set the environment variable QT_QUICK_FLICKABLE_WHEEL_DECELERATION to 5000 as mentioned in qt6.6 update log to overcome scroll related issues
os.environ["QT_QUICK_FLICKABLE_WHEEL_DECELERATION"] = str(int(os.environ.get("QT_QUICK_FLICKABLE_WHEEL_DECELERATION", "5000")))
if sys.platform != "linux": # Turns out the Linux build _does_ use this, but we're not making an Enterprise release for that system anyway.
os.environ["QT_PLUGIN_PATH"] = "" # Security workaround: Don't need it, and introduces an attack vector, so set to nul.
os.environ["QML2_IMPORT_PATH"] = "" # Security workaround: Don't need it, and introduces an attack vector, so set to nul.

View file

@ -177,6 +177,9 @@ class ThreeMFReader(MeshReader):
else:
Logger.log("w", "Unable to find extruder in position %s", setting_value)
continue
if key == "print_order":
um_node.printOrder = int(setting_value)
continue
if key in known_setting_keys:
setting_container.setProperty(key, "value", setting_value)
else:

View file

@ -10,16 +10,14 @@ from UM.Math.Vector import Vector
from UM.Logger import Logger
from UM.Math.Matrix import Matrix
from UM.Application import Application
from UM.Message import Message
from UM.Resources import Resources
from UM.Scene.SceneNode import SceneNode
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
from cura.CuraApplication import CuraApplication
from cura.CuraPackageManager import CuraPackageManager
from cura.Settings import CuraContainerStack
from cura.Utils.Threading import call_on_qt_thread
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Snapshot import Snapshot
from PyQt6.QtCore import QBuffer
@ -137,6 +135,9 @@ class ThreeMFWriter(MeshWriter):
for key in changed_setting_keys:
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
if isinstance(um_node, CuraSceneNode):
savitar_node.setSetting("cura:print_order", str(um_node.printOrder))
# Store the metadata.
for key, value in um_node.metadata.items():
savitar_node.setSetting(key, value)

View file

@ -35,6 +35,8 @@ message Slice
repeated EnginePlugin engine_plugins = 5;
string sentry_id = 6; // The anonymized Sentry user id that requested the slice
string cura_version = 7; // The version of Cura that requested the slice
optional string project_name = 8; // The name of the project that requested the slice
optional string user_name = 9; // The Digital Factory account name of the user that requested the slice
}
message Extruder

View file

@ -76,6 +76,7 @@ class CuraEngineBackend(QObject, Backend):
self._default_engine_location = executable_name
search_path = [
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..", "Resources")),
os.path.abspath(os.path.dirname(sys.executable)),
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "bin")),
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
@ -164,6 +165,7 @@ class CuraEngineBackend(QObject, Backend):
application.getPreferences().addPreference("general/auto_slice", False)
application.getPreferences().addPreference("info/send_engine_crash", True)
application.getPreferences().addPreference("info/anonymous_engine_crash_report", True)
self._use_timer: bool = False
@ -1094,14 +1096,14 @@ class CuraEngineBackend(QObject, Backend):
self._change_timer.timeout.disconnect(self.slice)
def _onPreferencesChanged(self, preference: str) -> None:
if preference != "general/auto_slice" and preference != "info/send_engine_crash":
if preference != "general/auto_slice" and preference != "info/send_engine_crash" and preference != "info/anonymous_engine_crash_report":
return
if preference == "general/auto_slice":
auto_slice = self.determineAutoSlicing()
if auto_slice:
self._change_timer.start()
elif preference == "info/send_engine_crash":
os.environ["use_sentry"] = "1" if CuraApplication.getInstance().getPreferences().getValue("info/send_engine_crash") else "0"
os.environ["USE_SENTRY"] = "1" if CuraApplication.getInstance().getPreferences().getValue("info/send_engine_crash") else "0"
def tickle(self) -> None:
"""Tickle the backend so in case of auto slicing, it starts the timer."""

View file

@ -1,4 +1,4 @@
# Copyright (c) 2023 UltiMaker
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
import uuid
@ -63,13 +63,12 @@ class GcodeStartEndFormatter(Formatter):
# will be used. Alternatively, if the expression is formatted as "{[expression], [extruder_nr]}",
# then the expression will be evaluated with the extruder stack of the specified extruder_nr.
_extruder_regex = re.compile(r"^\s*(?P<expression>.*)\s*,\s*(?P<extruder_nr>\d+)\s*$")
_extruder_regex = re.compile(r"^\s*(?P<expression>.*)\s*,\s*(?P<extruder_nr_expr>.*)\s*$")
def __init__(self, default_extruder_nr: int = -1, *,
additional_per_extruder_settings: Optional[Dict[str, Dict[str, any]]] = None) -> None:
def __init__(self, all_extruder_settings: Dict[str, Any], default_extruder_nr: int = -1) -> None:
super().__init__()
self._all_extruder_settings: Dict[str, Any] = all_extruder_settings
self._default_extruder_nr: int = default_extruder_nr
self._additional_per_extruder_settings: Optional[Dict[str, Dict[str, any]]] = additional_per_extruder_settings
def get_field(self, field_name, args: [str], kwargs: dict) -> Tuple[str, str]:
# get_field method parses all fields in the format-string and parses them individually to the get_value method.
@ -88,22 +87,32 @@ class GcodeStartEndFormatter(Formatter):
if expression in post_slice_data_variables:
return f"{{{expression}}}"
extruder_nr = self._default_extruder_nr
extruder_nr = str(self._default_extruder_nr)
# The settings may specify a specific extruder to use. This is done by
# formatting the expression as "{expression}, {extruder_nr}". If the
# formatting the expression as "{expression}, {extruder_nr_expr}". If the
# expression is formatted like this, we extract the extruder_nr and use
# it to get the value from the correct extruder stack.
match = self._extruder_regex.match(expression)
if match:
expression = match.group("expression")
extruder_nr = int(match.group("extruder_nr"))
extruder_nr_expr = match.group("extruder_nr_expr")
if self._additional_per_extruder_settings is not None and str(
extruder_nr) in self._additional_per_extruder_settings:
additional_variables = self._additional_per_extruder_settings[str(extruder_nr)]
if extruder_nr_expr.isdigit():
extruder_nr = extruder_nr_expr
else:
additional_variables = dict()
# We get the value of the extruder_nr_expr from `_all_extruder_settings` dictionary
# rather than the global container stack. The `_all_extruder_settings["-1"]` is a
# dict-representation of the global container stack, with additional properties such
# as `initial_extruder_nr`. As users may enter such expressions we can't use the
# global container stack.
extruder_nr = str(self._all_extruder_settings["-1"].get(extruder_nr_expr, "-1"))
if extruder_nr in self._all_extruder_settings:
additional_variables = self._all_extruder_settings[extruder_nr].copy()
else:
Logger.warning(f"Extruder {extruder_nr} does not exist, using global settings")
additional_variables = self._all_extruder_settings["-1"].copy()
# Add the arguments and keyword arguments to the additional settings. These
# are currently _not_ used, but they are added for consistency with the
@ -113,15 +122,17 @@ class GcodeStartEndFormatter(Formatter):
for key, value in kwargs.items():
additional_variables[key] = value
if extruder_nr == -1:
if extruder_nr == "-1":
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
else:
container_stack = ExtruderManager.getInstance().getExtruderStack(extruder_nr)
if not container_stack:
Logger.warning(f"Extruder {extruder_nr} does not exist, using global settings")
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
setting_function = SettingFunction(expression)
value = setting_function(container_stack, additional_variables=additional_variables)
return value
@ -131,12 +142,13 @@ class StartSliceJob(Job):
def __init__(self, slice_message: Arcus.PythonMessage) -> None:
super().__init__()
self._scene = CuraApplication.getInstance().getController().getScene() #type: Scene
self._scene: Scene = CuraApplication.getInstance().getController().getScene()
self._slice_message: Arcus.PythonMessage = slice_message
self._is_cancelled = False #type: bool
self._build_plate_number = None #type: Optional[int]
self._is_cancelled: bool = False
self._build_plate_number: Optional[int] = None
self._all_extruders_settings = None #type: Optional[Dict[str, Any]] # cache for all setting values from all stacks (global & extruder) for the current machine
# cache for all setting values from all stacks (global & extruder) for the current machine
self._all_extruders_settings: Optional[Dict[str, Any]] = None
def getSliceMessage(self) -> Arcus.PythonMessage:
return self._slice_message
@ -340,6 +352,12 @@ class StartSliceJob(Job):
self._slice_message.sentry_id = f"{user_id}"
self._slice_message.cura_version = CuraVersion
# Add the project name to the message if the user allows for non-anonymous crash data collection.
account = CuraApplication.getInstance().getCuraAPI().account
if account and account.isLoggedIn and not CuraApplication.getInstance().getPreferences().getValue("info/anonymous_engine_crash_report"):
self._slice_message.project_name = CuraApplication.getInstance().getPrintInformation().baseName
self._slice_message.user_name = account.userName
# Build messages for extruder stacks
for extruder_stack in global_stack.extruderList:
self._buildExtruderMessage(extruder_stack)
@ -471,10 +489,7 @@ class StartSliceJob(Job):
# Get "replacement-keys" for the extruders. In the formatter the settings stack is used to get the
# replacement values for the setting-keys. However, the values for `material_id`, `material_type`,
# etc are not in the settings stack.
additional_per_extruder_settings = self._all_extruders_settings.copy()
additional_per_extruder_settings["default_extruder_nr"] = default_extruder_nr
fmt = GcodeStartEndFormatter(default_extruder_nr=default_extruder_nr,
additional_per_extruder_settings=additional_per_extruder_settings)
fmt = GcodeStartEndFormatter(self._all_extruders_settings, default_extruder_nr=default_extruder_nr)
return str(fmt.format(value))
except:
Logger.logException("w", "Unable to do token replacement on start/end g-code")

View file

@ -1,7 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from .src import DigitalFactoryFileProvider, DigitalFactoryOutputDevicePlugin, DigitalFactoryController

View file

@ -3,7 +3,6 @@
import json
from json import JSONDecodeError
import re
from time import time
from typing import List, Any, Optional, Union, Type, Tuple, Dict, cast, TypeVar, Callable

View file

@ -4,7 +4,6 @@ from typing import List, Optional
from PyQt6.QtCore import Qt, pyqtSignal
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse

View file

@ -2,7 +2,6 @@
# Cura is released under the terms of the LGPLv3 or higher.
from UM.i18n import i18nCatalog
from UM.Platform import Platform
from . import GCodeGzWriter

View file

@ -11,7 +11,6 @@ from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.ContainerTree import ContainerTree
from UM.i18n import i18nCatalog
from cura.Settings.CuraStackBuilder import CuraStackBuilder
catalog = i18nCatalog("cura")

View file

@ -3,12 +3,10 @@
from typing import Optional, TYPE_CHECKING, Dict, List
from .Constants import PACKAGES_URL
from .PackageModel import PackageModel
from .RemotePackageList import RemotePackageList
from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To request the package list from the API.
from UM.i18n import i18nCatalog
if TYPE_CHECKING:

View file

@ -2,7 +2,6 @@
# Cura is released under the terms of the LGPLv3 or higher.
import re
from enum import Enum
from typing import Any, cast, Dict, List, Optional
from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal, pyqtSlot
@ -12,7 +11,6 @@ from cura.CuraApplication import CuraApplication
from cura.CuraPackageManager import CuraPackageManager
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To get names of materials we're compatible with.
from UM.i18n import i18nCatalog # To translate placeholder names if data is not present.
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry
catalog = i18nCatalog("cura")

View file

@ -25,7 +25,7 @@ UM.TooltipArea
onClicked:
{
addedSettingsModel.setVisible(model.key, checked);
UM.ActiveTool.forceUpdate();
UM.Controller.forceUpdate();
}
}

View file

@ -11,7 +11,7 @@ from UM.Settings.SettingInstance import SettingInstance
from UM.Logger import Logger
import UM.Settings.Models.SettingVisibilityHandler
from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherits-stack setting values from different extruders.
from cura.Settings.ExtruderManager import ExtruderManager # To get global-inherits-stack setting values from different extruders.
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator

View file

@ -23,7 +23,7 @@ Item
readonly property string infillMeshType: "infill_mesh"
readonly property string antiOverhangMeshType: "anti_overhang_mesh"
property var currentMeshType: UM.ActiveTool.properties.getValue("MeshType")
property var currentMeshType: UM.Controller.properties.getValue("MeshType")
// Update the view every time the currentMeshType changes
onCurrentMeshTypeChanged:
@ -56,7 +56,7 @@ Item
function setMeshType(type)
{
UM.ActiveTool.setProperty("MeshType", type)
UM.Controller.setProperty("MeshType", type)
updateMeshTypeCheckedState(type)
}
@ -224,7 +224,7 @@ Item
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
{
id: visibility_handler
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
selectedObjectId: UM.Controller.properties.getValue("SelectedObjectId")
}
// For some reason the model object is updated after removing him from the memory and
@ -320,7 +320,7 @@ Item
{
id: provider
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
containerStackId: UM.Controller.properties.getValue("ContainerID")
key: model.key
watchedProperties: [ "value", "enabled", "validationState" ]
storeIndex: 0
@ -330,7 +330,7 @@ Item
UM.SettingPropertyProvider
{
id: inheritStackProvider
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
containerStackId: UM.Controller.properties.getValue("ContainerID")
key: model.key
watchedProperties: [ "limit_to_extruder" ]
}
@ -381,22 +381,22 @@ Item
Connections
{
target: UM.ActiveTool
target: UM.Controller
function onPropertiesChanged()
{
// the values cannot be bound with UM.ActiveTool.properties.getValue() calls,
// the values cannot be bound with UM.Controller.properties.getValue() calls,
// so here we connect to the signal and update the those values.
if (typeof UM.ActiveTool.properties.getValue("SelectedObjectId") !== "undefined")
if (typeof UM.Controller.properties.getValue("SelectedObjectId") !== "undefined")
{
const selectedObjectId = UM.ActiveTool.properties.getValue("SelectedObjectId")
const selectedObjectId = UM.Controller.properties.getValue("SelectedObjectId")
if (addedSettingsModel.visibilityHandler.selectedObjectId != selectedObjectId)
{
addedSettingsModel.visibilityHandler.selectedObjectId = selectedObjectId
}
}
if (typeof UM.ActiveTool.properties.getValue("ContainerID") !== "undefined")
if (typeof UM.Controller.properties.getValue("ContainerID") !== "undefined")
{
const containerId = UM.ActiveTool.properties.getValue("ContainerID")
const containerId = UM.Controller.properties.getValue("ContainerID")
if (provider.containerStackId !== containerId)
{
provider.containerStackId = containerId

View file

@ -21,7 +21,7 @@
# M163 - Set Mix Factor
# M164 - Save Mix - saves to T2 as a unique mix
import re #To perform the search and replace.
import re # To perform the search and replace.
from ..Script import Script
class ColorMix(Script):

View file

@ -6,7 +6,6 @@
# Description: This plugin is now an option in 'Display Info on LCD'
from ..Script import Script
from UM.Application import Application
from UM.Message import Message
class DisplayFilenameAndLayerOnLCD(Script):

View file

@ -30,9 +30,6 @@
from ..Script import Script
from UM.Application import Application
from UM.Qt.Duration import DurationFormat
import UM.Util
import configparser
from UM.Preferences import Preferences
import time
import datetime
import math

View file

@ -7,8 +7,6 @@
from ..Script import Script
import re
import datetime
from UM.Message import Message
class DisplayProgressOnLCD(Script):

View file

@ -7,7 +7,7 @@
from typing import List
from ..Script import Script
from UM.Application import Application #To get the current printer's settings.
from UM.Application import Application # To get the current printer's settings.
class FilamentChange(Script):

View file

@ -7,7 +7,7 @@
from ..Script import Script
import re
from UM.Application import Application #To get the current printer's settings.
from UM.Application import Application # To get the current printer's settings.
from UM.Logger import Logger
from typing import List, Tuple

View file

@ -1,7 +1,7 @@
# Copyright (c) 2017 Ghostkeeper
# The PostProcessingPlugin is released under the terms of the LGPLv3 or higher.
import re #To perform the search and replace.
import re # To perform the search and replace.
from ..Script import Script

View file

@ -8,7 +8,7 @@ from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.FileHandler.WriteFileJob import WriteFileJob
from UM.FileHandler.FileWriter import FileWriter #To check against the write modes (text vs. binary).
from UM.FileHandler.FileWriter import FileWriter # To check against the write modes (text vs. binary).
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.OutputDevice.OutputDevice import OutputDevice
from UM.OutputDevice import OutputDeviceError

View file

@ -372,7 +372,10 @@ class SimulationView(CuraView):
self._minimum_path_num = min(self._minimum_path_num, self._current_path_num)
# update _current time when the path is changed by user
if self._current_path_num < self._max_paths and round(self._current_path_num)== self._current_path_num:
self._current_time = self.cumulativeLineDuration()[int(self._current_path_num)]
actual_path_num = int(self._current_path_num)
cumulative_line_duration = self.cumulativeLineDuration()
if actual_path_num < len(cumulative_line_duration):
self._current_time = cumulative_line_duration[actual_path_num]
self._startUpdateTopLayers()
self.currentPathNumChanged.emit()

View file

@ -5,7 +5,7 @@ import json
import os
import platform
import time
from typing import cast, Optional, Set, TYPE_CHECKING
from typing import Optional, Set, TYPE_CHECKING
from PyQt6.QtCore import pyqtSlot, QObject
from PyQt6.QtNetwork import QNetworkRequest

View file

@ -16,8 +16,6 @@ from UM.Application import Application
from UM.Logger import Logger
from UM.Message import Message
from UM.Math.Color import Color
from UM.PluginRegistry import PluginRegistry
from UM.Platform import Platform
from UM.Event import Event
from UM.View.RenderBatch import RenderBatch

View file

@ -22,7 +22,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Settings.InstanceContainer import InstanceContainer
from cura.CuraApplication import CuraApplication
from cura.Settings.CuraStackBuilder import CuraStackBuilder
from cura.Settings.GlobalStack import GlobalStack
from cura.Utils.Threading import call_on_qt_thread

View file

@ -9,8 +9,8 @@ try:
except ImportError:
Logger.log("w", "Could not import UFPWriter; libCharon may be missing")
from UM.i18n import i18nCatalog #To translate the file format description.
from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag.
from UM.i18n import i18nCatalog # To translate the file format description.
from UM.Mesh.MeshWriter import MeshWriter # For the binary mode flag.
i18n_catalog = i18nCatalog("cura")

View file

@ -4,9 +4,6 @@
from UM.Job import Job
from UM.Logger import Logger
from .avr_isp import ispBase
from .avr_isp.stk500v2 import Stk500v2
from time import time, sleep
from serial import Serial, SerialException

View file

@ -5,9 +5,9 @@ import os
from UM.i18n import i18nCatalog
from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter #To get the g-code output.
from UM.Message import Message #Show an error when already printing.
from UM.PluginRegistry import PluginRegistry #To get the g-code output.
from UM.Mesh.MeshWriter import MeshWriter # To get the g-code output.
from UM.Message import Message # Show an error when already printing.
from UM.PluginRegistry import PluginRegistry # To get the g-code output.
from UM.Qt.Duration import DurationFormat
from cura.CuraApplication import CuraApplication
@ -19,7 +19,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
from .AutoDetectBaudJob import AutoDetectBaudJob
from .AvrFirmwareUpdater import AvrFirmwareUpdater
from io import StringIO #To write the g-code output.
from io import StringIO # To write the g-code output.
from queue import Queue
from serial import Serial, SerialException, SerialTimeoutException
from threading import Thread, Event

View file

@ -1,16 +1,16 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import configparser #To read config files.
import io #To write config files to strings as if they were files.
import os.path #To get the path to write new user profiles to.
import configparser # To read config files.
import io # To write config files to strings as if they were files.
import os.path # To get the path to write new user profiles to.
from typing import Dict, List, Optional, Set, Tuple
import urllib #To serialise the user container file name properly.
import urllib # To serialise the user container file name properly.
import urllib.parse
import UM.VersionUpgrade #To indicate that a file is of incorrect format.
import UM.VersionUpgradeManager #To schedule more files to be upgraded.
from UM.Resources import Resources #To get the config storage path.
import UM.VersionUpgrade # To indicate that a file is of incorrect format.
import UM.VersionUpgradeManager # To schedule more files to be upgraded.
from UM.Resources import Resources # To get the config storage path.
## Creates a new machine instance instance by parsing a serialised machine
# instance in version 1 of the file format.

View file

@ -1,11 +1,11 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import configparser #To read config files.
import io #To output config files to string.
import configparser # To read config files.
import io # To output config files to string.
from typing import List, Optional, Tuple
import UM.VersionUpgrade #To indicate that a file is of the wrong format.
import UM.VersionUpgrade # To indicate that a file is of the wrong format.
## Creates a new preferences instance by parsing a serialised preferences file
# in version 1 of the file format.

View file

@ -1,8 +1,8 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import configparser #To read config files.
import io #To write config files to strings as if they were files.
import configparser # To read config files.
import io # To write config files to strings as if they were files.
from typing import Dict, List, Optional, Tuple
import UM.VersionUpgrade

View file

@ -0,0 +1,95 @@
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
import configparser
from typing import Tuple, List
import io
from UM.VersionUpgrade import VersionUpgrade
_REMOVED_SETTINGS = {
"support_interface_skip_height",
}
_NEW_SETTING_VERSION = "23"
class VersionUpgrade56to57(VersionUpgrade):
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades preferences to remove from the visibility list the settings that were removed in this version.
It also changes the preferences to have the new version number.
This removes any settings that were removed in the new Cura version.
:param serialized: The original contents of the preferences file.
:param filename: The file name of the preferences file.
:return: A list of new file names, and a list of the new contents for
those files.
"""
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
parser["metadata"]["setting_version"] = _NEW_SETTING_VERSION
# Remove deleted settings from the visible settings list.
if "general" in parser and "visible_settings" in parser["general"]:
visible_settings = set(parser["general"]["visible_settings"].split(";"))
for removed in _REMOVED_SETTINGS:
if removed in visible_settings:
visible_settings.remove(removed)
parser["general"]["visible_settings"] = ";".join(visible_settings)
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades instance containers to remove the settings that were removed in this version.
It also changes the instance containers to have the new version number.
This removes any settings that were removed in the new Cura version and updates settings that need to be updated
with a new value.
:param serialized: The original contents of the instance container.
:param filename: The original file name of the instance container.
:return: A list of new file names, and a list of the new contents for
those files.
"""
parser = configparser.ConfigParser(interpolation = None, comment_prefixes = ())
parser.read_string(serialized)
# Update version number.
parser["metadata"]["setting_version"] = _NEW_SETTING_VERSION
if "values" in parser:
# Remove deleted settings from the instance containers.
for removed in _REMOVED_SETTINGS:
if removed in parser["values"]:
del parser["values"][removed]
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]
def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""
Upgrades stacks to have the new version number.
:param serialized: The original contents of the stack.
:param filename: The original file name of the stack.
:return: A list of new file names, and a list of the new contents for
those files.
"""
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialized)
# Update version number.
if "metadata" not in parser:
parser["metadata"] = {}
parser["metadata"]["setting_version"] = _NEW_SETTING_VERSION
result = io.StringIO()
parser.write(result)
return [filename], [result.getvalue()]

View file

@ -0,0 +1,61 @@
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Any, Dict, TYPE_CHECKING
from . import VersionUpgrade56to57
if TYPE_CHECKING:
from UM.Application import Application
upgrade = VersionUpgrade56to57.VersionUpgrade56to57()
def getMetaData() -> Dict[str, Any]:
return {
"version_upgrade": {
# From To Upgrade function
("preferences", 7000022): ("preferences", 7000023, upgrade.upgradePreferences),
("machine_stack", 6000022): ("machine_stack", 6000023, upgrade.upgradeStack),
("extruder_train", 6000022): ("extruder_train", 6000023, upgrade.upgradeStack),
("definition_changes", 4000022): ("definition_changes", 4000023, upgrade.upgradeInstanceContainer),
("quality_changes", 4000022): ("quality_changes", 4000023, upgrade.upgradeInstanceContainer),
("quality", 4000022): ("quality", 4000023, upgrade.upgradeInstanceContainer),
("user", 4000022): ("user", 4000023, upgrade.upgradeInstanceContainer),
("intent", 4000022): ("intent", 4000023, upgrade.upgradeInstanceContainer),
},
"sources": {
"preferences": {
"get_version": upgrade.getCfgVersion,
"location": {"."}
},
"machine_stack": {
"get_version": upgrade.getCfgVersion,
"location": {"./machine_instances"}
},
"extruder_train": {
"get_version": upgrade.getCfgVersion,
"location": {"./extruders"}
},
"definition_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./definition_changes"}
},
"quality_changes": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality_changes"}
},
"quality": {
"get_version": upgrade.getCfgVersion,
"location": {"./quality"}
},
"user": {
"get_version": upgrade.getCfgVersion,
"location": {"./user"}
}
}
}
def register(app: "Application") -> Dict[str, Any]:
return {"version_upgrade": upgrade}

View file

@ -0,0 +1,8 @@
{
"name": "Version Upgrade 5.6 to 5.7",
"author": "UltiMaker",
"version": "1.0.0",
"description": "Upgrades configurations from Cura 5.6 to Cura 5.7.",
"api": 8,
"i18n-catalog": "cura"
}

View file

@ -7,7 +7,6 @@ from PyQt6.QtGui import QOpenGLContext, QImage
from UM.Application import Application
from UM.Logger import Logger
from UM.Math.Color import Color
from UM.PluginRegistry import PluginRegistry
from UM.Resources import Resources
from UM.Platform import Platform
from UM.Event import Event

View file

@ -3,9 +3,9 @@
import copy
import io
import json #To parse the product-to-id mapping file.
import os.path #To find the product-to-id mapping.
from typing import Any, Dict, List, Optional, Tuple, cast, Set, Union
import json # To parse the product-to-id mapping file.
import os.path # To find the product-to-id mapping.
from typing import Any, Dict, List, Optional, Tuple, cast, Set
import xml.etree.ElementTree as ET
from UM.PluginRegistry import PluginRegistry

View file

@ -1,5 +1,5 @@
pytest
pyinstaller==5.8.0
pyinstaller==6.3.0
pyinstaller-hooks-contrib
pyyaml
sip==6.5.1

View file

@ -112,7 +112,6 @@
"support_interface_density": { "value": 33.333 },
"support_interface_enable": { "value": true },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_roof_enable": { "value": true },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
"support_xy_distance_overhang": { "value": "wall_line_width_0" },

View file

@ -114,7 +114,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },

View file

@ -154,7 +154,6 @@
"support_infill_rate": { "value": "20" },
"support_interface_enable": { "value": "True" },
"support_interface_height": { "value": "1" },
"support_interface_skip_height": { "value": "layer_height" },
"support_join_distance": { "value": "1" },
"support_offset": { "value": "1.5" },
"support_pattern": { "default_value": "zigzag" },

View file

@ -108,7 +108,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -97,7 +97,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
"support_xy_distance_overhang": { "value": "wall_line_width_0" },

View file

@ -9,16 +9,20 @@
},
"overrides":
{
"acceleration_layer_0": { "value": 3000 },
"acceleration_print": { "value": 3000 },
"acceleration_travel": { "value": 5000 },
"acceleration_print":
{
"maximum_value_warning": "20000",
"value": 10000
},
"acceleration_wall": { "value": "acceleration_print/2" },
"cool_fan_full_layer": { "value": 2 },
"infill_line_width": { "value": "line_width + 0.05" },
"infill_overlap": { "value": "0 if infill_sparse_density < 40.01 and infill_pattern != 'concentric' else -5" },
"infill_pattern": { "value": "'lines' if infill_sparse_density > 35 else 'grid'" },
"initial_layer_line_width_factor": { "value": "100.0 if resolveOrValue('adhesion_type') == 'raft' else 125 if line_width < 0.5 else 110" },
"machine_acceleration": { "value": 3000 },
"machine_acceleration": { "value": 5000 },
"machine_depth": { "default_value": 230 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth - 5} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_head_with_fans_polygon":
{
@ -32,12 +36,12 @@
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 270 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 5000 },
"machine_max_acceleration_y": { "value": 5000 },
"machine_max_acceleration_x": { "value": 20000 },
"machine_max_acceleration_y": { "value": 20000 },
"machine_name": { "default_value": "ELEGOO NEPTUNE 4" },
"machine_nozzle_cool_down_speed": { "value": 0.75 },
"machine_nozzle_heat_up_speed": { "value": 1.6 },
"machine_start_gcode": { "default_value": "G28 ;home\nG92 E0 ;Reset Extruder\nG1 Z4.0 F3000 ;Move Z Axis up\nG92 E0 ;Reset Extruder\nG1 X1.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X1.1 Y80.0 Z0.28 F1500.0 E10 ;Draw the first line\nG1 X1.4 Y80.0 Z0.28 F5000.0 ;Move to side a little\nG1 X1.4 Y20 Z0.28 F1500.0 E20 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up" },
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 / 4 PRO\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X67.5 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X67.5 Y0 Z0.4 F300 ;Move to start position\nG1 X167.5 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X162.5 F3000\nG92 E0 ;Reset Extruder" },
"machine_width": { "default_value": 235 },
"retraction_amount": { "default_value": 0.5 },
"retraction_count_max": { "value": 80 },

View file

@ -0,0 +1,62 @@
{
"version": 2,
"name": "ELEGOO NEPTUNE 4 Max",
"inherits": "elegoo_neptune_4",
"metadata":
{
"visible": true,
"author": "mastercaution",
"platform": "elegoo_platform_max.3mf",
"platform_offset": [
-2.1,
-0.2,
0
],
"quality_definition": "elegoo_neptune_4"
},
"overrides":
{
"acceleration_print":
{
"maximum_value_warning": "15000",
"value": 2500
},
"machine_depth": { "default_value": 430 },
"machine_height": { "default_value": 482 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 15000 },
"machine_max_acceleration_y": { "value": 15000 },
"machine_name": { "default_value": "ELEGOO NEPTUNE 4 Max" },
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 MAX\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X165 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X165 Y0 Z0.4 F300 ;Move to start position\nG1 X265 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X260 F3000\nG92 E0 ;Reset Extruder" },
"machine_width": { "default_value": 430 },
"nozzle_disallowed_areas":
{
"default_value": [
[
[-215, -215],
[-215, 215],
[-211, 215],
[-211, -215]
],
[
[215, 215],
[215, -215],
[211, -215],
[211, 215]
],
[
[-215, -215],
[215, -215],
[-215, -211],
[215, -211]
],
[
[-215, 215],
[215, 215],
[-215, 211],
[215, 211]
]
]
}
}
}

View file

@ -0,0 +1,59 @@
{
"version": 2,
"name": "ELEGOO NEPTUNE 4 Plus",
"inherits": "elegoo_neptune_4",
"metadata":
{
"visible": true,
"author": "mastercaution",
"platform": "elegoo_platform_max.3mf",
"platform_offset": [
-2.1,
-0.2,
0
],
"quality_definition": "elegoo_neptune_4"
},
"overrides":
{
"acceleration_print": { "value": 4000 },
"machine_acceleration": { "value": 4000 },
"machine_depth": { "default_value": 330 },
"machine_height": { "default_value": 387 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 20000 },
"machine_max_acceleration_y": { "value": 20000 },
"machine_name": { "default_value": "ELEGOO NEPTUNE 4 Plus" },
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 PLUS\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X115 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X115 Y0 Z0.4 F300 ;Move to start position\nG1 X215 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X210 F3000\nG92 E0 ;Reset Extruder" },
"machine_width": { "default_value": 330 },
"nozzle_disallowed_areas":
{
"default_value": [
[
[-165, -165],
[-165, 165],
[-161, 165],
[-161, -165]
],
[
[165, 165],
[165, -165],
[161, -165],
[161, 165]
],
[
[-165, -165],
[165, -165],
[-165, -161],
[165, -161]
],
[
[-165, 165],
[165, 165],
[-165, 161],
[165, 161]
]
]
}
}
}

View file

@ -1668,7 +1668,7 @@
"value": "skin_line_width * 2",
"default_value": 1,
"minimum_value": "0",
"maximum_value_warning": "skin_line_width * 3",
"maximum_value_warning": "skin_line_width * 10",
"type": "float",
"enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'",
"limit_to_extruder": "top_bottom_extruder_nr",
@ -5423,20 +5423,6 @@
}
}
},
"support_interface_skip_height":
{
"label": "Support Interface Resolution",
"description": "When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values may cause normal support to be printed in some places where there should have been support interface.",
"unit": "mm",
"type": "float",
"default_value": 0.2,
"value": "layer_height",
"minimum_value": "0",
"maximum_value_warning": "support_interface_height",
"limit_to_extruder": "support_interface_extruder_nr",
"enabled": "support_interface_enable and (support_enable or support_meshes_present)",
"settable_per_mesh": true
},
"support_interface_density":
{
"label": "Support Interface Density",
@ -6467,6 +6453,18 @@
"settable_per_extruder": true,
"limit_to_extruder": "raft_surface_extruder_nr"
},
"raft_surface_monotonic":
{
"label": "Monotonic Raft Top Surface Order",
"description": "Print raft top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes the surface look more consistent, which is also visible on the model bottom surface.",
"type": "bool",
"default_value": false,
"value": "skin_monotonic",
"enabled": "resolveOrValue('adhesion_type') == 'raft' and raft_surface_layers > 0",
"settable_per_mesh": false,
"settable_per_extruder": true,
"limit_to_extruder": "raft_surface_extruder_nr"
},
"raft_wall_count":
{
"label": "Raft Wall Count",
@ -7248,6 +7246,16 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"user_defined_print_order_enabled":
{
"label": "Set Print Sequence Manually",
"description": "Allows to order the object list to set the print sequence manually. First object from the list will be printed first.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
"settable_per_extruder": false,
"enabled": "print_sequence == 'one_at_a_time'"
},
"infill_mesh":
{
"label": "Infill Mesh",

View file

@ -0,0 +1,15 @@
{
"version": 2,
"name": "Adventurer 3",
"inherits": "flashforge_adventurer3c",
"metadata":
{
"visible": true,
"author": "Jeremie-C",
"supports_network_connection": true
},
"overrides":
{
"machine_name": { "default_value": "Adventurer 3" }
}
}

View file

@ -0,0 +1,33 @@
{
"version": 2,
"name": "Adventurer 3C",
"inherits": "flashforge_adventurer_base",
"metadata":
{
"visible": true,
"author": "Jeremie-C",
"quality_definition": "flashforge_adventurer3"
},
"overrides":
{
"default_material_bed_temperature": { "maximum_value_warning": "100" },
"gantry_height": { "value": "150" },
"machine_center_is_zero": { "default_value": true },
"machine_depth": { "default_value": 150 },
"machine_end_gcode": { "default_value": ";end gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM132 X Y A B\nM652\nG91\nM18" },
"machine_head_with_fans_polygon":
{
"default_value": [
[-20, 10],
[-20, -10],
[10, 10],
[10, -10]
]
},
"machine_height": { "default_value": 150 },
"machine_name": { "default_value": "Adventurer 3C" },
"machine_start_gcode": { "default_value": ";Start Gcode\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\n;End Start" },
"machine_width": { "default_value": 150 },
"speed_print": { "maximum_value_warning": 100 }
}
}

View file

@ -0,0 +1,33 @@
{
"version": 2,
"name": "Adventurer 4",
"inherits": "flashforge_adventurer_base",
"metadata":
{
"visible": true,
"author": "Jeremie-C",
"quality_definition": "flashforge_adventurer4",
"supports_network_connection": true
},
"overrides":
{
"default_material_bed_temperature": { "maximum_value_warning": "110" },
"gantry_height": { "value": "250" },
"machine_depth": { "default_value": 200 },
"machine_end_gcode": { "default_value": ";End Gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM132 X Y A B\nM652\nG91\nM18" },
"machine_head_with_fans_polygon":
{
"default_value": [
[-20, 10],
[-20, -10],
[10, 10],
[10, -10]
]
},
"machine_height": { "default_value": 250 },
"machine_name": { "default_value": "Adventurer 4" },
"machine_start_gcode": { "default_value": ";Start Gcode\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\n;End Start" },
"machine_use_extruder_offset_to_offset_coords": { "default_value": false },
"machine_width": { "default_value": 220 }
}
}

View file

@ -0,0 +1,14 @@
{
"version": 2,
"name": "Adventurer 4 Lite",
"inherits": "flashforge_adventurer4",
"metadata":
{
"visible": true,
"author": "Jeremie-C"
},
"overrides":
{
"machine_name": { "default_value": "Adventurer 4 Lite" }
}
}

View file

@ -0,0 +1,34 @@
{
"version": 2,
"name": "Flashforge Adventurer Base",
"inherits": "fdmprinter",
"metadata":
{
"visible": false,
"author": "Jeremie-C",
"manufacturer": "Flashforge",
"file_formats": "application/gx;text/x-gcode",
"first_start_actions": [ "MachineSettingsAction" ],
"has_machine_quality": true,
"has_materials": true,
"has_variants": true,
"machine_extruder_trains": { "0": "flashforge_adventurer_extruder_0" },
"preferred_material": "generic_pla",
"preferred_quality_type": "normal",
"preferred_variant_name": "0.4mm Nozzle",
"variants_name": "Nozzle Size"
},
"overrides":
{
"adhesion_type": { "default_value": "skirt" },
"default_material_print_temperature": { "maximum_value_warning": "265" },
"layer_height":
{
"maximum_value_warning": "0.4",
"minimum_value_warning": "0.1"
},
"machine_center_is_zero": { "default_value": true },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": true }
}
}

View file

@ -92,7 +92,6 @@
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_line_width": { "value": "line_width - 0.1" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -112,7 +112,6 @@
"support_interface_density": { "value": 33.333 },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 0 },
"support_xy_distance": { "value": "wall_line_width_0 * 3" },

View file

@ -133,7 +133,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -70,7 +70,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -88,7 +88,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -88,7 +88,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -109,7 +109,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -119,7 +119,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -0,0 +1,20 @@
{
"version": 2,
"name": "RatRig Printer",
"inherits": "fdmprinter",
"metadata":
{
"visible": false,
"author": "nu-hin",
"manufacturer": "RatRig",
"file_formats": "text/x-gcode",
"exclude_materials": [],
"first_start_actions": [ "MachineSettingsAction" ],
"has_materials": true,
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
"preferred_material": "generic_pla",
"preferred_quality_type": "standard",
"quality_definition": "ratrig_base",
"supported_actions": [ "MachineSettingsAction" ]
}
}

View file

@ -0,0 +1,23 @@
{
"version": 2,
"name": "RatRig V-Core 3 200mm",
"inherits": "ratrig_vcore3_base",
"metadata":
{
"visible": true,
"platform": "ratrig_vcore3_200.stl",
"platform_offset": [
0,
5,
0
],
"weight": 16
},
"overrides":
{
"machine_depth": { "default_value": 200 },
"machine_height": { "default_value": 200 },
"machine_name": { "default_value": "RatRig V-Core 3 200mm" },
"machine_width": { "default_value": 200 }
}
}

View file

@ -0,0 +1,23 @@
{
"version": 2,
"name": "RatRig V-Core 3 300mm",
"inherits": "ratrig_vcore3_base",
"metadata":
{
"visible": true,
"platform": "ratrig_vcore3_300.stl",
"platform_offset": [
0,
5,
0
],
"weight": 16
},
"overrides":
{
"machine_depth": { "default_value": 300 },
"machine_height": { "default_value": 300 },
"machine_name": { "default_value": "RatRig V-Core 3 300mm" },
"machine_width": { "default_value": 300 }
}
}

View file

@ -0,0 +1,23 @@
{
"version": 2,
"name": "RatRig V-Core 3 400mm",
"inherits": "ratrig_vcore3_base",
"metadata":
{
"visible": true,
"platform": "ratrig_vcore3_400.stl",
"platform_offset": [
0,
3,
0
],
"weight": 16
},
"overrides":
{
"machine_depth": { "default_value": 400 },
"machine_height": { "default_value": 400 },
"machine_name": { "default_value": "RatRig V-Core 3 400mm" },
"machine_width": { "default_value": 400 }
}
}

View file

@ -0,0 +1,23 @@
{
"version": 2,
"name": "RatRig V-Core 3 500mm",
"inherits": "ratrig_vcore3_base",
"metadata":
{
"visible": true,
"platform": "ratrig_vcore3_500.stl",
"platform_offset": [
0,
0,
0
],
"weight": 16
},
"overrides":
{
"machine_depth": { "default_value": 500 },
"machine_height": { "default_value": 500 },
"machine_name": { "default_value": "RatRig V-Core 3 500mm" },
"machine_width": { "default_value": 500 }
}
}

View file

@ -0,0 +1,116 @@
{
"version": 2,
"name": "RatRig V-Core 3",
"inherits": "ratrig_base",
"metadata":
{
"visible": false,
"has_machine_quality": true,
"has_variants": true,
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
"preferred_variant_name": "0.4mm Nozzle",
"variants_name": "Nozzle Size"
},
"overrides":
{
"acceleration_enabled": { "value": true },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_roofing": { "enabled": "acceleration_enabled and roofing_layer_count > 0 and top_layers > 0" },
"acceleration_topbottom": { "value": "acceleration_print / 3" },
"acceleration_travel": { "value": 3000 },
"acceleration_travel_layer_0": { "value": "acceleration_travel / 3" },
"adaptive_layer_height_variation": { "value": 0.04 },
"adaptive_layer_height_variation_step": { "value": 0.04 },
"adhesion_type": { "value": "'skirt'" },
"brim_replaces_support": { "value": false },
"brim_width": { "value": "3" },
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_min_layer_time": { "value": 2 },
"fill_outline_gaps": { "value": false },
"gantry_height": { "value": 30 },
"infill_before_walls": { "value": false },
"infill_overlap": { "value": 30 },
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
"infill_wipe_dist": { "value": 0 },
"layer_height": { "default_value": 0.2 },
"layer_height_0": { "default_value": 0.2 },
"machine_acceleration": { "value": 3000 },
"machine_end_gcode": { "default_value": "END_PRINT" },
"machine_extruder_count": { "default_value": 1 },
"machine_head_with_fans_polygon":
{
"default_value": [
[-40, 90],
[-40, -30],
[40, -30],
[40, 90]
]
},
"machine_heated_bed": { "default_value": true },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 9000 },
"machine_max_acceleration_y": { "value": 9000 },
"machine_max_acceleration_z": { "value": 100 },
"machine_max_feedrate_e": { "value": 60 },
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },
"machine_max_feedrate_z": { "value": 10 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 5 },
"machine_max_jerk_z": { "value": 0.4 },
"machine_name": { "default_value": "RatRig V-Core 3" },
"machine_shape": { "default_value": "rectangular" },
"machine_show_variants": { "default_value": true },
"machine_start_gcode": { "default_value": "START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0}" },
"material_diameter": { "default_value": 1.75 },
"material_final_print_temperature": { "value": "material_print_temperature" },
"material_initial_print_temperature": { "value": "material_print_temperature" },
"meshfix_maximum_resolution": { "value": "0.25" },
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
"minimum_interface_area": { "value": 10 },
"minimum_support_area": { "value": 2 },
"optimize_wall_printing_order": { "value": true },
"retraction_amount": { "value": "machine_nozzle_size * 2" },
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
"retraction_combing_max_distance": { "value": 30 },
"retraction_count_max": { "value": 100 },
"retraction_extrusion_window": { "value": 10 },
"retraction_speed": { "value": 40 },
"roofing_layer_count": { "value": 1 },
"skin_overlap": { "value": 18 },
"skirt_brim_minimal_length": { "default_value": 30 },
"skirt_gap": { "value": 10 },
"skirt_line_count": { "value": 3 },
"speed_layer_0": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_print": { "value": 100 },
"speed_roofing": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_support": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_support_interface": { "value": "speed_topbottom" },
"speed_topbottom": { "value": "math.floor(speed_print / 2)" },
"speed_travel": { "value": 250 },
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
"speed_wall_x": { "value": "speed_wall" },
"speed_z_hop": { "value": 5 },
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width/2.0/layer_height)))" },
"support_brim_width": { "value": 4 },
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
"support_interface_density": { "value": 33.333 },
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_pattern": { "value": "'zigzag'" },
"support_use_towers": { "value": false },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
"support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" },
"top_bottom_pattern": { "value": "'lines'" },
"top_bottom_thickness": { "value": "layer_height_0 + layer_height * 3" },
"travel_avoid_supports": { "value": true },
"travel_retract_before_outer_wall": { "value": true },
"wall_0_wipe_dist": { "value": 0 },
"wall_thickness": { "value": "line_width * 2" },
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
"z_seam_type": { "value": "'back'" }
}
}

View file

@ -0,0 +1,127 @@
{
"version": 2,
"name": "RatRig V-Minion",
"inherits": "ratrig_base",
"metadata":
{
"visible": true,
"platform": "ratrig_vminion.stl",
"has_machine_quality": true,
"has_variants": true,
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
"platform_offset": [
0,
5,
0
],
"preferred_variant_name": "0.4mm Nozzle",
"variants_name": "Nozzle Size",
"weight": 8
},
"overrides":
{
"acceleration_enabled": { "value": true },
"acceleration_layer_0": { "value": "acceleration_topbottom" },
"acceleration_roofing": { "enabled": "acceleration_enabled and roofing_layer_count > 0 and top_layers > 0" },
"acceleration_topbottom": { "value": "acceleration_print / 3" },
"acceleration_travel": { "value": 3000 },
"acceleration_travel_layer_0": { "value": "acceleration_travel / 3" },
"adaptive_layer_height_variation": { "value": 0.04 },
"adaptive_layer_height_variation_step": { "value": 0.04 },
"adhesion_type": { "value": "'skirt'" },
"brim_replaces_support": { "value": false },
"brim_width": { "value": "3" },
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
"cool_min_layer_time": { "value": 2 },
"fill_outline_gaps": { "value": false },
"gantry_height": { "value": 30 },
"infill_before_walls": { "value": false },
"infill_overlap": { "value": 30 },
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
"infill_wipe_dist": { "value": 0 },
"layer_height": { "default_value": 0.2 },
"layer_height_0": { "default_value": 0.2 },
"machine_acceleration": { "value": 3000 },
"machine_depth": { "default_value": 180 },
"machine_end_gcode": { "default_value": "END_PRINT" },
"machine_extruder_count": { "default_value": 1 },
"machine_head_with_fans_polygon":
{
"default_value": [
[-40, 90],
[-40, -30],
[40, -30],
[40, 90]
]
},
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 180 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 9000 },
"machine_max_acceleration_y": { "value": 9000 },
"machine_max_acceleration_z": { "value": 100 },
"machine_max_feedrate_e": { "value": 60 },
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },
"machine_max_feedrate_z": { "value": 10 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 5 },
"machine_max_jerk_z": { "value": 0.4 },
"machine_name": { "default_value": "RatRig V-Minion" },
"machine_shape": { "default_value": "rectangular" },
"machine_show_variants": { "default_value": true },
"machine_start_gcode": { "default_value": "START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0}" },
"machine_width": { "default_value": 180 },
"material_diameter": { "default_value": 1.75 },
"material_final_print_temperature": { "value": "material_print_temperature" },
"material_initial_print_temperature": { "value": "material_print_temperature" },
"meshfix_maximum_resolution": { "value": "0.25" },
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
"minimum_interface_area": { "value": 10 },
"minimum_support_area": { "value": 2 },
"optimize_wall_printing_order": { "value": true },
"retraction_amount": { "value": "machine_nozzle_size * 2" },
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
"retraction_combing_max_distance": { "value": 30 },
"retraction_count_max": { "value": 100 },
"retraction_extrusion_window": { "value": 10 },
"retraction_speed": { "value": 40 },
"roofing_layer_count": { "value": 1 },
"skin_overlap": { "value": 18 },
"skirt_brim_minimal_length": { "default_value": 30 },
"skirt_gap": { "value": 10 },
"skirt_line_count": { "value": 3 },
"speed_layer_0": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_print": { "value": 100 },
"speed_roofing": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_support": { "value": "math.floor(speed_print * 3 / 10)" },
"speed_support_interface": { "value": "speed_topbottom" },
"speed_topbottom": { "value": "math.floor(speed_print / 2)" },
"speed_travel": { "value": 250 },
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
"speed_wall": { "value": "math.floor(speed_print / 2)" },
"speed_wall_x": { "value": "speed_wall" },
"speed_z_hop": { "value": 5 },
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width/2.0/layer_height)))" },
"support_brim_width": { "value": 4 },
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
"support_interface_density": { "value": 33.333 },
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_pattern": { "value": "'zigzag'" },
"support_use_towers": { "value": false },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
"support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" },
"top_bottom_pattern": { "value": "'lines'" },
"top_bottom_thickness": { "value": "layer_height_0 + layer_height * 3" },
"travel_avoid_supports": { "value": true },
"travel_retract_before_outer_wall": { "value": true },
"wall_0_wipe_dist": { "value": 0 },
"wall_thickness": { "value": "line_width * 2" },
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
"z_seam_type": { "value": "'back'" }
}
}

View file

@ -70,7 +70,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -111,7 +111,6 @@
"support_interface_density": { "value": 33.333 },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": "1 if (support_structure == 'tree') else 0" },
"support_xy_distance": { "value": "wall_line_width_0 * 3" },

View file

@ -68,7 +68,6 @@
"support_interface_density": { "value": 33.333 },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": "1 if (support_structure == 'tree') else 0" },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -106,6 +106,7 @@
"retraction_combing_max_distance": { "value": 15 },
"retraction_count_max": { "value": 25 },
"retraction_extrusion_window": { "value": 1 },
"retraction_min_travel": { "value": 5 },
"roofing_layer_count": { "value": "1" },
"roofing_material_flow": { "value": "material_flow" },
"skin_angles": { "value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]" },

View file

@ -156,7 +156,6 @@
"retraction_hop": { "value": "2" },
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "15" },
"skin_overlap": { "value": "10" },
"speed_prime_tower": { "value": "speed_topbottom" },

View file

@ -99,7 +99,7 @@
"acceleration_print":
{
"enabled": false,
"value": 300
"value": 800
},
"acceleration_print_layer_0":
{
@ -234,7 +234,7 @@
"jerk_print":
{
"enabled": false,
"value": 12.5
"value": 6.25
},
"jerk_print_layer_0":
{
@ -279,7 +279,7 @@
"jerk_travel":
{
"enabled": false,
"value": 12.5
"value": "jerk_print"
},
"jerk_travel_enabled":
{
@ -361,8 +361,10 @@
"raft_interface_line_width": { "value": 0.7 },
"raft_interface_speed": { "value": 90 },
"raft_interface_thickness": { "value": 0.3 },
"raft_margin": { "value": 3 },
"raft_interface_wall_count": { "value": "raft_wall_count" },
"raft_margin": { "value": 1.2 },
"raft_surface_extruder_nr": { "value": "int(anyExtruderWithMaterial('material_is_support_material')) if support_enable and extruderValue(support_extruder_nr,'material_is_support_material') else raft_base_extruder_nr" },
"raft_surface_wall_count": { "value": "raft_wall_count" },
"retraction_amount": { "value": 0.75 },
"retraction_combing": { "value": "'off'" },
"retraction_combing_max_distance": { "value": "speed_travel / 10" },
@ -371,7 +373,6 @@
"retraction_hop": { "value": 0.4 },
"retraction_hop_enabled": { "value": true },
"retraction_hop_only_when_collides": { "value": false },
"retraction_min_travel": { "value": "line_width * 4" },
"retraction_prime_speed": { "value": "retraction_speed" },
"retraction_speed": { "value": 5 },
"roofing_layer_count": { "value": 2 },
@ -384,6 +385,7 @@
"skin_preshrink": { "value": 0 },
"skirt_brim_material_flow": { "value": "material_flow" },
"skirt_brim_minimal_length": { "value": 500 },
"small_skin_width": { "value": 4 },
"speed_equalize_flow_width_factor": { "value": 0 },
"speed_prime_tower": { "value": "speed_topbottom" },
"speed_print": { "value": 50 },
@ -424,7 +426,7 @@
"travel_avoid_other_parts": { "value": false },
"wall_0_inset": { "value": 0 },
"wall_0_material_flow": { "value": "material_flow" },
"wall_0_wipe_dist": { "value": 0 },
"wall_0_wipe_dist": { "value": 0.8 },
"wall_material_flow": { "value": "material_flow" },
"wall_x_material_flow": { "value": "material_flow" },
"xy_offset": { "value": 0 },

View file

@ -108,7 +108,6 @@
"retraction_hop": { "value": "2" },
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "15" },
"retraction_speed": { "value": "45" },
"speed_prime_tower": { "value": "speed_topbottom" },

View file

@ -110,7 +110,6 @@
"retraction_hop": { "value": "2" },
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
"retraction_hop_only_when_collides": { "value": "True" },
"retraction_min_travel": { "value": "5" },
"retraction_prime_speed": { "value": "15" },
"retraction_speed": { "value": "45" },
"speed_prime_tower": { "value": "speed_topbottom" },

View file

@ -99,7 +99,6 @@
"support_interface_enable": { "value": true },
"support_interface_height": { "value": "layer_height * 4" },
"support_interface_pattern": { "value": "'grid'" },
"support_interface_skip_height": { "value": 0.2 },
"support_pattern": { "value": "'zigzag'" },
"support_wall_count": { "value": 1 },
"support_xy_distance": { "value": "wall_line_width_0 * 2" },

View file

@ -55,7 +55,7 @@
"machine_endstop_positive_direction_y": { "default_value": true },
"machine_endstop_positive_direction_z": { "default_value": false },
"machine_feeder_wheel_diameter": { "default_value": 7.5 },
"machine_gcode_flavor": { "default_value": "RepRap (RepRap)" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_head_with_fans_polygon":
{
"default_value": [
@ -76,7 +76,7 @@
"machine_max_jerk_xy": { "default_value": 20 },
"machine_max_jerk_z": { "default_value": 1 },
"machine_name": { "default_value": "VORON2" },
"machine_start_gcode": { "default_value": "print_start" },
"machine_start_gcode": { "default_value": ";Nozzle diameter = {machine_nozzle_size}\n;Filament type = {material_type}\n;Filament name = {material_name}\n;Filament weight = {filament_weight}\n; M190 S{material_bed_temperature_layer_0}\n; M109 S{material_print_temperature_layer_0}\nprint_start EXTRUDER={material_print_temperature_layer_0} BED={material_bed_temperature_layer_0} CHAMBER={build_volume_temperature}" },
"machine_steps_per_mm_x": { "default_value": 80 },
"machine_steps_per_mm_y": { "default_value": 80 },
"machine_steps_per_mm_z": { "default_value": 400 },

View file

@ -0,0 +1,16 @@
{
"version": 2,
"name": "Extruder",
"inherits": "fdmextruder",
"metadata":
{
"machine": "flashforge_adventurer_base",
"position": "0"
},
"overrides":
{
"extruder_nr": { "default_value": 0 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

View file

@ -0,0 +1,16 @@
{
"version": 2,
"name": "Extruder 1",
"inherits": "fdmextruder",
"metadata":
{
"machine": "ratrig_base",
"position": "0"
},
"overrides":
{
"extruder_nr": { "default_value": 0 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 }
}
}

Some files were not shown because too many files have changed in this diff Show more