Merge branch 'main' into CURA-9221_show_message_cloud_approval

This commit is contained in:
joeydelarago 2022-08-24 16:15:24 +02:00
commit 9b20a1b37f
26 changed files with 639 additions and 172 deletions

View file

@ -149,5 +149,5 @@ jobs:
run: conan upload "*" -r cura --all -c run: conan upload "*" -r cura --all -c
- name: Upload the Package(s) community - name: Upload the Package(s) community
if: ${{ always() && inputs.conan_upload_community == 'true' }} if: ${{ always() && inputs.conan_upload_community == true }}
run: conan upload "*" -r cura-ce -c run: conan upload "*" -r cura-ce -c

View file

@ -102,5 +102,5 @@ jobs:
run: conan upload "*" -r cura --all -c run: conan upload "*" -r cura --all -c
- name: Upload the Package(s) community - name: Upload the Package(s) community
if: ${{ always() && inputs.conan_upload_community == 'true' }} if: ${{ always() && inputs.conan_upload_community == true }}
run: conan upload "*" -r cura-ce -c run: conan upload "*" -r cura-ce -c

View file

@ -135,6 +135,9 @@ jobs:
user = "_" user = "_"
channel = "_" channel = "_"
else: else:
if latest_branch_version.prerelease and not "." in latest_branch_version.prerelease:
# The prerealese did not contain a version number, default it to 1
latest_branch_version.prerelease += ".1"
if event_name == "pull_request": if event_name == "pull_request":
actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{latest_branch_version.patch}-{latest_branch_version.prerelease.lower()}+{buildmetadata}pr_{issue_number}_{no_commits}" actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{latest_branch_version.patch}-{latest_branch_version.prerelease.lower()}+{buildmetadata}pr_{issue_number}_{no_commits}"
else: else:

View file

@ -1,2 +1,2 @@
conan!=1.51.0,!=1.51.1 conan!=1.51.0,!=1.51.1,!=1.51.2,!=1.51.3
sip==6.5.1 sip

View file

@ -0,0 +1,25 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="{{ name }}" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="{{ module_name }}" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />{% for key, value in env_vars.items() %}
<env name="{{ key }}" value="{{ value }}" />{% endfor %}
</envs>
<option name="SDK_HOME" value="{{ sdk_path }}" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/{{ script_name }}" />
<option name="PARAMETERS" value="{{ parameters }}" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>

View file

@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="{{ name }}" type="tests" factoryName="py.test" nameIsGenerated="true">
<module name="{{ module_name }}" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />{% for key, value in env_vars.items() %}
<env name="{{ key }}" value="{{ value }}" />{% endfor %}
</envs>
<option name="SDK_HOME" value="{{ sdk_path }}" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="_new_keywords" value="&quot;&quot;" />
<option name="_new_parameters" value="&quot;&quot;" />
<option name="_new_additionalArguments" value="&quot;&quot;" />
<option name="_new_target" value="&quot;$PROJECT_DIR$/{{ script_name }}&quot;" />
<option name="_new_targetType" value="&quot;PATH&quot;" />
<method v="2" />
</configuration>
</component>

View file

@ -11,3 +11,4 @@ CuraCloudAPIVersion = "{{ cura_cloud_api_version }}"
CuraCloudAccountAPIRoot = "{{ cura_cloud_account_api_root }}" CuraCloudAccountAPIRoot = "{{ cura_cloud_account_api_root }}"
CuraMarketplaceRoot = "{{ cura_marketplace_root }}" CuraMarketplaceRoot = "{{ cura_marketplace_root }}"
CuraDigitalFactoryURL = "{{ cura_digital_factory_url }}" CuraDigitalFactoryURL = "{{ cura_digital_factory_url }}"
CuraLatestURL = "{{ cura_latest_url }}"

132
README.md
View file

@ -1,64 +1,96 @@
# Cura
<p align="center"> <br>
<a href="https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml" alt="Unit Tests">
<img src="https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml/badge.svg" /></a>
<a href="https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml" alt="Unit Tests">
<img src="https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml/badge.svg" /></a>
<a href="https://github.com/Ultimaker/Cura/issues" alt="Open Issues">
<img src="https://img.shields.io/github/issues/ultimaker/cura" /></a>
<a href="https://github.com/Ultimaker/Cura/issues?q=is%3Aissue+is%3Aclosed" alt="Closed Issues">
<img src="https://img.shields.io/github/issues-closed/ultimaker/cura?color=g" /></a>
<a href="https://github.com/Ultimaker/Cura/pulls" alt="Pull Requests">
<img src="https://img.shields.io/github/issues-pr/ultimaker/cura" /></a>
<a href="https://github.com/Ultimaker/Cura/graphs/contributors" alt="Contributors">
<img src="https://img.shields.io/github/contributors/ultimaker/cura" /></a>
<a href="https://github.com/Ultimaker/Cura" alt="Repo Size">
<img src="https://img.shields.io/github/repo-size/ultimaker/cura?style=flat" /></a>
<a href="https://github.com/Ultimaker/Cura/blob/master/LICENSE" alt="License">
<img src="https://img.shields.io/github/license/ultimaker/cura?style=flat" /></a>
</p>
Ultimaker Cura is a state-of-the-art slicer application to prepare your 3D models for printing with a 3D printer. With hundreds of settings <div align = center>
and hundreds of community-managed print profiles, Ultimaker Cura is sure to lead your next project to a success.
![Screenshot](cura-logo.PNG) [![Badge Issues]][Issues]
[![Badge PullRequests]][PullRequests]
[![Badge Closed]][Closed]
## Logging Issues [![Badge Size]][#]
[![Badge License]][License]
[![Badge Contributors]][Contributors]
For crashes and similar issues, please attach the following information: [![Badge Test]][Test]
[![Badge Conan]][Conan]
* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output) <br>
* The Cura GUI log file, located at <br>
* `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\<your username>\AppData\Roaming\cura\<Cura version>\cura.log`
* `$HOME/Library/Application Support/cura/<Cura version>/cura.log` (OSX)
* `$HOME/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux)
If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder. ![Logo]
An alternative is to install the [ExtensiveSupportLogging plugin](https://marketplace.ultimaker.com/app/cura/plugins/UltimakerPackages/ExtensiveSupportLogging)
this creates a zip folder of the relevant log files. If you're experiencing performance issues, we might ask you to connect the CPU profiler
in this plugin and attach the collected data to your support ticket.
## Running from Source # Ultimaker Cura
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source) for details about running Cura from source.
## Plugins *State-of-the-art slicer app to prepare* <br>
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins. *your 3D models for your 3D printer.*
## Supported printers *With hundreds of settings & community-managed print profiles,* <br>
Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura) for guidelines about adding support *Ultimaker Cura is sure to lead your next project to a success.*
for new machines.
## Configuring Cura <br>
Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Cura-Settings) about configuration options for developers. <br>
## Translating Cura [![Button Building]][Building]
Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Translating-Cura) about how to translate Cura into other languages. [![Button Plugins]][Plugins]
[![Button Machines]][Machines]
[![Button Report]][Report]
[![Button Settings]][Settings]
[![Button Localize]][Localize]
<br>
<br>
<picture>
<source media="(prefers-color-scheme: light)" srcset="./cura-logo.PNG">
<source media="(prefers-color-scheme: dark)" srcset="./cura-logo-dark.PNG">
<img alt="Shows cura open on the preview screen with a large benchy model in the center." src="./cura-logo.PNG">
</picture>
</div>
<br>
<!----------------------------------------------------------------------------->
[Contributors]: https://github.com/Ultimaker/Cura/graphs/contributors
[PullRequests]: https://github.com/Ultimaker/Cura/pulls
[Machines]: https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura
[Building]: https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source
[Localize]: https://github.com/Ultimaker/Cura/wiki/Translating-Cura
[Settings]: https://github.com/Ultimaker/Cura/wiki/Cura-Settings
[Plugins]: https://github.com/Ultimaker/Cura/wiki/Plugin-Directory
[Closed]: https://github.com/Ultimaker/Cura/issues?q=is%3Aissue+is%3Aclosed
[Issues]: https://github.com/Ultimaker/Cura/issues
[Conan]: https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml
[Test]: https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml
[License]: LICENSE
[Report]: docs/Report.md
[Logo]: resources/images/cura-icon.png
[#]: #
<!---------------------------------[ Badges ]---------------------------------->
[Badge Contributors]: https://img.shields.io/github/contributors/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=db5e8a&color=ab4a6c&logo=GitHub
[Badge PullRequests]: https://img.shields.io/github/issues-pr/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=bb9f3e&color=937d31&logo=GitExtensions
[Badge License]: https://img.shields.io/badge/License-LGPL3-336887.svg?style=for-the-badge&labelColor=458cb5&logoColor=white&logo=GNU
[Badge Closed]: https://img.shields.io/github/issues-closed/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=629944&color=446a30&logo=AddThis
[Badge Issues]: https://img.shields.io/github/issues/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=c34360&color=933349&logo=AdBlock
[Badge Conan]: https://img.shields.io/github/workflow/status/Ultimaker/Cura/conan-package?style=for-the-badge&logoColor=white&labelColor=6185aa&color=4c6987&logo=Conan&label=Conan%20Package
[Badge Test]: https://img.shields.io/github/workflow/status/Ultimaker/Cura/unit-test?style=for-the-badge&logoColor=white&labelColor=4a999d&color=346c6e&logo=Codacy&label=Unit%20Test
[Badge Size]: https://img.shields.io/github/repo-size/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=715a97&color=584674&logo=GoogleAnalytics
<!---------------------------------[ Buttons ]--------------------------------->
[Button Localize]: https://img.shields.io/badge/Help_Localize-e2467d?style=for-the-badge&logoColor=white&logo=GoogleTranslate
[Button Machines]: https://img.shields.io/badge/Adding_Machines-yellow?style=for-the-badge&logoColor=white&logo=CloudFoundry
[Button Settings]: https://img.shields.io/badge/Configuration-00979D?style=for-the-badge&logoColor=white&logo=CodeReview
[Button Building]: https://img.shields.io/badge/Building_Cura-blue?style=for-the-badge&logoColor=white&logo=GitBook
[Button Plugins]: https://img.shields.io/badge/Plugin_Usage-569A31?style=for-the-badge&logoColor=white&logo=ROS
[Button Report]: https://img.shields.io/badge/Report_Issues-C9284D?style=for-the-badge&logoColor=white&logo=Cliqz
## License
![License](https://img.shields.io/github/license/ultimaker/cura?style=flat)
Cura is released under terms of the LGPLv3 or higher. A copy of this license should be included with the software. Terms of the license can be found in the LICENSE file. Or at
http://www.gnu.org/licenses/lgpl.html
> But in general it boils down to:
> **You need to share the source of any Cura modifications**

View file

@ -20,6 +20,9 @@
- "fdm_materials/(latest)@ultimaker/testing" - "fdm_materials/(latest)@ultimaker/testing"
- "cura_binary_data/(latest)@ultimaker/testing" - "cura_binary_data/(latest)@ultimaker/testing"
- "cpython/3.10.4" - "cpython/3.10.4"
internal_requirements:
- "fdm_materials_private/(latest)@ultimaker/testing"
- "cura_private_data/(latest)@ultimaker/testing"
runinfo: runinfo:
entrypoint: "cura_app.py" entrypoint: "cura_app.py"
pyinstaller: pyinstaller:
@ -32,6 +35,11 @@
package: "cura" package: "cura"
src: "resources" src: "resources"
dst: "share/cura/resources" dst: "share/cura/resources"
cura_private_data:
package: "cura_private_data"
src: "resources"
dst: "share/cura/resources"
internal: true
uranium_plugins: uranium_plugins:
package: "uranium" package: "uranium"
src: "plugins" src: "plugins"
@ -60,6 +68,11 @@
package: "fdm_materials" package: "fdm_materials"
src: "materials" src: "materials"
dst: "share/cura/resources/materials" dst: "share/cura/resources/materials"
fdm_materials_private:
package: "fdm_materials_private"
src: "resources/materials"
dst: "share/cura/resources/materials"
internal: true
tcl: tcl:
package: "tcl" package: "tcl"
src: "lib/tcl8.6" src: "lib/tcl8.6"
@ -112,6 +125,9 @@
- "fdm_materials/(latest)@ultimaker/testing" - "fdm_materials/(latest)@ultimaker/testing"
- "cura_binary_data/(latest)@ultimaker/testing" - "cura_binary_data/(latest)@ultimaker/testing"
- "cpython/3.10.4" - "cpython/3.10.4"
internal_requirements:
- "fdm_materials_private/(latest)@ultimaker/testing"
- "cura_private_data/(latest)@ultimaker/testing"
runinfo: runinfo:
entrypoint: "cura_app.py" entrypoint: "cura_app.py"
pyinstaller: pyinstaller:
@ -124,6 +140,11 @@
package: "cura" package: "cura"
src: "resources" src: "resources"
dst: "share/cura/resources" dst: "share/cura/resources"
cura_private_data:
package: "cura_private_data"
src: "resources"
dst: "share/cura/resources"
internal: true
uranium_plugins: uranium_plugins:
package: "uranium" package: "uranium"
src: "plugins" src: "plugins"
@ -152,6 +173,11 @@
package: "fdm_materials" package: "fdm_materials"
src: "materials" src: "materials"
dst: "share/cura/resources/materials" dst: "share/cura/resources/materials"
fdm_materials_private:
package: "fdm_materials_private"
src: "resources/materials"
dst: "share/cura/resources/materials"
internal: true
tcl: tcl:
package: "tcl" package: "tcl"
src: "lib/tcl8.6" src: "lib/tcl8.6"
@ -286,3 +312,113 @@
Windows: "./icons/Cura.ico" Windows: "./icons/Cura.ico"
Macos: "./icons/cura.icns" Macos: "./icons/cura.icns"
Linux: "./icons/cura-128.png" Linux: "./icons/cura-128.png"
pycharm_targets:
- jinja_path: .run_templates/pycharm_cura_run.run.xml.jinja
module_name: Cura
name: cura
script_name: cura_app.py
- jinja_path: .run_templates/pycharm_cura_run.run.xml.jinja
module_name: Cura
name: cura_external_engine
parameters: --external-backend
script_name: cura_app.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in tests
script_name: tests/
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestBuildVolume.py
script_name: tests/TestBuildVolume.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestConvexHullDecorator.py
script_name: tests/TestConvexHullDecorator.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestCuraSceneNode.py
script_name: tests/TestCuraSceneNode.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestCuraSceneNode.py
script_name: tests/TestExtruderManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestGCodeListDecorator.py
script_name: tests/TestGCodeListDecorator.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestIntentManager.py
script_name: tests/TestIntentManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestLayer.py
script_name: tests/TestLayer.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestMachineAction.py
script_name: tests/TestMachineAction.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestMachineManager.py
script_name: tests/TestMachineManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestOAuth2.py
script_name: tests/TestOAuth2.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestObjectsModel.py
script_name: tests/TestObjectsModel.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestPrintInformation.py
script_name: tests/TestPrintInformation.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestProfileRequirements.py
script_name: tests/TestProfileRequirements.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestThemes.py
script_name: tests/TestThemes.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestContainerManager.py
script_name: tests/Settings/TestContainerManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestCuraContainerRegistry.py
script_name: tests/Settings/TestCuraContainerRegistry.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestCuraStackBuilder.py
script_name: tests/Settings/TestCuraStackBuilder.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestDefinitionContainer.py
script_name: tests/Settings/TestDefinitionContainer.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestExtruderStack.py
script_name: tests/Settings/TestExtruderStack.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestGlobalStack.py
script_name: tests/Settings/TestGlobalStack.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestProfiles.py
script_name: tests/Settings/TestProfiles.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestSettingInheritanceManager.py
script_name: tests/Settings/TestSettingInheritanceManager.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestSettingOverrideDecorator.py
script_name: tests/Settings/TestSettingOverrideDecorator.py
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
module_name: Cura
name: pytest in TestSettingVisibilityPresets.py
script_name: tests/Settings/TestSettingVisibilityPresets.py

View file

@ -1,11 +1,6 @@
import os import os
import sys
from pathlib import Path from pathlib import Path
from io import StringIO
from platform import python_version
from jinja2 import Template from jinja2 import Template
from conans import tools from conans import tools
@ -14,7 +9,7 @@ from conan.tools import files
from conan.tools.env import VirtualRunEnv, Environment from conan.tools.env import VirtualRunEnv, Environment
from conan.errors import ConanInvalidConfiguration from conan.errors import ConanInvalidConfiguration
required_conan_version = ">=1.47.0" required_conan_version = ">=1.48.0"
class CuraConan(ConanFile): class CuraConan(ConanFile):
@ -59,18 +54,9 @@ class CuraConan(ConanFile):
"revision": "auto" "revision": "auto"
} }
# TODO: Add unit tests (but they need a different jinja template @property
_pycharm_targets = [{ def _pycharm_targets(self):
"name": "cura", return self.conan_data["pycharm_targets"]
"module_name": "Cura",
"script_name": "cura_app.py",
}, {
"name": "cura_external_engine",
"module_name": "Cura",
"script_name": "cura_app.py",
"parameters": "--external-backend"
}
]
# FIXME: These env vars should be defined in the runenv. # FIXME: These env vars should be defined in the runenv.
_cura_env = None _cura_env = None
@ -114,6 +100,10 @@ class CuraConan(ConanFile):
def _digital_factory_url(self): def _digital_factory_url(self):
return "https://digitalfactory-staging.ultimaker.com" if self._staging else "https://digitalfactory.ultimaker.com" return "https://digitalfactory-staging.ultimaker.com" if self._staging else "https://digitalfactory.ultimaker.com"
@property
def _cura_latest_url(self):
return "https://software.ultimaker.com/latest.json"
@property @property
def requirements_txts(self): def requirements_txts(self):
if self.options.devtools: if self.options.devtools:
@ -175,12 +165,16 @@ class CuraConan(ConanFile):
cura_cloud_api_version = self.options.cloud_api_version, cura_cloud_api_version = self.options.cloud_api_version,
cura_cloud_account_api_root = self._cloud_account_api_root, cura_cloud_account_api_root = self._cloud_account_api_root,
cura_marketplace_root = self._marketplace_root, cura_marketplace_root = self._marketplace_root,
cura_digital_factory_url = self._digital_factory_url)) cura_digital_factory_url = self._digital_factory_url,
cura_latest_url = self._cura_latest_url))
def _generate_pyinstaller_spec(self, location, entrypoint_location, icon_path, entitlements_file): def _generate_pyinstaller_spec(self, location, entrypoint_location, icon_path, entitlements_file):
pyinstaller_metadata = self._um_data()["pyinstaller"] pyinstaller_metadata = self._um_data()["pyinstaller"]
datas = [(str(self._base_dir.joinpath("conan_install_info.json")), ".")] datas = [(str(self._base_dir.joinpath("conan_install_info.json")), ".")]
for data in pyinstaller_metadata["datas"].values(): for data in pyinstaller_metadata["datas"].values():
if not self.options.internal and data.get("internal", False):
continue
if "package" in data: # get the paths from conan package if "package" in data: # get the paths from conan package
if data["package"] == self.name: if data["package"] == self.name:
if self.in_local_cache: if self.in_local_cache:
@ -262,6 +256,9 @@ class CuraConan(ConanFile):
def requirements(self): def requirements(self):
for req in self._um_data()["requirements"]: for req in self._um_data()["requirements"]:
self.requires(req) self.requires(req)
if self.options.internal:
for req in self._um_data()["internal_requirements"]:
self.requires(req)
def layout(self): def layout(self):
self.folders.source = "." self.folders.source = "."
@ -300,11 +297,17 @@ class CuraConan(ConanFile):
self.copy("*.fdm_material", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False) self.copy("*.fdm_material", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False)
self.copy("*.sig", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False) self.copy("*.sig", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False)
if self.options.internal:
self.copy("*.fdm_material", root_package = "fdm_materials_private", src = "@resdirs", dst = "resources/materials", keep_path = False)
self.copy("*.sig", root_package = "fdm_materials_private", src = "@resdirs", dst = "resources/materials", keep_path = False)
self.copy("*", root_package = "cura_private_data", src = self.deps_cpp_info["cura_private_data"].resdirs[0],
dst = self._share_dir.joinpath("cura", "resources"), keep_path = True)
# Copy resources of cura_binary_data # Copy resources of cura_binary_data
self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[0], self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[0],
dst = "venv/share/cura", keep_path = True) dst = self._share_dir.joinpath("cura", "resources"), keep_path = True)
self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[1], self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[1],
dst = "venv/share/uranium", keep_path = True) dst =self._share_dir.joinpath("uranium", "resources"), keep_path = True)
self.copy("*.dll", src = "@bindirs", dst = self._site_packages) self.copy("*.dll", src = "@bindirs", dst = self._site_packages)
self.copy("*.pyd", src = "@libdirs", dst = self._site_packages) self.copy("*.pyd", src = "@libdirs", dst = self._site_packages)
@ -332,6 +335,15 @@ class CuraConan(ConanFile):
self.copy_deps("*.sig", root_package = "fdm_materials", src = self.deps_cpp_info["fdm_materials"].resdirs[0], self.copy_deps("*.sig", root_package = "fdm_materials", src = self.deps_cpp_info["fdm_materials"].resdirs[0],
dst = self._share_dir.joinpath("cura", "resources", "materials"), keep_path = False) dst = self._share_dir.joinpath("cura", "resources", "materials"), keep_path = False)
# Copy internal resources
if self.options.internal:
self.copy_deps("*.fdm_material", root_package = "fdm_materials_private", src = self.deps_cpp_info["fdm_materials_private"].resdirs[0],
dst = self._share_dir.joinpath("cura", "resources", "materials"), keep_path = False)
self.copy_deps("*.sig", root_package = "fdm_materials_private", src = self.deps_cpp_info["fdm_materials_private"].resdirs[0],
dst = self._share_dir.joinpath("cura", "resources", "materials"), keep_path = False)
self.copy_deps("*", root_package = "cura_private_data", src = self.deps_cpp_info["cura_private_data"].resdirs[0],
dst = self._share_dir.joinpath("cura", "resources"), keep_path = True)
# Copy resources of Uranium (keep folder structure) # Copy resources of Uranium (keep folder structure)
self.copy_deps("*", root_package = "uranium", src = self.deps_cpp_info["uranium"].resdirs[0], self.copy_deps("*", root_package = "uranium", src = self.deps_cpp_info["uranium"].resdirs[0],
dst = self._share_dir.joinpath("uranium", "resources"), keep_path = True) dst = self._share_dir.joinpath("uranium", "resources"), keep_path = True)

BIN
cura-logo-dark.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 KiB

After

Width:  |  Height:  |  Size: 1 MiB

Before After
Before After

View file

@ -9,12 +9,20 @@ DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
DEFAULT_CURA_VERSION = "dev" DEFAULT_CURA_VERSION = "dev"
DEFAULT_CURA_BUILD_TYPE = "" DEFAULT_CURA_BUILD_TYPE = ""
DEFAULT_CURA_DEBUG_MODE = False DEFAULT_CURA_DEBUG_MODE = False
DEFAULT_CURA_LATEST_URL = "https://software.ultimaker.com/latest.json"
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
# CuraVersion.py.in template. # CuraVersion.py.in template.
CuraSDKVersion = "8.1.0" CuraSDKVersion = "8.1.0"
try:
from cura.CuraVersion import CuraLatestURL
if CuraLatestURL == "":
CuraLatestURL = DEFAULT_CURA_LATEST_URL
except ImportError:
CuraLatestURL = DEFAULT_CURA_LATEST_URL
try: try:
from cura.CuraVersion import CuraAppName # type: ignore from cura.CuraVersion import CuraAppName # type: ignore
if CuraAppName == "": if CuraAppName == "":

View file

@ -145,6 +145,8 @@ class CuraApplication(QtApplication):
DefinitionChangesContainer = Resources.UserType + 10 DefinitionChangesContainer = Resources.UserType + 10
SettingVisibilityPreset = Resources.UserType + 11 SettingVisibilityPreset = Resources.UserType + 11
IntentInstanceContainer = Resources.UserType + 12 IntentInstanceContainer = Resources.UserType + 12
AbstractMachineStack = Resources.UserType + 13
pyqtEnum(ResourceTypes) pyqtEnum(ResourceTypes)
@ -152,6 +154,7 @@ class CuraApplication(QtApplication):
super().__init__(name = ApplicationMetadata.CuraAppName, super().__init__(name = ApplicationMetadata.CuraAppName,
app_display_name = ApplicationMetadata.CuraAppDisplayName, app_display_name = ApplicationMetadata.CuraAppDisplayName,
version = ApplicationMetadata.CuraVersion if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType, version = ApplicationMetadata.CuraVersion if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType,
latest_url = ApplicationMetadata.CuraLatestURL,
api_version = ApplicationMetadata.CuraSDKVersion, api_version = ApplicationMetadata.CuraSDKVersion,
build_type = ApplicationMetadata.CuraBuildType, build_type = ApplicationMetadata.CuraBuildType,
is_debug_mode = ApplicationMetadata.CuraDebugMode, is_debug_mode = ApplicationMetadata.CuraDebugMode,
@ -422,6 +425,7 @@ class CuraApplication(QtApplication):
Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility") Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility")
Resources.addStorageType(self.ResourceTypes.IntentInstanceContainer, "intent") Resources.addStorageType(self.ResourceTypes.IntentInstanceContainer, "intent")
Resources.addStorageType(self.ResourceTypes.AbstractMachineStack, "abstract_machine_instances")
self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality")
self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes")
@ -432,6 +436,7 @@ class CuraApplication(QtApplication):
self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine") self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine")
self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes")
self._container_registry.addResourceType(self.ResourceTypes.IntentInstanceContainer, "intent") self._container_registry.addResourceType(self.ResourceTypes.IntentInstanceContainer, "intent")
self._container_registry.addResourceType(self.ResourceTypes.AbstractMachineStack, "abstract_machine")
Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware") Resources.addType(self.ResourceTypes.Firmware, "firmware")
@ -480,6 +485,7 @@ class CuraApplication(QtApplication):
("variant", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"), ("variant", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"),
("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"), ("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"),
("machine", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"), ("machine", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"),
("abstract_machine", 1): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"),
("extruder", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer") ("extruder", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer")
} }
) )

View file

@ -45,11 +45,7 @@ class MachineErrorChecker(QObject):
self._start_time = 0. # measure checking time self._start_time = 0. # measure checking time
# This timer delays the starting of error check so we can react less frequently if the user is frequently self._setCheckTimer()
# changing settings.
self._error_check_timer = QTimer(self)
self._error_check_timer.setInterval(100)
self._error_check_timer.setSingleShot(True)
self._keys_to_check = set() # type: Set[str] self._keys_to_check = set() # type: Set[str]
@ -66,6 +62,18 @@ class MachineErrorChecker(QObject):
self._onMachineChanged() self._onMachineChanged()
def _setCheckTimer(self) -> None:
"""A QTimer to regulate error check frequency
This timer delays the starting of error check
so we can react less frequently if the user is frequently
changing settings.
"""
self._error_check_timer = QTimer(self)
self._error_check_timer.setInterval(100)
self._error_check_timer.setSingleShot(True)
def _onMachineChanged(self) -> None: def _onMachineChanged(self) -> None:
if self._global_stack: if self._global_stack:
self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged) self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged)

View file

@ -1,20 +1,19 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from dataclasses import dataclass
@dataclass
class Peripheral: class Peripheral:
"""Data class that represents a peripheral for a printer. """Data class that represents a peripheral for a printer.
Output device plug-ins may specify that the printer has a certain set of Output device plug-ins may specify that the printer has a certain set of
peripherals. This set is then possibly shown in the interface of the monitor peripherals. This set is then possibly shown in the interface of the monitor
stage. stage.
"""
def __init__(self, peripheral_type: str, name: str) -> None: Args:
"""Constructs the peripheral. type (string): A unique ID for the type of peripheral.
name (string): A human-readable name for the peripheral.
:param peripheral_type: A unique ID for the type of peripheral.
:param name: A human-readable name for the peripheral.
""" """
self.type = peripheral_type type: str
self.name = name name: str

View file

@ -0,0 +1,35 @@
from typing import List
from UM.Settings.ContainerStack import ContainerStack
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
from cura.Settings.GlobalStack import GlobalStack
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
from UM.Settings.ContainerRegistry import ContainerRegistry
class AbstractMachine(GlobalStack):
""" Represents a group of machines of the same type. This allows the user to select settings before selecting a printer. """
def __init__(self, container_id: str) -> None:
super().__init__(container_id)
self.setMetaDataEntry("type", "abstract_machine")
def getMachines(self) -> List[ContainerStack]:
from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
printer_type = self.definition.getId()
return [machine for machine in registry.findContainerStacks(type="machine") if machine.definition.id == printer_type and ConnectionType.CloudConnection in machine.configuredConnectionTypes]
## private:
_abstract_machine_mime = MimeType(
name = "application/x-cura-abstract-machine",
comment = "Cura Abstract Machine",
suffixes = ["global.cfg"]
)
MimeTypeDatabase.addMimeType(_abstract_machine_mime)
ContainerRegistry.addContainerTypeByName(AbstractMachine, "abstract_machine", _abstract_machine_mime.name)

View file

@ -108,7 +108,7 @@ class CuraContainerRegistry(ContainerRegistry):
:param container_type: :type{string} Type of the container (machine, quality, ...) :param container_type: :type{string} Type of the container (machine, quality, ...)
:param container_name: :type{string} Name to check :param container_name: :type{string} Name to check
""" """
container_class = ContainerStack if container_type == "machine" else InstanceContainer container_class = ContainerStack if "machine" in container_type else InstanceContainer
return self.findContainersMetadata(container_type = container_class, id = container_name, type = container_type, ignore_case = True) or \ return self.findContainersMetadata(container_type = container_class, id = container_name, type = container_type, ignore_case = True) or \
self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type) self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type)

View file

@ -427,4 +427,4 @@ class _ContainerIndexes:
} }
# Reverse lookup: type -> index # Reverse lookup: type -> index
TypeIndexMap = dict([(v, k) for k, v in IndexTypeMap.items()]) TypeIndexMap = {v: k for k, v in IndexTypeMap.items()}

View file

@ -9,6 +9,7 @@ from UM.Settings.Interfaces import DefinitionContainerInterface
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from .AbstractMachine import AbstractMachine
from .GlobalStack import GlobalStack from .GlobalStack import GlobalStack
from .ExtruderStack import ExtruderStack from .ExtruderStack import ExtruderStack
@ -27,7 +28,7 @@ class CuraStackBuilder:
:return: The new global stack or None if an error occurred. :return: The new global stack or None if an error occurred.
""" """
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication # inline import needed due to circular import
application = CuraApplication.getInstance() application = CuraApplication.getInstance()
registry = application.getContainerRegistry() registry = application.getContainerRegistry()
container_tree = ContainerTree.getInstance() container_tree = ContainerTree.getInstance()
@ -91,7 +92,7 @@ class CuraStackBuilder:
:param extruder_position: The position of the current extruder. :param extruder_position: The position of the current extruder.
""" """
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication # inline import needed due to circular import
application = CuraApplication.getInstance() application = CuraApplication.getInstance()
registry = application.getContainerRegistry() registry = application.getContainerRegistry()
@ -199,13 +200,21 @@ class CuraStackBuilder:
:return: A new Global stack instance with the specified parameters. :return: A new Global stack instance with the specified parameters.
""" """
from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
stack = GlobalStack(new_stack_id) stack = GlobalStack(new_stack_id)
stack.setDefinition(definition) stack.setDefinition(definition)
cls.createUserContainer(new_stack_id, definition, stack, variant_container, material_container, quality_container)
return stack
@classmethod
def createUserContainer(cls, new_stack_id: str, definition: DefinitionContainerInterface,
stack: GlobalStack,
variant_container: "InstanceContainer",
material_container: "InstanceContainer",
quality_container: "InstanceContainer") -> None:
from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
# Create user container # Create user container
user_container = cls.createUserChangesContainer(new_stack_id + "_user", definition.getId(), new_stack_id, user_container = cls.createUserChangesContainer(new_stack_id + "_user", definition.getId(), new_stack_id,
@ -221,8 +230,6 @@ class CuraStackBuilder:
registry.addContainer(user_container) registry.addContainer(user_container)
return stack
@classmethod @classmethod
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str, def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str,
is_global_stack: bool) -> "InstanceContainer": is_global_stack: bool) -> "InstanceContainer":
@ -259,3 +266,49 @@ class CuraStackBuilder:
container_stack.definitionChanges = definition_changes_container container_stack.definitionChanges = definition_changes_container
return definition_changes_container return definition_changes_container
@classmethod
def createAbstractMachine(cls, definition_id: str) -> Optional[AbstractMachine]:
"""Create a new instance of an abstract machine.
:param definition_id: The ID of the machine definition to use.
:return: The new Abstract Machine or None if an error occurred.
"""
abstract_machine_id = definition_id + "_abstract_machine"
from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance()
registry = application.getContainerRegistry()
container_tree = ContainerTree.getInstance()
if registry.findContainerStacks(type = "abstract_machine", id = abstract_machine_id):
# This abstract machine already exists
return None
match registry.findDefinitionContainers(type = "machine", id = definition_id):
case []:
# It should not be possible for the definition to be missing since an abstract machine will only
# be created as a result of a machine with definition_id being created.
Logger.error(f"Definition {definition_id} was not found!")
return None
case [machine_definition, *_definitions]:
machine_node = container_tree.machines[machine_definition.getId()]
name = machine_definition.getName()
stack = AbstractMachine(abstract_machine_id)
stack.setDefinition(machine_definition)
cls.createUserContainer(
name,
machine_definition,
stack,
application.empty_variant_container,
application.empty_material_container,
machine_node.preferredGlobalQuality().container,
)
stack.setName(name)
registry.addContainer(stack)
return stack

View file

@ -10,10 +10,13 @@ if TYPE_CHECKING:
# #
# This class manages a all registered upon-exit checks that need to be perform when the application tries to exit. # This class manages all registered upon-exit checks
# For example, to show a confirmation dialog when there is USB printing in progress, etc. All callbacks will be called # that need to be performed when the application tries to exit.
# in the order of when they got registered. If all callbacks "passes", that is, for example, if the user clicks "yes" # For example, show a confirmation dialog when there is USB printing in progress.
# on the exit confirmation dialog or nothing that's blocking the exit, then the application will quit after that. # All callbacks will be called in the order of when they were registered.
# If all callbacks "pass", for example:
# if the user clicks "yes" on the exit confirmation dialog
# and nothing else is blocking the exit, then the application will quit.
# #
class OnExitCallbackManager: class OnExitCallbackManager:
@ -35,10 +38,12 @@ class OnExitCallbackManager:
def getIsAllChecksPassed(self) -> bool: def getIsAllChecksPassed(self) -> bool:
return self._is_all_checks_passed return self._is_all_checks_passed
# Trigger the next callback if available. If not, it means that all callbacks have "passed", which means we should # Trigger the next callback if there is one.
# not block the application to quit, and it will call the application to actually quit. # If not, all callbacks have "passed",
# which means we should not prevent the application from quitting,
# and we call the application to actually quit.
def triggerNextCallback(self) -> None: def triggerNextCallback(self) -> None:
# Get the next callback and schedule that if # Get the next callback and schedule it
this_callback = None this_callback = None
if self._current_callback_idx < len(self._on_exit_callback_list): if self._current_callback_idx < len(self._on_exit_callback_list):
this_callback = self._on_exit_callback_list[self._current_callback_idx] this_callback = self._on_exit_callback_list[self._current_callback_idx]
@ -55,10 +60,11 @@ class OnExitCallbackManager:
# Tell the application to exit # Tell the application to exit
self._application.callLater(self._application.closeApplication) self._application.callLater(self._application.closeApplication)
# This is the callback function which an on-exit callback should call when it finishes, it should provide the # Callback function which an on-exit callback calls when it finishes.
# "should_proceed" flag indicating whether this check has "passed", or in other words, whether quitting the # It provides a "should_proceed" flag indicating whether the check has "passed",
# application should be blocked. If the last on-exit callback doesn't block the quitting, it will call the next # or whether quitting the application should be blocked.
# registered on-exit callback if available. # If the last on-exit callback doesn't block quitting, it will call the next
# registered on-exit callback if one is available.
def onCurrentCallbackFinished(self, should_proceed: bool = True) -> None: def onCurrentCallbackFinished(self, should_proceed: bool = True) -> None:
if not should_proceed: if not should_proceed:
Logger.log("d", "on-app-exit callback finished and we should not proceed.") Logger.log("d", "on-app-exit callback finished and we should not proceed.")

81
docs/Report.md Normal file
View file

@ -0,0 +1,81 @@
# Reporting Issues
Please attach the following information in case <br>
you want to report crashing or similar issues.
<br>
## DxDiag
### ![Badge Windows]
The log as produced by **dxdiag**.
<kbd>start</kbd>  »  <kbd>run</kbd>  »  <kbd>dxdiag</kbd>  »  <kbd>save output</kbd>
<br>
<br>
## Cura GUI Log
If the Cura user interface still starts, you can also <br>
reach these directories from the application menu:
<kbd>Help</kbd>  »  <kbd>Show settings folder</kbd>
<br>
### ![Badge Windows]
```
%APPDATA%\cura\< >\cura.log
```
or
```
C:\Users\<your username>\AppData\Roaming\cura\< >\cura.log
```
<br>
### ![Badge Linux]
```
~/.local/share/cura/< >/cura.log
```
<br>
### ![Badge MacOS]
```
~/Library/Application Support/cura/< >/cura.log
```
<br>
<br>
## Alternative
An alternative is to install the **[ExtensiveSupportLogging]** <br>
plugin this creates a zip folder of the relevant log files.
If you're experiencing performance issues, we might ask <br>
you to connect the CPU profiler in this plugin and attach <br>
the collected data to your support ticket.
<br>
<!----------------------------------------------------------------------------->
[ExtensiveSupportLogging]: https://marketplace.ultimaker.com/app/cura/plugins/UltimakerPackages/ExtensiveSupportLogging
<!---------------------------------[ Badges ]---------------------------------->
[Badge Windows]: https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logoColor=white&logo=Windows
[Badge Linux]: https://img.shields.io/badge/Linux-00A95C?style=for-the-badge&logoColor=white&logo=Linux
[Badge MacOS]: https://img.shields.io/badge/MacOS-403C3D?style=for-the-badge&logoColor=white&logo=MacOS

View file

@ -2,32 +2,77 @@ Setting Properties
==== ====
Each setting in Cura has a number of properties. It's not just a key and a value. This page lists the properties that a setting can define. Each setting in Cura has a number of properties. It's not just a key and a value. This page lists the properties that a setting can define.
* `key` (string): The identifier by which the setting is referenced. This is not a human-readable name, but just a reference string, such as `layer_height_0`. Typically these are named with the most significant category first, in order to sort them better, such as `material_print_temperature`. This is not actually a real property but just an identifier; it can't be changed. * `key` (string): __The identifier by which the setting is referenced.__
* `value` (optional): The current value of the setting. This can be a function, an arbitrary Python expression that depends on the values of other settings. If it's not present, the `default_value` is used. * This is not a human-readable name, but just a reference string, such as `layer_height_0`.
* `default_value`: A default value for the setting if `value` is undefined. This property is required however. It can't be a Python expression, but it can be any JSON type. This is made separate so that CuraEngine can read it out as well for its debugging mode via the command line, without needing a complete Python interpreter. * This is not actually a real property but just an identifier; it can't be changed.
* `label` (string): The human-readable name for the setting. This label is translated. * Typically these are named with the most significant category first, in order to sort them better, such as `material_print_temperature`.
* `description` (string): A longer description of what the setting does when you change it. This description is translated as well. * `value` (optional): __The current value of the setting.__
* `type` (string): The type of value that this setting contains. Allowed types are: `bool`, `str`, `float`, `int`, `enum`, `category`, `[int]`, `vec3`, `polygon` and `polygons`. * This can be a function (an arbitrary Python expression) that depends on the values of other settings.
* `unit` (optional string): A unit that is displayed at the right-hand side of the text field where the user enters the setting value. * If it's not present, the `default_value` is used.
* `resolve` (optional string): A Python expression that resolves disagreements for global settings if multiple per-extruder profiles define different values for a setting. Typically this takes the values for the setting from all stacks and computes one final value for it that will be used for the global setting. For instance, the `resolve` function for the build plate temperature is `max(extruderValues('material_bed_temperature')`, meaning that it will use the hottest bed temperature of all materials of the extruders in use. * `default_value`: __A default value for the setting if `value` is undefined.__
* `limit_to_extruder` (optional): A Python expression that indicates which extruder a setting will be obtained from. This is used for settings that may be extruder-specific but the extruder is not necessarily the current extruder. For instance, support settings need to be evaluated for the support extruder. Infill settings need to be evaluated for the infill extruder if the infill extruder is changed. * This property is required.
* `enabled` (optional string or boolean): Whether the setting can currently be made visible for the user. This can be a simple true/false, or a Python expression that depends on other settings. Typically used for settings that don't apply when another setting is disabled, such as to hide the support settings if support is disabled. * It can't be a Python expression, but it can be any JSON type.
* `minimum_value` (optional): The lowest acceptable value for this setting. If it's any lower, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a layer height of 0mm. This property only applies to numerical settings. * This is made separate so that CuraEngine can read it out for its debugging mode via the command line, without needing a complete Python interpreter.
* `maximum_value` (optional): The highest acceptable value for this setting. If it's any higher, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a support overhang angle of more than 90 degrees. This property only applies to numerical settings. * `label` (string): __The human-readable name for the setting.__
* `minimum_value_warning` (optional): The threshold under which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a low setting value. This property only applies to numerical settings. * This label is translated.
* `maximum_value_warning` (optional): The threshold above which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a high setting value. This property only applies to numerical settings. * `description` (string): __A longer description of what the setting does when you change it.__
* `settable_globally` (optional boolean): Whether the setting can be changed globally. For some mesh-type settings such as `support_mesh` this doesn't make sense, so those can't be changed globally. They are not displayed in the main settings list then. * This description is translated.
* `settable_per_meshgroup` (optional boolean): Whether a setting can be changed per group of meshes. Currently unused in Cura. * `type` (string): __The type of value that this setting contains.__
* `settable_per_extruder` (optional boolean): Whether a setting can be changed per extruder. Some settings, like the build plate temperature, can't be adjusted separately for each extruder. An icon is shown in the interface to indicate this. If the user changes these settings they are stored in the global stack. * Allowed types are: `bool`, `str`, `float`, `int`, `enum`, `category`, `[int]`, `vec3`, `polygon` and `polygons`.
* `settable_per_mesh` (optional boolean): Whether a setting can be changed per mesh. The settings that can be changed per mesh are shown in the list of available settings in the per-object settings tool. * `unit` (optional string): __A unit that is displayed at the right-hand side of the text field where the user enters the setting value.__
* `children` (optional list): A list of child settings. These are displayed with an indentation. If all child settings are overridden by the user, the parent setting gets greyed out to indicate that the parent setting has no effect any more. This is not strictly always the case though, because that would depend on the inheritance functions in the `value`. * `resolve` (optional string): __A Python expression that resolves disagreements for global settings if multiple per-extruder profiles define different values for a setting.__
* `icon` (optional string): A path to an icon to be displayed. Only applies to setting categories. * Typically this takes the values for the setting from all stacks and computes one final value for it that will be used for the global setting. For instance, the `resolve` function for the build plate temperature is `max(extruderValues('material_bed_temperature')`, meaning that it will use the hottest bed temperature of all materials of the extruders in use.
* `allow_empty` (optional bool): Whether the setting is allowed to be empty. If it's not, this will be treated as a setting error and Cura will not allow the user to slice. Only applies to string-type settings. * `limit_to_extruder` (optional): __A Python expression that indicates which extruder a setting will be obtained from.__
* `warning_description` (optional string): A warning message to display when the setting has a warning value. This is currently unused by Cura. * This is used for settings that may be extruder-specific but the extruder is not necessarily the current extruder. For instance, support settings need to be evaluated for the support extruder. Infill settings need to be evaluated for the infill extruder if the infill extruder is changed.
* `error_description` (optional string): An error message to display when the setting has an error value. This is currently unused by Cura. * `enabled` (optional string or boolean): __Whether the setting can currently be made visible for the user.__
* `options` (dictionary): A list of values that the user can choose from. The keys of this dictionary are keys that CuraEngine identifies the option with. The values are human-readable strings and will be translated. Only applies to (and only required for) enum-type settings. * This can be a simple true/false, or a Python expression that depends on other settings.
* `comments` (optional string): Comments to other programmers about the setting. This is not used by Cura. * Typically used for settings that don't apply when another setting is disabled, such as to hide the support settings if support is disabled.
* `is_uuid` (optional boolean): Whether or not this setting indicates a UUID-4. If it is, the setting will indicate an error if it's not in the correct format. Only applies to string-type settings. * `minimum_value` (optional): __The lowest acceptable value for this setting.__
* `regex_blacklist_pattern` (optional string): A regular expression, where if the setting value matches with this regular expression, it gets an error state. Only applies to string-type settings. * If it's any lower, Cura will not allow the user to slice.
* `error_value` (optional): If the setting value is equal to this value, it will show a setting error. This is used to display errors for non-numerical settings such as checkboxes. * This property only applies to numerical settings.
* `warning_value` (optional): If the setting value is equal to this value, it will show a setting warning. This is used to display warnings for non-numerical settings such as checkboxes. * By convention this is used to prevent setting values that are technically or physically impossible, such as a layer height of 0mm.
* `maximum_value` (optional): __The highest acceptable value for this setting.__
* If it's any higher, Cura will not allow the user to slice.
* This property only applies to numerical settings.
* By convention this is used to prevent setting values that are technically or physically impossible, such as a support overhang angle of more than 90 degrees.
* `minimum_value_warning` (optional): __The threshold under which a warning is displayed to the user.__
* This property only applies to numerical settings.
* By convention this is used to indicate that it will probably not print very nicely with such a low setting value.
* `maximum_value_warning` (optional): __The threshold above which a warning is displayed to the user.__
* This property only applies to numerical settings.
* By convention this is used to indicate that it will probably not print very nicely with such a high setting value.
* `settable_globally` (optional boolean): __Whether the setting can be changed globally.__
* For some mesh-type settings such as `support_mesh` this doesn't make sense, so those can't be changed globally. They are not displayed in the main settings list then.
* `settable_per_meshgroup` (optional boolean): __Whether a setting can be changed per group of meshes.__
* *This is currently unused by Cura.*
* `settable_per_extruder` (optional boolean): __Whether a setting can be changed per extruder.__
* Some settings, like the build plate temperature, can't be adjusted separately for each extruder. An icon is shown in the interface to indicate this.
* If the user changes these settings they are stored in the global stack.
* `settable_per_mesh` (optional boolean): __Whether a setting can be changed per mesh.__
* The settings that can be changed per mesh are shown in the list of available settings in the per-object settings tool.
* `children` (optional list): __A list of child settings.__
* These are displayed with an indentation. If all child settings are overridden by the user, the parent setting gets greyed out to indicate that the parent setting has no effect any more. This is not strictly always the case though, because that would depend on the inheritance functions in the `value`.
* `icon` (optional string): __A path to an icon to be displayed.__
* Only applies to setting categories.
* `allow_empty` (optional bool): __Whether the setting is allowed to be empty.__
* If it's not, this will be treated as a setting error and Cura will not allow the user to slice.
* Only applies to string-type settings.
* `warning_description` (optional string): __A warning message to display when the setting has a warning value.__
* *This is currently unused by Cura.*
* `error_description` (optional string): __An error message to display when the setting has an error value.__
* *This is currently unused by Cura.*
* `options` (dictionary): __A list of values that the user can choose from.__
* The keys of this dictionary are keys that CuraEngine identifies the option with.
* The values are human-readable strings and will be translated.
* Only applies to (and only required for) enum-type settings.
* `comments` (optional string): __Comments to other programmers about the setting.__
* *This is currently unused by Cura.*
* `is_uuid` (optional boolean): __Whether or not this setting indicates a UUID-4.__
* If it is, the setting will indicate an error if it's not in the correct format.
* Only applies to string-type settings.
* `regex_blacklist_pattern` (optional string): __A regular expression, where if the setting value matches with this regular expression, it gets an error state.__
* Only applies to string-type settings.
* `error_value` (optional): __If the setting value is equal to this value, it will show a setting error.__
* This is used to display errors for non-numerical settings such as checkboxes.
* `warning_value` (optional): __If the setting value is equal to this value, it will show a setting warning.__
* This is used to display warnings for non-numerical settings such as checkboxes.

View file

@ -400,11 +400,13 @@ class CloudOutputDeviceManager:
# We do not use use MachineManager.addMachine here because we need to set the cluster ID before activating it. # We do not use use MachineManager.addMachine here because we need to set the cluster ID before activating it.
new_machine = CuraStackBuilder.createMachine(device.name, device.printerType, show_warning_message=False) new_machine = CuraStackBuilder.createMachine(device.name, device.printerType, show_warning_message=False)
if not new_machine: if not new_machine:
Logger.log("e", "Failed creating a new machine") Logger.error(f"Failed creating a new machine for {device.name}")
return False return False
self._setOutputDeviceMetadata(device, new_machine) self._setOutputDeviceMetadata(device, new_machine)
_abstract_machine = CuraStackBuilder.createAbstractMachine(device.printerType)
if activate: if activate:
CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId()) CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId())

View file

@ -1225,20 +1225,7 @@
"default_value": 0.3, "default_value": 0.3,
"value": "min_wall_line_width", "value": "min_wall_line_width",
"type": "float", "type": "float",
"settable_per_mesh": true, "settable_per_mesh": true
"children":
{
"wall_split_middle_threshold": {
"label": "Split Middle Line Threshold",
"description": "The smallest line width, as a factor of the normal line width, above which the middle line (if there is one) will be split into two. Reduce this setting to use more, thinner lines. Increase to use fewer, wider lines. Note that this applies -as if- the entire shape should be filled with wall, so the middle here refers to the middle of the object between two outer edges of the shape, even if there actually is fill or (other) skin in the print instead of wall.",
"type": "float",
"unit": "%",
"default_value": 50,
"value": "max(1, min(99, 100 * (2 * min_even_wall_line_width - wall_line_width_0) / wall_line_width_0))",
"minimum_value": "1",
"maximum_value": "99"
}
}
}, },
"min_odd_wall_line_width": "min_odd_wall_line_width":
{ {
@ -1250,20 +1237,7 @@
"default_value": 0.3, "default_value": 0.3,
"value": "min_wall_line_width", "value": "min_wall_line_width",
"type": "float", "type": "float",
"settable_per_mesh": true, "settable_per_mesh": true
"children":
{
"wall_add_middle_threshold": {
"label": "Add Middle Line Threshold",
"description": "The smallest line width, as a factor of the normal line width, above which a middle line (if there wasn't one already) will be added. Reduce this setting to use more, thinner lines. Increase to use fewer, wider lines. Note that this applies -as if- the entire shape should be filled with wall, so the middle here refers to the middle of the object between two outer edges of the shape, even if there actually is fill or (other) skin in the print instead of wall.",
"type": "float",
"unit": "%",
"default_value": 75,
"value": "max(1, min(99, 100 * min_odd_wall_line_width / wall_line_width_x))",
"minimum_value": "1",
"maximum_value": "99"
}
}
} }
} }
}, },
@ -8049,7 +8023,7 @@
"label": "Remove Raft Inside Corners", "label": "Remove Raft Inside Corners",
"description": "Remove inside corners from the raft, causing the raft to become convex.", "description": "Remove inside corners from the raft, causing the raft to become convex.",
"type": "bool", "type": "bool",
"default_value": false, "default_value": true,
"resolve": "any(extruderValues('raft_remove_inside_corners'))", "resolve": "any(extruderValues('raft_remove_inside_corners'))",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,

View file

@ -51,7 +51,17 @@ Item
{ {
target: infillSlider target: infillSlider
property: "value" property: "value"
value: parseInt(infillDensity.properties.value) value: {
// The infill slider has a max value of 100. When it is given a value > 100 onValueChanged updates the setting to be 100.
// When changing to an intent with infillDensity > 100, it would always be clamped to 100.
// This will force the slider to ignore the first onValueChanged for values > 100 so higher values can be set.
var density = parseInt(infillDensity.properties.value)
if (density > 100) {
infillSlider.ignoreValueChange = true
}
return density
}
} }
// Here are the elements that are shown in the left column // Here are the elements that are shown in the left column
@ -84,6 +94,8 @@ Item
{ {
id: infillSlider id: infillSlider
property var ignoreValueChange: false
width: parent.width width: parent.width
height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider
@ -157,7 +169,13 @@ Item
target: infillSlider target: infillSlider
function onValueChanged() function onValueChanged()
{ {
// Don't round the value if it's already the same if (infillSlider.ignoreValueChange)
{
infillSlider.ignoreValueChange = false
return
}
// Don't update if the setting value, if the slider has the same value
if (parseInt(infillDensity.properties.value) == infillSlider.value) if (parseInt(infillDensity.properties.value) == infillSlider.value)
{ {
return return