mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge branch 'main' into CURA-11561_mockup_pap
This commit is contained in:
commit
80d7536763
178 changed files with 1253 additions and 434 deletions
|
@ -55,7 +55,8 @@ exe = EXE(
|
||||||
target_arch={{ target_arch }},
|
target_arch={{ target_arch }},
|
||||||
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
||||||
entitlements_file={{ entitlements_file }},
|
entitlements_file={{ entitlements_file }},
|
||||||
icon={{ icon }}
|
icon={{ icon }},
|
||||||
|
contents_directory='.'
|
||||||
)
|
)
|
||||||
|
|
||||||
coll = COLLECT(
|
coll = COLLECT(
|
||||||
|
@ -70,188 +71,7 @@ coll = COLLECT(
|
||||||
)
|
)
|
||||||
|
|
||||||
{% if macos == true %}
|
{% if macos == true %}
|
||||||
# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
|
app = BUNDLE(
|
||||||
# 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(
|
|
||||||
coll,
|
coll,
|
||||||
name='{{ display_name }}.app',
|
name='{{ display_name }}.app',
|
||||||
icon={{ icon }},
|
icon={{ icon }},
|
||||||
|
@ -272,8 +92,9 @@ app = UMBUNDLE(
|
||||||
}],
|
}],
|
||||||
'CFBundleDocumentTypes': [{
|
'CFBundleDocumentTypes': [{
|
||||||
'CFBundleTypeRole': 'Viewer',
|
'CFBundleTypeRole': 'Viewer',
|
||||||
'CFBundleTypeExtensions': ['*'],
|
'CFBundleTypeExtensions': ['stl', 'obj', '3mf', 'gcode', 'ufp'],
|
||||||
'CFBundleTypeName': 'Model Files',
|
'CFBundleTypeName': 'Model Files',
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
){% endif %}
|
)
|
||||||
|
{% endif %}
|
||||||
|
|
|
@ -118,7 +118,6 @@ pyinstaller:
|
||||||
- "sqlite3"
|
- "sqlite3"
|
||||||
- "trimesh"
|
- "trimesh"
|
||||||
- "win32ctypes"
|
- "win32ctypes"
|
||||||
- "PyQt6"
|
|
||||||
- "PyQt6.QtNetwork"
|
- "PyQt6.QtNetwork"
|
||||||
- "PyQt6.sip"
|
- "PyQt6.sip"
|
||||||
- "stl"
|
- "stl"
|
||||||
|
@ -160,6 +159,10 @@ pycharm_targets:
|
||||||
module_name: Cura
|
module_name: Cura
|
||||||
name: pytest in TestGCodeListDecorator.py
|
name: pytest in TestGCodeListDecorator.py
|
||||||
script_name: tests/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
|
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||||
module_name: Cura
|
module_name: Cura
|
||||||
name: pytest in TestIntentManager.py
|
name: pytest in TestIntentManager.py
|
||||||
|
@ -188,6 +191,10 @@ pycharm_targets:
|
||||||
module_name: Cura
|
module_name: Cura
|
||||||
name: pytest in TestPrintInformation.py
|
name: pytest in TestPrintInformation.py
|
||||||
script_name: tests/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
|
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||||
module_name: Cura
|
module_name: Cura
|
||||||
name: pytest in TestProfileRequirements.py
|
name: pytest in TestProfileRequirements.py
|
||||||
|
|
|
@ -104,7 +104,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
||||||
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
||||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||||
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
|
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.AddPrinterPagesModel import AddPrinterPagesModel
|
||||||
from cura.UI.MachineSettingsManager import MachineSettingsManager
|
from cura.UI.MachineSettingsManager import MachineSettingsManager
|
||||||
from cura.UI.ObjectsModel import ObjectsModel
|
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.MachineListModel import MachineListModel
|
||||||
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
|
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
|
||||||
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
|
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
|
||||||
|
from .PrintOrderManager import PrintOrderManager
|
||||||
from .SingleInstance import SingleInstance
|
from .SingleInstance import SingleInstance
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -179,6 +181,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
# Variables set from CLI
|
# Variables set from CLI
|
||||||
self._files_to_open = []
|
self._files_to_open = []
|
||||||
|
self._urls_to_open = []
|
||||||
self._use_single_instance = False
|
self._use_single_instance = False
|
||||||
|
|
||||||
self._single_instance = None
|
self._single_instance = None
|
||||||
|
@ -186,7 +189,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
|
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_container = None # type: EmptyInstanceContainer
|
||||||
self.empty_definition_changes_container = None # type: EmptyInstanceContainer
|
self.empty_definition_changes_container = None # type: EmptyInstanceContainer
|
||||||
|
@ -202,6 +205,7 @@ class CuraApplication(QtApplication):
|
||||||
self._container_manager = None
|
self._container_manager = None
|
||||||
|
|
||||||
self._object_manager = None
|
self._object_manager = None
|
||||||
|
self._print_order_manager = None
|
||||||
self._extruders_model = None
|
self._extruders_model = None
|
||||||
self._extruders_model_with_optional = None
|
self._extruders_model_with_optional = None
|
||||||
self._build_plate_model = None
|
self._build_plate_model = None
|
||||||
|
@ -333,7 +337,7 @@ class CuraApplication(QtApplication):
|
||||||
for filename in self._cli_args.file:
|
for filename in self._cli_args.file:
|
||||||
url = QUrl(filename)
|
url = QUrl(filename)
|
||||||
if url.scheme() in self._supported_url_schemes:
|
if url.scheme() in self._supported_url_schemes:
|
||||||
self._open_url_queue.append(url)
|
self._urls_to_open.append(url)
|
||||||
else:
|
else:
|
||||||
self._files_to_open.append(os.path.abspath(filename))
|
self._files_to_open.append(os.path.abspath(filename))
|
||||||
|
|
||||||
|
@ -352,11 +356,11 @@ class CuraApplication(QtApplication):
|
||||||
self.__addAllEmptyContainers()
|
self.__addAllEmptyContainers()
|
||||||
self.__setLatestResouceVersionsForVersionUpgrade()
|
self.__setLatestResouceVersionsForVersionUpgrade()
|
||||||
|
|
||||||
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
self._machine_action_manager = MachineActionManager(self)
|
||||||
self._machine_action_manager.initialize()
|
self._machine_action_manager.initialize()
|
||||||
|
|
||||||
def __sendCommandToSingleInstance(self):
|
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 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.
|
# 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)
|
Resources.addExpectedDirNameInData(dir_name)
|
||||||
|
|
||||||
app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
|
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"))
|
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||||
|
|
||||||
if not hasattr(sys, "frozen"):
|
if not hasattr(sys, "frozen"):
|
||||||
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
|
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
|
||||||
if cura_data_root:
|
if cura_data_root:
|
||||||
|
@ -899,6 +909,7 @@ class CuraApplication(QtApplication):
|
||||||
# initialize info objects
|
# initialize info objects
|
||||||
self._print_information = PrintInformation.PrintInformation(self)
|
self._print_information = PrintInformation.PrintInformation(self)
|
||||||
self._cura_actions = CuraActions.CuraActions(self)
|
self._cura_actions = CuraActions.CuraActions(self)
|
||||||
|
self._print_order_manager = PrintOrderManager(self.getObjectsModel().getNodes)
|
||||||
self.processEvents()
|
self.processEvents()
|
||||||
# Initialize setting visibility presets model.
|
# Initialize setting visibility presets model.
|
||||||
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self.getPreferences(), parent = self)
|
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self.getPreferences(), parent = self)
|
||||||
|
@ -956,6 +967,8 @@ class CuraApplication(QtApplication):
|
||||||
self.callLater(self._openFile, file_name)
|
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.
|
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)
|
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:
|
for url in self._open_url_queue:
|
||||||
self.callLater(self._openUrl, url)
|
self.callLater(self._openUrl, url)
|
||||||
|
|
||||||
|
@ -979,6 +992,7 @@ class CuraApplication(QtApplication):
|
||||||
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])
|
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])
|
||||||
|
|
||||||
Selection.selectionChanged.connect(self.onSelectionChanged)
|
Selection.selectionChanged.connect(self.onSelectionChanged)
|
||||||
|
self._print_order_manager.printOrderChanged.connect(self._onPrintOrderChanged)
|
||||||
|
|
||||||
# Set default background color for scene
|
# Set default background color for scene
|
||||||
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
|
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
|
||||||
|
@ -1068,6 +1082,10 @@ class CuraApplication(QtApplication):
|
||||||
def getTextManager(self, *args) -> "TextManager":
|
def getTextManager(self, *args) -> "TextManager":
|
||||||
return self._text_manager
|
return self._text_manager
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
|
def getWorkplaceDropToBuildplate(self, drop_to_build_plate: bool) ->None:
|
||||||
|
return self._physics.setAppPerModelDropDown(drop_to_build_plate)
|
||||||
|
|
||||||
def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions":
|
def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions":
|
||||||
if self._cura_formula_functions is None:
|
if self._cura_formula_functions is None:
|
||||||
self._cura_formula_functions = CuraFormulaFunctions(self)
|
self._cura_formula_functions = CuraFormulaFunctions(self)
|
||||||
|
@ -1094,6 +1112,10 @@ class CuraApplication(QtApplication):
|
||||||
self._object_manager = ObjectsModel(self)
|
self._object_manager = ObjectsModel(self)
|
||||||
return self._object_manager
|
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)
|
@pyqtSlot(result = QObject)
|
||||||
def getExtrudersModel(self, *args) -> "ExtrudersModel":
|
def getExtrudersModel(self, *args) -> "ExtrudersModel":
|
||||||
if self._extruders_model is None:
|
if self._extruders_model is None:
|
||||||
|
@ -1129,18 +1151,16 @@ class CuraApplication(QtApplication):
|
||||||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||||
return self._setting_inheritance_manager
|
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
|
"""Get the machine action manager
|
||||||
|
|
||||||
We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
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.
|
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 getMachineActionManagerQml(self)-> MachineActionManager.MachineActionManager:
|
|
||||||
return cast(QObject, self._machine_action_manager)
|
|
||||||
|
|
||||||
@pyqtSlot(result = QObject)
|
@pyqtSlot(result = QObject)
|
||||||
def getMaterialManagementModel(self) -> MaterialManagementModel:
|
def getMaterialManagementModel(self) -> MaterialManagementModel:
|
||||||
|
@ -1250,6 +1270,7 @@ class CuraApplication(QtApplication):
|
||||||
self.processEvents()
|
self.processEvents()
|
||||||
engine.rootContext().setContextProperty("Printer", self)
|
engine.rootContext().setContextProperty("Printer", self)
|
||||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||||
|
engine.rootContext().setContextProperty("PrintOrderManager", self._print_order_manager)
|
||||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||||
engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
|
engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
|
||||||
|
@ -1264,7 +1285,7 @@ class CuraApplication(QtApplication):
|
||||||
qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
|
qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
|
||||||
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
|
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
|
||||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
|
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
|
||||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
|
qmlRegisterSingletonType(MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
|
||||||
|
|
||||||
self.processEvents()
|
self.processEvents()
|
||||||
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
|
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
|
||||||
|
@ -1745,8 +1766,12 @@ class CuraApplication(QtApplication):
|
||||||
Selection.remove(node)
|
Selection.remove(node)
|
||||||
Selection.add(group_node)
|
Selection.add(group_node)
|
||||||
|
|
||||||
|
all_nodes = self.getObjectsModel().getNodes()
|
||||||
|
PrintOrderManager.updatePrintOrdersAfterGroupOperation(all_nodes, group_node, selected_nodes)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def ungroupSelected(self) -> None:
|
def ungroupSelected(self) -> None:
|
||||||
|
all_nodes = self.getObjectsModel().getNodes()
|
||||||
selected_objects = Selection.getAllSelectedObjects().copy()
|
selected_objects = Selection.getAllSelectedObjects().copy()
|
||||||
for node in selected_objects:
|
for node in selected_objects:
|
||||||
if node.callDecoration("isGroup"):
|
if node.callDecoration("isGroup"):
|
||||||
|
@ -1754,21 +1779,30 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
group_parent = node.getParent()
|
group_parent = node.getParent()
|
||||||
children = node.getChildren().copy()
|
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
|
# Set the parent of the children to the parent of the group-node
|
||||||
op.addOperation(SetParentOperation(child, group_parent))
|
op.addOperation(SetParentOperation(child, group_parent))
|
||||||
|
|
||||||
# Add all individual nodes to the selection
|
# Add all individual nodes to the selection
|
||||||
Selection.add(child)
|
Selection.add(child)
|
||||||
|
|
||||||
|
PrintOrderManager.updatePrintOrdersAfterUngroupOperation(all_nodes, node, children_to_ungroup)
|
||||||
op.push()
|
op.push()
|
||||||
# Note: The group removes itself from the scene once all its children have left it,
|
# Note: The group removes itself from the scene once all its children have left it,
|
||||||
# see GroupDecorator._onChildrenChanged
|
# 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]:
|
def _createSplashScreen(self) -> Optional[CuraSplashScreen.CuraSplashScreen]:
|
||||||
if self._is_headless:
|
if self._is_headless:
|
||||||
return None
|
return None
|
||||||
|
|
88
cura/HitChecker.py
Normal file
88
cura/HitChecker.py
Normal 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
|
|
@ -16,6 +16,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To downlo
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||||
|
REQUEST_TIMEOUT = 5 # Seconds
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationHelpers:
|
class AuthorizationHelpers:
|
||||||
|
@ -53,7 +54,8 @@ class AuthorizationHelpers:
|
||||||
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
||||||
headers_dict = headers,
|
headers_dict = headers,
|
||||||
callback = lambda response: self.parseTokenResponse(response, callback),
|
callback = lambda response: self.parseTokenResponse(response, callback),
|
||||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback)
|
error_callback = lambda response, _: self.parseTokenResponse(response, callback),
|
||||||
|
timeout = REQUEST_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
def getAccessTokenUsingRefreshToken(self, refresh_token: str, callback: Callable[[AuthenticationResponse], None]) -> None:
|
def getAccessTokenUsingRefreshToken(self, refresh_token: str, callback: Callable[[AuthenticationResponse], None]) -> None:
|
||||||
|
@ -77,7 +79,9 @@ class AuthorizationHelpers:
|
||||||
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
||||||
headers_dict = headers,
|
headers_dict = headers,
|
||||||
callback = lambda response: self.parseTokenResponse(response, callback),
|
callback = lambda response: self.parseTokenResponse(response, callback),
|
||||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback)
|
error_callback = lambda response, _: self.parseTokenResponse(response, callback),
|
||||||
|
urgent = True,
|
||||||
|
timeout = REQUEST_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
def parseTokenResponse(self, token_response: QNetworkReply, callback: Callable[[AuthenticationResponse], None]) -> None:
|
def parseTokenResponse(self, token_response: QNetworkReply, callback: Callable[[AuthenticationResponse], None]) -> None:
|
||||||
|
@ -122,7 +126,8 @@ class AuthorizationHelpers:
|
||||||
check_token_url,
|
check_token_url,
|
||||||
headers_dict = headers,
|
headers_dict = headers,
|
||||||
callback = lambda reply: self._parseUserProfile(reply, success_callback, failed_callback),
|
callback = lambda reply: self._parseUserProfile(reply, success_callback, failed_callback),
|
||||||
error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None
|
error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None,
|
||||||
|
timeout = REQUEST_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
def _parseUserProfile(self, reply: QNetworkReply, success_callback: Optional[Callable[[UserProfile], None]], failed_callback: Optional[Callable[[], None]] = None) -> None:
|
def _parseUserProfile(self, reply: QNetworkReply, success_callback: Optional[Callable[[UserProfile], None]], failed_callback: Optional[Callable[[], None]] = None) -> None:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2021 Ultimaker B.V.
|
# Copyright (c) 2024 UltiMaker
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
@ -6,13 +6,14 @@ from datetime import datetime, timedelta
|
||||||
from typing import Callable, Dict, Optional, TYPE_CHECKING, Union
|
from typing import Callable, Dict, Optional, TYPE_CHECKING, Union
|
||||||
from urllib.parse import urlencode, quote_plus
|
from urllib.parse import urlencode, quote_plus
|
||||||
|
|
||||||
from PyQt6.QtCore import QUrl
|
from PyQt6.QtCore import QUrl, QTimer
|
||||||
from PyQt6.QtGui import QDesktopServices
|
from PyQt6.QtGui import QDesktopServices
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Message import Message
|
from UM.Message import Message
|
||||||
from UM.Signal import Signal
|
from UM.Signal import Signal
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To download log-in tokens.
|
||||||
from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers, TOKEN_TIMESTAMP_FORMAT
|
from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers, TOKEN_TIMESTAMP_FORMAT
|
||||||
from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer
|
from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer
|
||||||
from cura.OAuth2.Models import AuthenticationResponse, BaseModel
|
from cura.OAuth2.Models import AuthenticationResponse, BaseModel
|
||||||
|
@ -25,6 +26,8 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
MYCLOUD_LOGOFF_URL = "https://account.ultimaker.com/logoff?utm_source=cura&utm_medium=software&utm_campaign=change-account-before-adding-printers"
|
MYCLOUD_LOGOFF_URL = "https://account.ultimaker.com/logoff?utm_source=cura&utm_medium=software&utm_campaign=change-account-before-adding-printers"
|
||||||
|
|
||||||
|
REFRESH_TOKEN_MAX_RETRIES = 15
|
||||||
|
REFRESH_TOKEN_RETRY_INTERVAL = 1000
|
||||||
|
|
||||||
class AuthorizationService:
|
class AuthorizationService:
|
||||||
"""The authorization service is responsible for handling the login flow, storing user credentials and providing
|
"""The authorization service is responsible for handling the login flow, storing user credentials and providing
|
||||||
|
@ -57,6 +60,12 @@ class AuthorizationService:
|
||||||
|
|
||||||
self.onAuthStateChanged.connect(self._authChanged)
|
self.onAuthStateChanged.connect(self._authChanged)
|
||||||
|
|
||||||
|
self._refresh_token_retries = 0
|
||||||
|
self._refresh_token_retry_timer = QTimer()
|
||||||
|
self._refresh_token_retry_timer.setInterval(REFRESH_TOKEN_RETRY_INTERVAL)
|
||||||
|
self._refresh_token_retry_timer.setSingleShot(True)
|
||||||
|
self._refresh_token_retry_timer.timeout.connect(self.refreshAccessToken)
|
||||||
|
|
||||||
def _authChanged(self, logged_in):
|
def _authChanged(self, logged_in):
|
||||||
if logged_in and self._unable_to_get_data_message is not None:
|
if logged_in and self._unable_to_get_data_message is not None:
|
||||||
self._unable_to_get_data_message.hide()
|
self._unable_to_get_data_message.hide()
|
||||||
|
@ -167,16 +176,29 @@ class AuthorizationService:
|
||||||
return
|
return
|
||||||
|
|
||||||
def process_auth_data(response: AuthenticationResponse) -> None:
|
def process_auth_data(response: AuthenticationResponse) -> None:
|
||||||
|
self._currently_refreshing_token = False
|
||||||
|
|
||||||
if response.success:
|
if response.success:
|
||||||
|
self._refresh_token_retries = 0
|
||||||
self._storeAuthData(response)
|
self._storeAuthData(response)
|
||||||
|
HttpRequestManager.getInstance().setDelayRequests(False)
|
||||||
self.onAuthStateChanged.emit(logged_in = True)
|
self.onAuthStateChanged.emit(logged_in = True)
|
||||||
else:
|
else:
|
||||||
Logger.warning("Failed to get a new access token from the server.")
|
if self._refresh_token_retries >= REFRESH_TOKEN_MAX_RETRIES:
|
||||||
|
self._refresh_token_retries = 0
|
||||||
|
Logger.warning("Failed to get a new access token from the server, giving up.")
|
||||||
|
HttpRequestManager.getInstance().setDelayRequests(False)
|
||||||
self.onAuthStateChanged.emit(logged_in = False)
|
self.onAuthStateChanged.emit(logged_in = False)
|
||||||
|
else:
|
||||||
|
# Retry a bit later, network may be offline right now and will hopefully be back soon
|
||||||
|
Logger.warning("Failed to get a new access token from the server, retrying later.")
|
||||||
|
self._refresh_token_retries += 1
|
||||||
|
self._refresh_token_retry_timer.start()
|
||||||
|
|
||||||
if self._currently_refreshing_token:
|
if self._currently_refreshing_token:
|
||||||
Logger.debug("Was already busy refreshing token. Do not start a new request.")
|
Logger.debug("Was already busy refreshing token. Do not start a new request.")
|
||||||
return
|
return
|
||||||
|
HttpRequestManager.getInstance().setDelayRequests(True)
|
||||||
self._currently_refreshing_token = True
|
self._currently_refreshing_token = True
|
||||||
self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token, process_auth_data)
|
self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token, process_auth_data)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,11 @@ from UM.Scene.Iterator import Iterator
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from functools import cmp_to_key
|
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):
|
class OneAtATimeIterator(Iterator.Iterator):
|
||||||
"""Iterator that returns a list of nodes in the order that they need to be printed
|
"""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:
|
def __init__(self, scene_node) -> None:
|
||||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
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:
|
def _fillStack(self) -> None:
|
||||||
"""Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
|
"""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[:]
|
self._node_stack = node_list[:]
|
||||||
return
|
return
|
||||||
|
|
||||||
# Copy the list
|
hit_checker = HitChecker(node_list)
|
||||||
self._original_node_list = node_list[:]
|
|
||||||
|
|
||||||
# Initialise the hit map (pre-compute all hits between all objects)
|
if PrintOrderManager.isUserDefinedPrintOrderEnabled():
|
||||||
self._hit_map = [[self._checkHit(i, j) for i in node_list] for j in node_list]
|
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!
|
# update print orders so that user can try to arrange the nodes automatically first
|
||||||
for a in range(0, len(node_list)):
|
# and if result is not satisfactory he/she can switch to manual mode and change it
|
||||||
for b in range(0, len(node_list)):
|
for index, node in enumerate(self._node_stack):
|
||||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
node.printOrder = index + 1
|
||||||
return
|
|
||||||
|
@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.
|
# 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.
|
# 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)]
|
todo_node_list = [_ObjectOrder([], node_list)]
|
||||||
while len(todo_node_list) > 0:
|
while len(todo_node_list) > 0:
|
||||||
current = todo_node_list.pop()
|
current = todo_node_list.pop()
|
||||||
for node in current.todo:
|
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
|
# 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.
|
# We found a possible result. Create new todo & order list.
|
||||||
new_todo_list = current.todo[:]
|
new_todo_list = current.todo[:]
|
||||||
new_todo_list.remove(node)
|
new_todo_list.remove(node)
|
||||||
new_order = current.order[:] + [node]
|
new_order = current.order[:] + [node]
|
||||||
if len(new_todo_list) == 0:
|
if len(new_todo_list) == 0:
|
||||||
# We have no more nodes to check, so quit looking.
|
# We have no more nodes to check, so quit looking.
|
||||||
self._node_stack = new_order
|
return new_order # Solution found!
|
||||||
return
|
|
||||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||||
self._node_stack = [] #No result found!
|
return [] # 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
|
|
||||||
|
|
||||||
|
|
||||||
class _ObjectOrder:
|
class _ObjectOrder:
|
||||||
|
|
|
@ -38,7 +38,14 @@ class PlatformPhysics:
|
||||||
self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models
|
self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models
|
||||||
|
|
||||||
Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False)
|
Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False)
|
||||||
Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True)
|
Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", False)
|
||||||
|
self._app_per_model_drop = Application.getInstance().getPreferences().getValue("physics/automatic_drop_down")
|
||||||
|
|
||||||
|
def getAppPerModelDropDown(self):
|
||||||
|
return self._app_per_model_drop
|
||||||
|
|
||||||
|
def setAppPerModelDropDown(self, drop_to_buildplate):
|
||||||
|
self._app_per_model_drop = drop_to_buildplate
|
||||||
|
|
||||||
def _onSceneChanged(self, source):
|
def _onSceneChanged(self, source):
|
||||||
if not source.callDecoration("isSliceable"):
|
if not source.callDecoration("isSliceable"):
|
||||||
|
@ -71,6 +78,7 @@ class PlatformPhysics:
|
||||||
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
|
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
|
||||||
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
|
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
|
||||||
random.shuffle(nodes)
|
random.shuffle(nodes)
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
|
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
|
||||||
continue
|
continue
|
||||||
|
@ -80,7 +88,10 @@ class PlatformPhysics:
|
||||||
# Move it downwards if bottom is above platform
|
# Move it downwards if bottom is above platform
|
||||||
move_vector = Vector()
|
move_vector = Vector()
|
||||||
|
|
||||||
if node.getSetting(SceneNodeSettings.AutoDropDown, app_automatic_drop_down) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
# if per model drop is different then app_automatic_drop, in case of 3mf loading when user changes this setting for that model
|
||||||
|
if (self._app_per_model_drop != app_automatic_drop_down):
|
||||||
|
node.setSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop)
|
||||||
|
if node.getSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||||
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
||||||
|
|
||||||
|
@ -168,6 +179,8 @@ class PlatformPhysics:
|
||||||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||||
op.push()
|
op.push()
|
||||||
|
|
||||||
|
# setting this drop to model same as app_automatic_drop_down
|
||||||
|
self._app_per_model_drop = app_automatic_drop_down
|
||||||
# After moving, we have to evaluate the boundary checks for nodes
|
# After moving, we have to evaluate the boundary checks for nodes
|
||||||
build_volume.updateNodeBoundaryCheck()
|
build_volume.updateNodeBoundaryCheck()
|
||||||
|
|
||||||
|
|
171
cura/PrintOrderManager.py
Normal file
171
cura/PrintOrderManager.py
Normal 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()
|
|
@ -25,10 +25,19 @@ class CuraSceneNode(SceneNode):
|
||||||
if not no_setting_override:
|
if not no_setting_override:
|
||||||
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
|
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||||
self._outside_buildarea = False
|
self._outside_buildarea = False
|
||||||
|
self._print_order = 0
|
||||||
|
|
||||||
def setOutsideBuildArea(self, new_value: bool) -> None:
|
def setOutsideBuildArea(self, new_value: bool) -> None:
|
||||||
self._outside_buildarea = new_value
|
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:
|
def isOutsideBuildArea(self) -> bool:
|
||||||
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
|
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
|
||||||
|
|
||||||
|
@ -157,3 +166,6 @@ class CuraSceneNode(SceneNode):
|
||||||
|
|
||||||
def transformChanged(self) -> None:
|
def transformChanged(self) -> None:
|
||||||
self._transformChanged()
|
self._transformChanged()
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return "{print_order}. {name}".format(print_order = self._print_order, name = self.getName())
|
||||||
|
|
|
@ -5,6 +5,7 @@ import json
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from PyQt6.QtCore import QUrl
|
||||||
from PyQt6.QtNetwork import QLocalServer, QLocalSocket
|
from PyQt6.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
from UM.Qt.QtApplication import QtApplication # For typing.
|
from UM.Qt.QtApplication import QtApplication # For typing.
|
||||||
|
@ -12,9 +13,10 @@ from UM.Logger import Logger
|
||||||
|
|
||||||
|
|
||||||
class SingleInstance:
|
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._application = application
|
||||||
self._files_to_open = files_to_open
|
self._files_to_open = files_to_open
|
||||||
|
self._url_to_open = url_to_open
|
||||||
|
|
||||||
self._single_instance_server = None
|
self._single_instance_server = None
|
||||||
|
|
||||||
|
@ -33,7 +35,7 @@ class SingleInstance:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# We only send the files that need to be opened.
|
# 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.")
|
Logger.log("i", "No file need to be opened, do nothing.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -55,6 +57,10 @@ class SingleInstance:
|
||||||
payload = {"command": "open", "filePath": os.path.abspath(filename)}
|
payload = {"command": "open", "filePath": os.path.abspath(filename)}
|
||||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
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"}
|
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"))
|
||||||
|
|
||||||
|
@ -94,6 +100,12 @@ class SingleInstance:
|
||||||
elif command == "open":
|
elif command == "open":
|
||||||
self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f))
|
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.
|
# Command: Activate the window and bring it to the top.
|
||||||
elif command == "focus":
|
elif command == "focus":
|
||||||
# Operating systems these days prevent windows from moving around by themselves.
|
# Operating systems these days prevent windows from moving around by themselves.
|
||||||
|
|
|
@ -14,6 +14,9 @@ from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
from UM.i18n import i18nCatalog
|
from UM.i18n import i18nCatalog
|
||||||
|
|
||||||
|
from cura.PrintOrderManager import PrintOrderManager
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +79,9 @@ class ObjectsModel(ListModel):
|
||||||
self._build_plate_number = nr
|
self._build_plate_number = nr
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
|
def getNodes(self) -> List[CuraSceneNode]:
|
||||||
|
return list(map(lambda n: n["node"], self.items))
|
||||||
|
|
||||||
def _updateSceneDelayed(self, source) -> None:
|
def _updateSceneDelayed(self, source) -> None:
|
||||||
if not isinstance(source, Camera):
|
if not isinstance(source, Camera):
|
||||||
self._update_timer.start()
|
self._update_timer.start()
|
||||||
|
@ -175,6 +181,10 @@ class ObjectsModel(ListModel):
|
||||||
|
|
||||||
all_nodes = self._renameNodes(name_to_node_info_dict)
|
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:
|
for node in all_nodes:
|
||||||
if hasattr(node, "isOutsideBuildArea"):
|
if hasattr(node, "isOutsideBuildArea"):
|
||||||
is_outside_build_area = node.isOutsideBuildArea() # type: ignore
|
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
|
# for anti overhang meshes and groups the extruder nr is irrelevant
|
||||||
extruder_number = -1
|
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({
|
nodes.append({
|
||||||
"name": node.getName(),
|
"name": name,
|
||||||
"selected": Selection.isSelected(node),
|
"selected": Selection.isSelected(node),
|
||||||
"outside_build_area": is_outside_build_area,
|
"outside_build_area": is_outside_build_area,
|
||||||
"buildplate_number": node_build_plate_number,
|
"buildplate_number": node_build_plate_number,
|
||||||
|
@ -234,5 +249,5 @@ class ObjectsModel(ListModel):
|
||||||
"node": node
|
"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)
|
self.setItems(nodes)
|
||||||
|
|
|
@ -184,6 +184,9 @@ class ThreeMFReader(MeshReader):
|
||||||
else:
|
else:
|
||||||
Logger.log("w", "Unable to find extruder in position %s", setting_value)
|
Logger.log("w", "Unable to find extruder in position %s", setting_value)
|
||||||
continue
|
continue
|
||||||
|
if key == "print_order":
|
||||||
|
um_node.printOrder = int(setting_value)
|
||||||
|
continue
|
||||||
if key in known_setting_keys:
|
if key in known_setting_keys:
|
||||||
setting_container.setProperty(key, "value", setting_value)
|
setting_container.setProperty(key, "value", setting_value)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -353,6 +353,11 @@ class WorkspaceDialog(QObject):
|
||||||
|
|
||||||
Application.getInstance().getBackend().close()
|
Application.getInstance().getBackend().close()
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
|
def setDropToBuildPlateForModel(self, drop_to_buildplate: bool) -> None:
|
||||||
|
CuraApplication.getInstance().getWorkplaceDropToBuildplate(drop_to_buildplate)
|
||||||
|
|
||||||
|
|
||||||
def setMaterialConflict(self, material_conflict: bool) -> None:
|
def setMaterialConflict(self, material_conflict: bool) -> None:
|
||||||
if self._has_material_conflict != material_conflict:
|
if self._has_material_conflict != material_conflict:
|
||||||
self._has_material_conflict = material_conflict
|
self._has_material_conflict = material_conflict
|
||||||
|
|
|
@ -351,6 +351,25 @@ UM.Dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row
|
||||||
|
{
|
||||||
|
id: dropToBuildPlate
|
||||||
|
width: parent.width
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
UM.CheckBox
|
||||||
|
{
|
||||||
|
id: checkDropModels
|
||||||
|
text: catalog.i18nc("@text:window", "Drop models to buildplate")
|
||||||
|
checked: UM.Preferences.getValue("physics/automatic_drop_down")
|
||||||
|
onCheckedChanged: manager.setDropToBuildPlateForModel(checked)
|
||||||
|
}
|
||||||
|
function reloadValue()
|
||||||
|
{
|
||||||
|
checkDropModels.checked = UM.Preferences.getValue("physics/automatic_drop_down")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Row
|
Row
|
||||||
{
|
{
|
||||||
id: clearBuildPlateWarning
|
id: clearBuildPlateWarning
|
||||||
|
@ -473,6 +492,7 @@ UM.Dialog
|
||||||
materialSection.reloadValues()
|
materialSection.reloadValues()
|
||||||
profileSection.reloadValues()
|
profileSection.reloadValues()
|
||||||
printerSection.reloadValues()
|
printerSection.reloadValues()
|
||||||
|
dropToBuildPlate.reloadValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ from cura.CuraApplication import CuraApplication
|
||||||
from cura.CuraPackageManager import CuraPackageManager
|
from cura.CuraPackageManager import CuraPackageManager
|
||||||
from cura.Settings import CuraContainerStack
|
from cura.Settings import CuraContainerStack
|
||||||
from cura.Utils.Threading import call_on_qt_thread
|
from cura.Utils.Threading import call_on_qt_thread
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
from cura.Snapshot import Snapshot
|
from cura.Snapshot import Snapshot
|
||||||
|
|
||||||
from PyQt6.QtCore import QBuffer
|
from PyQt6.QtCore import QBuffer
|
||||||
|
@ -148,6 +149,9 @@ class ThreeMFWriter(MeshWriter):
|
||||||
for key in model_exported_settings:
|
for key in model_exported_settings:
|
||||||
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
|
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.
|
# Store the metadata.
|
||||||
for key, value in um_node.metadata.items():
|
for key, value in um_node.metadata.items():
|
||||||
savitar_node.setSetting(key, value)
|
savitar_node.setSetting(key, value)
|
||||||
|
|
|
@ -76,6 +76,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._default_engine_location = executable_name
|
self._default_engine_location = executable_name
|
||||||
|
|
||||||
search_path = [
|
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.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), "bin")),
|
||||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
|
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pytest
|
pytest
|
||||||
pyinstaller==5.8.0
|
pyinstaller==6.3.0
|
||||||
pyinstaller-hooks-contrib
|
pyinstaller-hooks-contrib
|
||||||
pyyaml
|
pyyaml
|
||||||
sip==6.5.1
|
sip==6.5.1
|
||||||
|
|
|
@ -5135,7 +5135,7 @@
|
||||||
"unit": "mm",
|
"unit": "mm",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"minimum_value": "0",
|
"minimum_value": "0",
|
||||||
"maximum_value_warning": "machine_nozzle_size",
|
"maximum_value_warning": "5*layer_height",
|
||||||
"default_value": 0.1,
|
"default_value": 0.1,
|
||||||
"limit_to_extruder": "support_interface_extruder_nr if support_interface_enable else support_infill_extruder_nr",
|
"limit_to_extruder": "support_interface_extruder_nr if support_interface_enable else support_infill_extruder_nr",
|
||||||
"enabled": "support_enable or support_meshes_present",
|
"enabled": "support_enable or support_meshes_present",
|
||||||
|
@ -7246,6 +7246,16 @@
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": 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":
|
"infill_mesh":
|
||||||
{
|
{
|
||||||
"label": "Infill Mesh",
|
"label": "Infill Mesh",
|
||||||
|
|
|
@ -106,6 +106,7 @@
|
||||||
"retraction_combing_max_distance": { "value": 15 },
|
"retraction_combing_max_distance": { "value": 15 },
|
||||||
"retraction_count_max": { "value": 25 },
|
"retraction_count_max": { "value": 25 },
|
||||||
"retraction_extrusion_window": { "value": 1 },
|
"retraction_extrusion_window": { "value": 1 },
|
||||||
|
"retraction_min_travel": { "value": 5 },
|
||||||
"roofing_layer_count": { "value": "1" },
|
"roofing_layer_count": { "value": "1" },
|
||||||
"roofing_material_flow": { "value": "material_flow" },
|
"roofing_material_flow": { "value": "material_flow" },
|
||||||
"skin_angles": { "value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]" },
|
"skin_angles": { "value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]" },
|
||||||
|
|
|
@ -156,7 +156,6 @@
|
||||||
"retraction_hop": { "value": "2" },
|
"retraction_hop": { "value": "2" },
|
||||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||||
"retraction_hop_only_when_collides": { "value": "True" },
|
"retraction_hop_only_when_collides": { "value": "True" },
|
||||||
"retraction_min_travel": { "value": "5" },
|
|
||||||
"retraction_prime_speed": { "value": "15" },
|
"retraction_prime_speed": { "value": "15" },
|
||||||
"skin_overlap": { "value": "10" },
|
"skin_overlap": { "value": "10" },
|
||||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||||
|
|
|
@ -373,7 +373,6 @@
|
||||||
"retraction_hop": { "value": 0.4 },
|
"retraction_hop": { "value": 0.4 },
|
||||||
"retraction_hop_enabled": { "value": true },
|
"retraction_hop_enabled": { "value": true },
|
||||||
"retraction_hop_only_when_collides": { "value": false },
|
"retraction_hop_only_when_collides": { "value": false },
|
||||||
"retraction_min_travel": { "value": "line_width * 4" },
|
|
||||||
"retraction_prime_speed": { "value": "retraction_speed" },
|
"retraction_prime_speed": { "value": "retraction_speed" },
|
||||||
"retraction_speed": { "value": 5 },
|
"retraction_speed": { "value": 5 },
|
||||||
"roofing_layer_count": { "value": 2 },
|
"roofing_layer_count": { "value": 2 },
|
||||||
|
|
|
@ -108,7 +108,6 @@
|
||||||
"retraction_hop": { "value": "2" },
|
"retraction_hop": { "value": "2" },
|
||||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||||
"retraction_hop_only_when_collides": { "value": "True" },
|
"retraction_hop_only_when_collides": { "value": "True" },
|
||||||
"retraction_min_travel": { "value": "5" },
|
|
||||||
"retraction_prime_speed": { "value": "15" },
|
"retraction_prime_speed": { "value": "15" },
|
||||||
"retraction_speed": { "value": "45" },
|
"retraction_speed": { "value": "45" },
|
||||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||||
|
|
|
@ -110,7 +110,6 @@
|
||||||
"retraction_hop": { "value": "2" },
|
"retraction_hop": { "value": "2" },
|
||||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||||
"retraction_hop_only_when_collides": { "value": "True" },
|
"retraction_hop_only_when_collides": { "value": "True" },
|
||||||
"retraction_min_travel": { "value": "5" },
|
|
||||||
"retraction_prime_speed": { "value": "15" },
|
"retraction_prime_speed": { "value": "15" },
|
||||||
"retraction_speed": { "value": "45" },
|
"retraction_speed": { "value": "45" },
|
||||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||||
|
|
|
@ -4946,6 +4946,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Rozdělit modely"
|
msgstr "Rozdělit modely"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Tisknout před"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Tisknout po"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Odinstalovat"
|
msgstr "Odinstalovat"
|
||||||
|
|
|
@ -2583,6 +2583,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Tisková sekvence"
|
msgstr "Tisková sekvence"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Nastavit tiskovou sekvenci ručně"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Umožňuje řadit seznam objektů pro ruční nastavení tiskové sekvence. První objekt ze seznamu bude vytisknut jako první."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Rychlost tisku"
|
msgstr "Rychlost tisku"
|
||||||
|
|
|
@ -4565,6 +4565,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -4930,6 +4930,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Gruppierung für Modelle aufheben"
|
msgstr "Gruppierung für Modelle aufheben"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Vor dem Drucken"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Nach dem Drucken"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Deinstallieren"
|
msgstr "Deinstallieren"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Druckreihenfolge"
|
msgstr "Druckreihenfolge"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Druckreihenfolge manuell einstellen"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Ermöglicht das Ordnen der Objektliste, um die Druckreihenfolge manuell festzulegen. Das erste Objekt aus der Liste wird zuerst gedruckt."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Druckgeschwindigkeit"
|
msgstr "Druckgeschwindigkeit"
|
||||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Desagrupar modelos"
|
msgstr "Desagrupar modelos"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Imprimir antes"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Imprimir después"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Desinstalar"
|
msgstr "Desinstalar"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Secuencia de impresión"
|
msgstr "Secuencia de impresión"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Establecer secuencia de impresión manualmente"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Permite ordenar la lista de objetos para establecer la secuencia de impresión manualmente. El primer objeto de la lista se imprimirá primero."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Velocidad de impresión"
|
msgstr "Velocidad de impresión"
|
||||||
|
|
|
@ -4588,6 +4588,14 @@ msgctxt "print_sequence option one_at_a_time"
|
||||||
msgid "One at a Time"
|
msgid "One at a Time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgctxt "infill_mesh label"
|
msgctxt "infill_mesh label"
|
||||||
msgid "Infill Mesh"
|
msgid "Infill Mesh"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -4899,6 +4899,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Poista mallien ryhmitys"
|
msgstr "Poista mallien ryhmitys"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Tulosta ennen"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Tulosta jälkeen"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2578,6 +2578,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Tulostusjärjestys"
|
msgstr "Tulostusjärjestys"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Aseta tulostusjärjestys manuaalisesti"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Mahdollistaa kohteiden järjestämisen tulostusjärjestyksen manuaaliseen asettamiseen. Listan ensimmäinen kohde tulostetaan ensin."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Tulostusnopeus"
|
msgstr "Tulostusnopeus"
|
||||||
|
|
|
@ -4928,6 +4928,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Dégrouper les modèles"
|
msgstr "Dégrouper les modèles"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Imprimer avant"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Imprimer après"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Désinstaller"
|
msgstr "Désinstaller"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Séquence d'impression"
|
msgstr "Séquence d'impression"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Définir la séquence d'impression manuellement"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Permet de classer la liste des objets pour définir manuellement la séquence d'impression. Le premier objet de la liste sera imprimé en premier."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Vitesse d’impression"
|
msgstr "Vitesse d’impression"
|
||||||
|
|
|
@ -4913,6 +4913,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Csoport bontása"
|
msgstr "Csoport bontása"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Nyomtatás előtt"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Nyomtatás után"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Nyomtatási sorrend"
|
msgstr "Nyomtatási sorrend"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Nyomtatási sorrend kézi beállítása"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Lehetővé teszi az objektumlista rendezését a nyomtatási sorrend kézi beállításához. A lista első objektuma lesz először nyomtatva."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Nyomtatási sebesség"
|
msgstr "Nyomtatási sebesség"
|
||||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Separa modelli"
|
msgstr "Separa modelli"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Stampa prima"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Stampa dopo"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Disinstalla"
|
msgstr "Disinstalla"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Sequenza di stampa"
|
msgstr "Sequenza di stampa"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Imposta manualmente la sequenza di stampa"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Consente di ordinare l'elenco degli oggetti per impostare manualmente la sequenza di stampa. Il primo oggetto dell'elenco sarà stampato per primo."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Velocità di stampa"
|
msgstr "Velocità di stampa"
|
||||||
|
|
|
@ -4914,6 +4914,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "モデルを非グループ化"
|
msgstr "モデルを非グループ化"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "印刷前"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "印刷後"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "アンインストール"
|
msgstr "アンインストール"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "印刷頻度"
|
msgstr "印刷頻度"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "手動で印刷順序を設定する"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "オブジェクトリストを並べ替えて、手動で印刷順序を設定することができます。リストの最初のオブジェクトが最初に印刷されます。"
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "印刷速度"
|
msgstr "印刷速度"
|
||||||
|
|
|
@ -4917,6 +4917,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "모델 그룹 해제"
|
msgstr "모델 그룹 해제"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "인쇄 전"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "인쇄 후"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "설치 제거"
|
msgstr "설치 제거"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "프린팅 순서"
|
msgstr "프린팅 순서"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "수동으로 인쇄 순서 설정"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "객체 목록을 정렬하여 수동으로 인쇄 순서를 설정할 수 있습니다. 목록의 첫 번째 객체가 먼저 인쇄됩니다."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "프린팅 속도"
|
msgstr "프린팅 속도"
|
||||||
|
|
|
@ -4925,6 +4925,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Groeperen van Modellen Opheffen"
|
msgstr "Groeperen van Modellen Opheffen"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Afdrukken voor"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Afdrukken na"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "De-installeren"
|
msgstr "De-installeren"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Printvolgorde"
|
msgstr "Printvolgorde"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Handmatig afdrukvolgorde instellen"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Maakt het mogelijk de objectlijst te ordenen om de afdrukvolgorde handmatig in te stellen. Het eerste object van de lijst wordt als eerste afgedrukt."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Printsnelheid"
|
msgstr "Printsnelheid"
|
||||||
|
|
|
@ -4916,6 +4916,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Rozgrupuj modele"
|
msgstr "Rozgrupuj modele"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Drukuj przed"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Drukuj po"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2584,6 +2584,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Sekwencja Wydruku"
|
msgstr "Sekwencja Wydruku"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Ręczne ustawienie kolejności drukowania"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Umożliwia ręczne ustawienie kolejności drukowania na liście obiektów. Pierwszy obiekt z listy zostanie wydrukowany jako pierwszy."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Prędkość Druku"
|
msgstr "Prędkość Druku"
|
||||||
|
|
|
@ -4942,6 +4942,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Desagrupar Modelos"
|
msgstr "Desagrupar Modelos"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Imprimir antes"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Imprimir depois"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Desinstalar"
|
msgstr "Desinstalar"
|
||||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Sequência de Impressão"
|
msgstr "Sequência de Impressão"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Definir sequência de impressão manualmente"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Permite ordenar a lista de objetos para definir a sequência de impressão manualmente. O primeiro objeto da lista será impresso primeiro."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Velocidade de Impressão"
|
msgstr "Velocidade de Impressão"
|
||||||
|
|
|
@ -4932,6 +4932,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Desagrupar Modelos"
|
msgstr "Desagrupar Modelos"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Imprimir antes"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Imprimir depois"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Desinstalar"
|
msgstr "Desinstalar"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Sequência de impressão"
|
msgstr "Sequência de impressão"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Definir sequência de impressão manualmente"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Permite ordenar a lista de objetos para definir a sequência de impressão manualmente. O primeiro objeto da lista será impresso primeiro."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Velocidade de Impressão"
|
msgstr "Velocidade de Impressão"
|
||||||
|
|
|
@ -4955,6 +4955,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Разгруппировать модели"
|
msgstr "Разгруппировать модели"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Печатать до"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Печатать после"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Удалить"
|
msgstr "Удалить"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Последовательная печать"
|
msgstr "Последовательная печать"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Установить последовательность печати вручную"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Позволяет упорядочить список объектов для ручной настройки последовательности печати. Первый объект из списка будет напечатан первым."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Скорость печати"
|
msgstr "Скорость печати"
|
||||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "Model Grubunu Çöz"
|
msgstr "Model Grubunu Çöz"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "Önce Yazdır"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "Sonra Yazdır"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "Kaldır"
|
msgstr "Kaldır"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "Yazdırma Dizisi"
|
msgstr "Yazdırma Dizisi"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "Baskı Sırasını Manuel Olarak Ayarla"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "Nesne listesini sıralayarak baskı sırasını manuel olarak ayarlamayı sağlar. Listeden ilk nesne ilk olarak basılacak."
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "Yazdırma Hızı"
|
msgstr "Yazdırma Hızı"
|
||||||
|
|
|
@ -4919,6 +4919,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "拆分模型"
|
msgstr "拆分模型"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "打印前"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "打印后"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr "卸载"
|
msgstr "卸载"
|
||||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "打印序列"
|
msgstr "打印序列"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "手动设置打印顺序"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "允许对对象列表进行排序,以手动设置打印顺序。列表中的第一个对象将首先被打印。"
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "打印速度"
|
msgstr "打印速度"
|
||||||
|
|
|
@ -4911,6 +4911,14 @@ msgctxt "@action:inmenu menubar:edit"
|
||||||
msgid "Ungroup Models"
|
msgid "Ungroup Models"
|
||||||
msgstr "取消模型群組"
|
msgstr "取消模型群組"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print Before"
|
||||||
|
msgstr "列印前"
|
||||||
|
|
||||||
|
msgctxt "@action:inmenu menubar:edit"
|
||||||
|
msgid "Print After"
|
||||||
|
msgstr "列印後"
|
||||||
|
|
||||||
msgctxt "@button"
|
msgctxt "@button"
|
||||||
msgid "Uninstall"
|
msgid "Uninstall"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
||||||
msgid "Print Sequence"
|
msgid "Print Sequence"
|
||||||
msgstr "列印順序"
|
msgstr "列印順序"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled label"
|
||||||
|
msgid "Set Print Sequence Manually"
|
||||||
|
msgstr "手動設置列印順序"
|
||||||
|
|
||||||
|
msgctxt "user_defined_print_order_enabled description"
|
||||||
|
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||||
|
msgstr "允許手動設置物件列表以設定列印順序。列表中的第一個物件將首先被列印。"
|
||||||
|
|
||||||
msgctxt "speed_print label"
|
msgctxt "speed_print label"
|
||||||
msgid "Print Speed"
|
msgid "Print Speed"
|
||||||
msgstr "列印速度"
|
msgstr "列印速度"
|
||||||
|
|
|
@ -35,6 +35,9 @@ Item
|
||||||
property alias mergeObjects: mergeObjectsAction
|
property alias mergeObjects: mergeObjectsAction
|
||||||
//property alias unMergeObjects: unMergeObjectsAction
|
//property alias unMergeObjects: unMergeObjectsAction
|
||||||
|
|
||||||
|
property alias printObjectBeforePrevious: printObjectBeforePreviousAction
|
||||||
|
property alias printObjectAfterNext: printObjectAfterNextAction
|
||||||
|
|
||||||
property alias multiplyObject: multiplyObjectAction
|
property alias multiplyObject: multiplyObjectAction
|
||||||
|
|
||||||
property alias selectAll: selectAllAction
|
property alias selectAll: selectAllAction
|
||||||
|
@ -405,6 +408,26 @@ Item
|
||||||
onTriggered: CuraApplication.ungroupSelected()
|
onTriggered: CuraApplication.ungroupSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: printObjectBeforePreviousAction
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:edit","Print Before") + " " + PrintOrderManager.previousNodeName
|
||||||
|
enabled: PrintOrderManager.shouldEnablePrintBeforeAction
|
||||||
|
icon.name: "print-before"
|
||||||
|
shortcut: "PgUp"
|
||||||
|
onTriggered: PrintOrderManager.swapSelectedAndPreviousNodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
Action
|
||||||
|
{
|
||||||
|
id: printObjectAfterNextAction
|
||||||
|
text: catalog.i18nc("@action:inmenu menubar:edit","Print After") + " " + PrintOrderManager.nextNodeName
|
||||||
|
enabled: PrintOrderManager.shouldEnablePrintAfterAction
|
||||||
|
icon.name: "print-after"
|
||||||
|
shortcut: "PgDown"
|
||||||
|
onTriggered: PrintOrderManager.swapSelectedAndNextNodes()
|
||||||
|
}
|
||||||
|
|
||||||
Action
|
Action
|
||||||
{
|
{
|
||||||
id: mergeObjectsAction
|
id: mergeObjectsAction
|
||||||
|
|
|
@ -78,6 +78,19 @@ Cura.Menu
|
||||||
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
||||||
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
||||||
|
|
||||||
|
// Edit print sequence actions
|
||||||
|
Cura.MenuSeparator { visible: PrintOrderManager.shouldShowEditPrintOrderActions }
|
||||||
|
Cura.MenuItem
|
||||||
|
{
|
||||||
|
action: Cura.Actions.printObjectBeforePrevious
|
||||||
|
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||||
|
}
|
||||||
|
Cura.MenuItem
|
||||||
|
{
|
||||||
|
action: Cura.Actions.printObjectAfterNext
|
||||||
|
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||||
|
}
|
||||||
|
|
||||||
Connections
|
Connections
|
||||||
{
|
{
|
||||||
target: UM.Controller
|
target: UM.Controller
|
||||||
|
|
|
@ -25,4 +25,17 @@ Cura.Menu
|
||||||
Cura.MenuItem { action: Cura.Actions.groupObjects }
|
Cura.MenuItem { action: Cura.Actions.groupObjects }
|
||||||
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
Cura.MenuItem { action: Cura.Actions.mergeObjects }
|
||||||
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
Cura.MenuItem { action: Cura.Actions.unGroupObjects }
|
||||||
|
|
||||||
|
// Edit print sequence actions
|
||||||
|
Cura.MenuSeparator { visible: PrintOrderManager.shouldShowEditPrintOrderActions }
|
||||||
|
Cura.MenuItem
|
||||||
|
{
|
||||||
|
action: Cura.Actions.printObjectBeforePrevious
|
||||||
|
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||||
|
}
|
||||||
|
Cura.MenuItem
|
||||||
|
{
|
||||||
|
action: Cura.Actions.printObjectAfterNext
|
||||||
|
visible: PrintOrderManager.shouldShowEditPrintOrderActions
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -509,10 +509,13 @@ UM.PreferencesPage
|
||||||
id: dropDownCheckbox
|
id: dropDownCheckbox
|
||||||
text: catalog.i18nc("@option:check", "Automatically drop models to the build plate")
|
text: catalog.i18nc("@option:check", "Automatically drop models to the build plate")
|
||||||
checked: boolCheck(UM.Preferences.getValue("physics/automatic_drop_down"))
|
checked: boolCheck(UM.Preferences.getValue("physics/automatic_drop_down"))
|
||||||
onCheckedChanged: UM.Preferences.setValue("physics/automatic_drop_down", checked)
|
onCheckedChanged:
|
||||||
|
{
|
||||||
|
UM.Preferences.setValue("physics/automatic_drop_down", checked)
|
||||||
|
CuraApplication.getWorkplaceDropToBuildplate(checked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UM.TooltipArea
|
UM.TooltipArea
|
||||||
{
|
{
|
||||||
|
@ -627,6 +630,8 @@ UM.PreferencesPage
|
||||||
UM.TooltipArea
|
UM.TooltipArea
|
||||||
{
|
{
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
|
// Mac only allows applications to run as a single instance, so providing the option for this os doesn't make much sense
|
||||||
|
visible: Qt.platform.os !== "osx"
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
text: catalog.i18nc("@info:tooltip","Should opening files from the desktop or external applications open in the same instance of Cura?")
|
text: catalog.i18nc("@info:tooltip","Should opening files from the desktop or external applications open in the same instance of Cura?")
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import Cura 1.0 as Cura
|
||||||
UM.ManagementPage
|
UM.ManagementPage
|
||||||
{
|
{
|
||||||
id: base
|
id: base
|
||||||
property var machineActionManager: CuraApplication.getMachineActionManagerQml()
|
|
||||||
Item { enabled: false; UM.I18nCatalog { id: catalog; name: "cura"} }
|
Item { enabled: false; UM.I18nCatalog { id: catalog; name: "cura"} }
|
||||||
|
|
||||||
title: catalog.i18nc("@title:tab", "Printers")
|
title: catalog.i18nc("@title:tab", "Printers")
|
||||||
|
@ -63,7 +62,7 @@ UM.ManagementPage
|
||||||
Repeater
|
Repeater
|
||||||
{
|
{
|
||||||
id: machineActionRepeater
|
id: machineActionRepeater
|
||||||
model: base.currentItem ? machineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id)) : null
|
model: base.currentItem ? CuraApplication.getSupportedActionMachineList(base.currentItem.id) : null
|
||||||
|
|
||||||
Item
|
Item
|
||||||
{
|
{
|
||||||
|
|
|
@ -222,7 +222,7 @@ Item
|
||||||
UM.Label
|
UM.Label
|
||||||
{
|
{
|
||||||
id: toolHint
|
id: toolHint
|
||||||
text: UM.Controller.properties.getValue("ToolHint") != undefined ? UM.ActiveTool.properties.getValue("ToolHint") : ""
|
text: UM.Controller.properties.getValue("ToolHint") != undefined ? UM.Controller.properties.getValue("ToolHint") : ""
|
||||||
color: UM.Theme.getColor("tooltip_text")
|
color: UM.Theme.getColor("tooltip_text")
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ machine_nozzle_heat_up_speed = 1.4
|
||||||
material_print_temperature = =default_material_print_temperature - 20
|
material_print_temperature = =default_material_print_temperature - 20
|
||||||
ooze_shield_angle = 40
|
ooze_shield_angle = 40
|
||||||
raft_airgap = 0.4
|
raft_airgap = 0.4
|
||||||
retraction_min_travel = 5
|
|
||||||
speed_print = 70
|
speed_print = 70
|
||||||
speed_topbottom = =math.ceil(speed_print * 30 / 70)
|
speed_topbottom = =math.ceil(speed_print * 30 / 70)
|
||||||
speed_wall = =math.ceil(speed_print * 30 / 70)
|
speed_wall = =math.ceil(speed_print * 30 / 70)
|
||||||
|
|
|
@ -25,7 +25,6 @@ prime_tower_wipe_enabled = True
|
||||||
raft_airgap = 0.25
|
raft_airgap = 0.25
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 50
|
speed_print = 50
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
|
@ -28,7 +28,6 @@ retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.2
|
retraction_extra_prime_amount = 0.2
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
retraction_prime_speed = 15
|
retraction_prime_speed = 15
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_wall = =math.ceil(speed_print * 25 / 25)
|
speed_wall = =math.ceil(speed_print * 25 / 25)
|
||||||
|
|
|
@ -27,7 +27,6 @@ acceleration_wall_x = =acceleration_wall
|
||||||
bridge_skin_speed = =bridge_wall_speed
|
bridge_skin_speed = =bridge_wall_speed
|
||||||
bridge_sparse_infill_max_density = 50
|
bridge_sparse_infill_max_density = 50
|
||||||
bridge_wall_speed = 30
|
bridge_wall_speed = 30
|
||||||
cool_fan_speed_0 = 0
|
|
||||||
cool_min_layer_time = 4
|
cool_min_layer_time = 4
|
||||||
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
||||||
infill_sparse_density = 15
|
infill_sparse_density = 15
|
||||||
|
@ -65,7 +64,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -63,7 +63,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -64,7 +64,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -64,7 +64,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -26,7 +26,6 @@ prime_tower_wipe_enabled = True
|
||||||
raft_airgap = 0.25
|
raft_airgap = 0.25
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 50
|
speed_print = 50
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
|
@ -25,7 +25,6 @@ prime_tower_wipe_enabled = True
|
||||||
raft_airgap = 0.25
|
raft_airgap = 0.25
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 50
|
speed_print = 50
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
|
@ -26,7 +26,6 @@ prime_tower_wipe_enabled = True
|
||||||
raft_airgap = 0.25
|
raft_airgap = 0.25
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 50
|
speed_print = 50
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
|
@ -25,7 +25,6 @@ prime_tower_wipe_enabled = True
|
||||||
raft_airgap = 0.25
|
raft_airgap = 0.25
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 50
|
speed_print = 50
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||||
|
|
|
@ -28,7 +28,6 @@ retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
||||||
speed_wall = =math.ceil(speed_print * 25 / 25)
|
speed_wall = =math.ceil(speed_print * 25 / 25)
|
||||||
|
|
|
@ -29,7 +29,6 @@ retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
||||||
speed_wall = =math.ceil(speed_print * 25 / 25)
|
speed_wall = =math.ceil(speed_print * 25 / 25)
|
||||||
|
|
|
@ -29,7 +29,6 @@ retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop = 2
|
retraction_hop = 2
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = 0.8
|
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
speed_topbottom = =math.ceil(speed_print * 25 / 25)
|
||||||
speed_wall = =math.ceil(speed_print * 25 / 25)
|
speed_wall = =math.ceil(speed_print * 25 / 25)
|
||||||
|
|
|
@ -28,7 +28,6 @@ prime_tower_wipe_enabled = True
|
||||||
retraction_count_max = 15
|
retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = =line_width * 2
|
|
||||||
skin_line_width = =round(line_width / 0.8, 2)
|
skin_line_width = =round(line_width / 0.8, 2)
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 0.8)
|
speed_topbottom = =math.ceil(speed_print * 0.8)
|
||||||
|
|
|
@ -29,7 +29,6 @@ prime_tower_wipe_enabled = True
|
||||||
retraction_count_max = 15
|
retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = =line_width * 2
|
|
||||||
skin_line_width = =round(line_width / 0.8, 2)
|
skin_line_width = =round(line_width / 0.8, 2)
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 0.8)
|
speed_topbottom = =math.ceil(speed_print * 0.8)
|
||||||
|
|
|
@ -28,7 +28,6 @@ prime_tower_wipe_enabled = True
|
||||||
retraction_count_max = 15
|
retraction_count_max = 15
|
||||||
retraction_extra_prime_amount = 0.8
|
retraction_extra_prime_amount = 0.8
|
||||||
retraction_hop_only_when_collides = True
|
retraction_hop_only_when_collides = True
|
||||||
retraction_min_travel = =line_width * 2
|
|
||||||
skin_line_width = =round(line_width / 0.8, 2)
|
skin_line_width = =round(line_width / 0.8, 2)
|
||||||
speed_print = 25
|
speed_print = 25
|
||||||
speed_topbottom = =math.ceil(speed_print * 0.8)
|
speed_topbottom = =math.ceil(speed_print * 0.8)
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -66,7 +66,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -28,6 +28,7 @@ bridge_skin_speed = =bridge_wall_speed
|
||||||
bridge_sparse_infill_max_density = 50
|
bridge_sparse_infill_max_density = 50
|
||||||
bridge_wall_speed = 30
|
bridge_wall_speed = 30
|
||||||
cool_min_layer_time = 4
|
cool_min_layer_time = 4
|
||||||
|
infill_material_flow = =1.05 * material_flow
|
||||||
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
||||||
infill_sparse_density = 15
|
infill_sparse_density = 15
|
||||||
jerk_infill = =jerk_print
|
jerk_infill = =jerk_print
|
||||||
|
@ -49,6 +50,7 @@ raft_airgap = 0.15
|
||||||
retraction_amount = 6.5
|
retraction_amount = 6.5
|
||||||
retraction_prime_speed = 15
|
retraction_prime_speed = 15
|
||||||
retraction_speed = 45
|
retraction_speed = 45
|
||||||
|
skin_material_flow = =material_flow
|
||||||
small_skin_on_surface = False
|
small_skin_on_surface = False
|
||||||
small_skin_width = 4
|
small_skin_width = 4
|
||||||
speed_infill = =speed_print
|
speed_infill = =speed_print
|
||||||
|
@ -67,9 +69,11 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
|
wall_x_material_flow = =1.05 * wall_material_flow
|
||||||
|
wall_x_material_flow_roofing = =wall_material_flow
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
z_seam_type = back
|
z_seam_type = back
|
||||||
zig_zaggify_infill = True
|
zig_zaggify_infill = True
|
||||||
|
|
|
@ -67,7 +67,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.4
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
||||||
|
|
|
@ -64,7 +64,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -64,7 +64,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -28,6 +28,7 @@ bridge_skin_speed = =bridge_wall_speed
|
||||||
bridge_sparse_infill_max_density = 50
|
bridge_sparse_infill_max_density = 50
|
||||||
bridge_wall_speed = 30
|
bridge_wall_speed = 30
|
||||||
cool_min_layer_time = 4
|
cool_min_layer_time = 4
|
||||||
|
infill_material_flow = =1.1 * material_flow
|
||||||
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
||||||
infill_sparse_density = 15
|
infill_sparse_density = 15
|
||||||
jerk_infill = =jerk_print
|
jerk_infill = =jerk_print
|
||||||
|
@ -48,6 +49,7 @@ prime_tower_enable = False
|
||||||
retraction_amount = 8
|
retraction_amount = 8
|
||||||
retraction_prime_speed = 15
|
retraction_prime_speed = 15
|
||||||
retraction_speed = 45
|
retraction_speed = 45
|
||||||
|
skin_material_flow = =1.05 * material_flow
|
||||||
small_skin_on_surface = False
|
small_skin_on_surface = False
|
||||||
small_skin_width = 4
|
small_skin_width = 4
|
||||||
speed_infill = =speed_print
|
speed_infill = =speed_print
|
||||||
|
@ -66,9 +68,11 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
|
wall_x_material_flow = =1.1 * wall_material_flow
|
||||||
|
wall_x_material_flow_roofing = =wall_material_flow
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
z_seam_type = back
|
z_seam_type = back
|
||||||
zig_zaggify_infill = True
|
zig_zaggify_infill = True
|
||||||
|
|
|
@ -66,7 +66,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.4
|
||||||
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
top_bottom_thickness = =max(1.2 , layer_height * 6)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -66,7 +66,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -28,6 +28,7 @@ bridge_skin_speed = =bridge_wall_speed
|
||||||
bridge_sparse_infill_max_density = 50
|
bridge_sparse_infill_max_density = 50
|
||||||
bridge_wall_speed = 30
|
bridge_wall_speed = 30
|
||||||
cool_min_layer_time = 6
|
cool_min_layer_time = 6
|
||||||
|
infill_material_flow = =1.1 * material_flow
|
||||||
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
infill_pattern = ='zigzag' if infill_sparse_density > 80 else 'grid'
|
||||||
infill_sparse_density = 15
|
infill_sparse_density = 15
|
||||||
jerk_infill = =jerk_print
|
jerk_infill = =jerk_print
|
||||||
|
@ -49,6 +50,7 @@ raft_airgap = 0.25
|
||||||
retraction_amount = 6.5
|
retraction_amount = 6.5
|
||||||
retraction_prime_speed = =retraction_speed
|
retraction_prime_speed = =retraction_speed
|
||||||
retraction_speed = 45
|
retraction_speed = 45
|
||||||
|
skin_material_flow = =1.05 * material_flow
|
||||||
small_skin_on_surface = False
|
small_skin_on_surface = False
|
||||||
small_skin_width = 4
|
small_skin_width = 4
|
||||||
speed_infill = =speed_print
|
speed_infill = =speed_print
|
||||||
|
@ -67,9 +69,11 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
|
wall_x_material_flow = =1.1 * wall_material_flow
|
||||||
|
wall_x_material_flow_roofing = =wall_material_flow
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
z_seam_type = back
|
z_seam_type = back
|
||||||
zig_zaggify_infill = True
|
zig_zaggify_infill = True
|
||||||
|
|
|
@ -67,7 +67,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
wall_line_width_0 = =line_width * (1 + magic_spiralize * 0.25)
|
||||||
|
|
|
@ -65,7 +65,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.3
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
|
@ -66,7 +66,7 @@ support_bottom_distance = =support_z_distance
|
||||||
support_interface_enable = True
|
support_interface_enable = True
|
||||||
support_structure = tree
|
support_structure = tree
|
||||||
support_top_distance = =support_z_distance
|
support_top_distance = =support_z_distance
|
||||||
support_z_distance = =math.ceil(0.3/layer_height)*layer_height
|
support_z_distance = 0.35
|
||||||
top_bottom_thickness = =max(1 , layer_height * 5)
|
top_bottom_thickness = =max(1 , layer_height * 5)
|
||||||
wall_0_wipe_dist = 0.8
|
wall_0_wipe_dist = 0.8
|
||||||
z_seam_relative = True
|
z_seam_relative = True
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue