mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-04-29 23:34:32 +08:00
Merge branch 'main' into main
This commit is contained in:
commit
d38a0bb628
75
.github/workflows/conan-recipe-version.yml
vendored
75
.github/workflows/conan-recipe-version.yml
vendored
@ -53,9 +53,18 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Checkout repo PR
|
||||
uses: actions/checkout@v3
|
||||
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- name: Setup Python and pip
|
||||
uses: actions/setup-python@v4
|
||||
@ -106,13 +115,15 @@ jobs:
|
||||
else:
|
||||
channel = repo.active_branch.name.split("_")[0].replace("-", "_").lower()
|
||||
|
||||
if event_name == "pull_request":
|
||||
if "pull_request" in event_name:
|
||||
channel = f"pr_{issue_number}"
|
||||
|
||||
# %% Get the actual version
|
||||
latest_branch_version = tools.Version("0.0.0")
|
||||
latest_branch_tag = None
|
||||
for tag in repo.git.tag(merged = True).splitlines():
|
||||
if str(tag).startswith("firmware") or str(tag).startswith("master"):
|
||||
continue # Quick-fix for the versioning scheme name of the embedded team in fdm_materials(_private) repo
|
||||
try:
|
||||
version = tools.Version(tag)
|
||||
except ConanException:
|
||||
@ -121,36 +132,46 @@ jobs:
|
||||
latest_branch_version = version
|
||||
latest_branch_tag = repo.tag(tag)
|
||||
|
||||
# %% Get the actual version
|
||||
no_commits = 0
|
||||
for commit in repo.iter_commits("HEAD"):
|
||||
if commit == latest_branch_tag.commit:
|
||||
break
|
||||
no_commits += 1
|
||||
|
||||
if no_commits == 0:
|
||||
# This is a release
|
||||
actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{latest_branch_version.patch}"
|
||||
if channel == "stable":
|
||||
user = "_"
|
||||
channel = "_"
|
||||
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":
|
||||
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}"
|
||||
if latest_branch_tag:
|
||||
# %% Get the actual version
|
||||
no_commits = 0
|
||||
for commit in repo.iter_commits("HEAD"):
|
||||
if commit == latest_branch_tag.commit:
|
||||
break
|
||||
no_commits += 1
|
||||
|
||||
if no_commits == 0:
|
||||
# This is a release
|
||||
actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{latest_branch_version.patch}"
|
||||
if channel == "stable":
|
||||
user = "_"
|
||||
channel = "_"
|
||||
else:
|
||||
if channel in ("stable", "_", ""):
|
||||
channel_metadata = f"{no_commits}"
|
||||
else:
|
||||
latest_branch_version_prerelease = latest_branch_version.prerelease
|
||||
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 = f"{latest_branch_version.prerelease}.1"
|
||||
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}"
|
||||
channel_metadata = f"{channel}_{no_commits}"
|
||||
# FIXME: for when we create a new release branch
|
||||
else:
|
||||
if channel in ("stable", "_", ""):
|
||||
channel_metadata = f"{no_commits}"
|
||||
else:
|
||||
channel_metadata = f"{channel}_{no_commits}"
|
||||
if latest_branch_version.prerelease == "":
|
||||
bump_up_minor = int(latest_branch_version.minor) + 1
|
||||
actual_version = f"{latest_branch_version.major}.{bump_up_minor}.{latest_branch_version.patch}-alpha+{buildmetadata}{channel_metadata}"
|
||||
if is_release_branch:
|
||||
bump_up_patch = int(latest_branch_version.patch) + 1
|
||||
actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{bump_up_patch}-alpha+{buildmetadata}{channel_metadata}"
|
||||
else:
|
||||
bump_up_minor = int(latest_branch_version.minor) + 1
|
||||
reset_patch = 0
|
||||
actual_version = f"{latest_branch_version.major}.{bump_up_minor}.{reset_patch}-alpha+{buildmetadata}{channel_metadata}"
|
||||
else:
|
||||
actual_version = f"{latest_branch_version.major}.{latest_branch_version.minor}.{latest_branch_version.patch}-{latest_branch_version.prerelease.lower()}+{buildmetadata}{channel_metadata}"
|
||||
else:
|
||||
# FIXME: for external PR's
|
||||
actual_version = f"5.2.0-alpha+{buildmetadata}pr_{issue_number}"
|
||||
|
||||
# %% print to output
|
||||
cmd_name = ["echo", f"::set-output name=name::{project_name}"]
|
||||
|
@ -1,2 +1,2 @@
|
||||
conan!=1.51.0,!=1.51.1,!=1.51.2,!=1.51.3
|
||||
conan!=1.51.0,!=1.51.1,!=1.51.2,!=1.51.3,!=1.52.0
|
||||
sip
|
||||
|
13
.github/workflows/unit-test.yml
vendored
13
.github/workflows/unit-test.yml
vendored
@ -103,7 +103,18 @@ jobs:
|
||||
|
||||
- name: Install Linux system requirements
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev -y
|
||||
run: |
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y
|
||||
|
||||
- name: Install GCC-12 on ubuntu-22.04
|
||||
if: ${{ startsWith(inputs.runs_on, 'ubuntu-22.04') }}
|
||||
run: |
|
||||
sudo apt install g++-12 gcc-12 -y
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12
|
||||
|
||||
- name: Get Conan configuration
|
||||
run: conan config install https://github.com/Ultimaker/conan-config.git
|
||||
|
@ -1,6 +1,8 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# NOTE: This is only being used for translation scripts.
|
||||
|
||||
# For MSVC flags, will be ignored on non-Windows OS's and this project in general. Only needed for cura-build-environment.
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
project(cura)
|
||||
@ -13,47 +15,8 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
set(URANIUM_DIR "${CMAKE_SOURCE_DIR}/../Uranium" CACHE PATH "The location of the Uranium repository")
|
||||
set(URANIUM_SCRIPTS_DIR "${URANIUM_DIR}/scripts" CACHE PATH "The location of the scripts directory of the Uranium repository")
|
||||
|
||||
option(CURA_DEBUGMODE "Enable debug dialog and other debug features" OFF)
|
||||
if(CURA_DEBUGMODE)
|
||||
set(_cura_debugmode "ON")
|
||||
endif()
|
||||
|
||||
option(GENERATE_TRANSLATIONS "Should the translations be generated?" ON)
|
||||
|
||||
set(CURA_APP_NAME "cura" CACHE STRING "Short name of Cura, used for configuration folder")
|
||||
set(CURA_APP_DISPLAY_NAME "Ultimaker Cura" CACHE STRING "Display name of Cura")
|
||||
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
|
||||
set(CURA_CLOUD_API_ROOT "" CACHE STRING "Alternative Cura cloud API root")
|
||||
set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version")
|
||||
set(CURA_CLOUD_ACCOUNT_API_ROOT "" CACHE STRING "Alternative Cura cloud account API version")
|
||||
set(CURA_MARKETPLACE_ROOT "" CACHE STRING "Alternative Marketplace location")
|
||||
set(CURA_DIGITAL_FACTORY_URL "" CACHE STRING "Alternative Digital Factory location")
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/com.ultimaker.cura.desktop.in ${CMAKE_BINARY_DIR}/com.ultimaker.cura.desktop @ONLY)
|
||||
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
||||
if(NOT DEFINED Python_VERSION)
|
||||
set(Python_VERSION
|
||||
3.10
|
||||
CACHE STRING "Python Version" FORCE)
|
||||
message(STATUS "Setting Python version to ${Python_VERSION}. Set Python_VERSION if you want to compile against an other version.")
|
||||
endif()
|
||||
if(APPLE)
|
||||
set(Python_FIND_FRAMEWORK NEVER)
|
||||
endif()
|
||||
find_package(Python ${Python_VERSION} EXACT REQUIRED COMPONENTS Interpreter)
|
||||
message(STATUS "Linking and building ${project_name} against Python ${Python_VERSION}")
|
||||
if(NOT DEFINED Python_SITELIB_LOCAL)
|
||||
set(Python_SITELIB_LOCAL
|
||||
"${Python_SITELIB}"
|
||||
CACHE PATH "Local alternative site-package location to install Cura" FORCE)
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
include(CuraTests)
|
||||
|
||||
if(NOT ${URANIUM_DIR} STREQUAL "")
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${URANIUM_DIR}/cmake")
|
||||
endif()
|
||||
@ -66,24 +29,4 @@ if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
|
||||
if(${GENERATE_TRANSLATIONS})
|
||||
CREATE_TRANSLATION_TARGETS()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
|
||||
|
||||
include(CuraPluginInstall)
|
||||
|
||||
install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
install(DIRECTORY cura DESTINATION "${Python_SITELIB_LOCAL}")
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION "${Python_SITELIB_LOCAL}/cura/")
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/com.ultimaker.cura.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/resources/images/cura-icon.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/)
|
||||
install(FILES com.ultimaker.cura.appdata.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
|
||||
install(FILES cura.sharedmimeinfo
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/
|
||||
RENAME cura.xml )
|
||||
endif()
|
||||
endif()
|
@ -1,55 +0,0 @@
|
||||
mode: ContinuousDelivery
|
||||
next-version: 5.1
|
||||
branches:
|
||||
main:
|
||||
regex: ^main$
|
||||
mode: ContinuousDelivery
|
||||
tag: alpha
|
||||
increment: None
|
||||
prevent-increment-of-merged-branch-version: true
|
||||
track-merge-target: false
|
||||
source-branches: [ ]
|
||||
tracks-release-branches: false
|
||||
is-release-branch: false
|
||||
is-mainline: true
|
||||
pre-release-weight: 55000
|
||||
develop:
|
||||
regex: ^CURA-.*$
|
||||
mode: ContinuousDelivery
|
||||
tag: alpha
|
||||
increment: None
|
||||
prevent-increment-of-merged-branch-version: false
|
||||
track-merge-target: true
|
||||
source-branches: [ 'main' ]
|
||||
tracks-release-branches: true
|
||||
is-release-branch: false
|
||||
is-mainline: false
|
||||
pre-release-weight: 0
|
||||
release:
|
||||
regex: ^[\d]+\.[\d]+$
|
||||
mode: ContinuousDelivery
|
||||
tag: beta
|
||||
increment: None
|
||||
prevent-increment-of-merged-branch-version: true
|
||||
track-merge-target: false
|
||||
source-branches: [ 'main' ]
|
||||
tracks-release-branches: false
|
||||
is-release-branch: true
|
||||
is-mainline: false
|
||||
pre-release-weight: 30000
|
||||
pull-request-main:
|
||||
regex: ^(pull|pull\-requests|pr)[/-]
|
||||
mode: ContinuousDelivery
|
||||
tag: alpha+
|
||||
increment: Inherit
|
||||
prevent-increment-of-merged-branch-version: true
|
||||
tag-number-pattern: '[/-](?<number>\d+)[-/]'
|
||||
track-merge-target: true
|
||||
source-branches: [ 'main' ]
|
||||
tracks-release-branches: false
|
||||
is-release-branch: false
|
||||
is-mainline: false
|
||||
pre-release-weight: 30000
|
||||
ignore:
|
||||
sha: [ ]
|
||||
merge-message-formats: { }
|
74
Jenkinsfile
vendored
74
Jenkinsfile
vendored
@ -1,74 +0,0 @@
|
||||
parallel_nodes(['linux && cura', 'windows && cura'])
|
||||
{
|
||||
timeout(time: 2, unit: "HOURS")
|
||||
{
|
||||
|
||||
// Prepare building
|
||||
stage('Prepare')
|
||||
{
|
||||
// Ensure we start with a clean build directory.
|
||||
step([$class: 'WsCleanup'])
|
||||
|
||||
// Checkout whatever sources are linked to this pipeline.
|
||||
checkout scm
|
||||
}
|
||||
|
||||
// If any error occurs during building, we want to catch it and continue with the "finale" stage.
|
||||
catchError
|
||||
{
|
||||
// Building and testing should happen in a subdirectory.
|
||||
dir('build')
|
||||
{
|
||||
// Perform the "build". Since Uranium is Python code, this basically only ensures CMake is setup.
|
||||
stage('Build')
|
||||
{
|
||||
def branch = env.BRANCH_NAME
|
||||
if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}"))
|
||||
{
|
||||
branch = "master"
|
||||
}
|
||||
|
||||
// Ensure CMake is setup. Note that since this is Python code we do not really "build" it.
|
||||
def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}")
|
||||
cmake("..", "-DCMAKE_PREFIX_PATH=\"${env.CURA_ENVIRONMENT_PATH}/${branch}\" -DCMAKE_BUILD_TYPE=Release -DURANIUM_DIR=\"${uranium_dir}\"")
|
||||
}
|
||||
|
||||
// Try and run the unit tests. If this stage fails, we consider the build to be "unstable".
|
||||
stage('Unit Test')
|
||||
{
|
||||
if (isUnix())
|
||||
{
|
||||
// For Linux
|
||||
try {
|
||||
sh 'make CTEST_OUTPUT_ON_FAILURE=TRUE test'
|
||||
} catch(e)
|
||||
{
|
||||
currentBuild.result = "UNSTABLE"
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Windows
|
||||
try
|
||||
{
|
||||
// This also does code style checks.
|
||||
bat 'ctest -V'
|
||||
} catch(e)
|
||||
{
|
||||
currentBuild.result = "UNSTABLE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform any post-build actions like notification and publishing of unit tests.
|
||||
stage('Finalize')
|
||||
{
|
||||
// Publish the test results to Jenkins.
|
||||
junit allowEmptyResults: true, testResults: 'build/junit*.xml'
|
||||
|
||||
notify_build_result(env.CURA_EMAIL_RECIPIENTS, '#cura-dev', ['master', '2.'])
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# CuraPluginInstall.cmake is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
#
|
||||
# This module detects all plugins that need to be installed and adds them using the CMake install() command.
|
||||
# It detects all plugin folder in the path "plugins/*" where there's a "plugin.json" in it.
|
||||
#
|
||||
# Plugins can be configured to NOT BE INSTALLED via the variable "CURA_NO_INSTALL_PLUGINS" as a list of string in the
|
||||
# form of "a;b;c" or "a,b,c". By default all plugins will be installed.
|
||||
#
|
||||
|
||||
option(PRINT_PLUGIN_LIST "Should the list of plugins that are installed be printed?" ON)
|
||||
|
||||
# Options or configuration variables
|
||||
set(CURA_NO_INSTALL_PLUGINS "" CACHE STRING "A list of plugins that should not be installed, separated with ';' or ','.")
|
||||
|
||||
file(GLOB_RECURSE _plugin_json_list ${CMAKE_SOURCE_DIR}/plugins/*/plugin.json)
|
||||
list(LENGTH _plugin_json_list _plugin_json_list_len)
|
||||
|
||||
# Sort the lists alphabetically so we can handle cases like this:
|
||||
# - plugins/my_plugin/plugin.json
|
||||
# - plugins/my_plugin/my_module/plugin.json
|
||||
# In this case, only "plugins/my_plugin" should be added via install().
|
||||
set(_no_install_plugin_list ${CURA_NO_INSTALL_PLUGINS})
|
||||
# Sanitize the string so the comparison will be case-insensitive.
|
||||
string(STRIP "${_no_install_plugin_list}" _no_install_plugin_list)
|
||||
string(TOLOWER "${_no_install_plugin_list}" _no_install_plugin_list)
|
||||
|
||||
# WORKAROUND counterpart of what's in cura-build.
|
||||
string(REPLACE "," ";" _no_install_plugin_list "${_no_install_plugin_list}")
|
||||
|
||||
list(LENGTH _no_install_plugin_list _no_install_plugin_list_len)
|
||||
|
||||
if(_no_install_plugin_list_len GREATER 0)
|
||||
list(SORT _no_install_plugin_list)
|
||||
endif()
|
||||
if(_plugin_json_list_len GREATER 0)
|
||||
list(SORT _plugin_json_list)
|
||||
endif()
|
||||
|
||||
# Check all plugin directories and add them via install() if needed.
|
||||
set(_install_plugin_list "")
|
||||
foreach(_plugin_json_path ${_plugin_json_list})
|
||||
get_filename_component(_plugin_dir ${_plugin_json_path} DIRECTORY)
|
||||
file(RELATIVE_PATH _rel_plugin_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_plugin_dir})
|
||||
get_filename_component(_plugin_dir_name ${_plugin_dir} NAME)
|
||||
|
||||
# Make plugin name comparison case-insensitive
|
||||
string(TOLOWER "${_plugin_dir_name}" _plugin_dir_name_lowercase)
|
||||
|
||||
# Check if this plugin needs to be skipped for installation
|
||||
set(_add_plugin ON) # Indicates if this plugin should be added to the build or not.
|
||||
set(_is_no_install_plugin OFF) # If this plugin will not be added, this indicates if it's because the plugin is
|
||||
# specified in the NO_INSTALL_PLUGINS list.
|
||||
if(_no_install_plugin_list)
|
||||
if("${_plugin_dir_name_lowercase}" IN_LIST _no_install_plugin_list)
|
||||
set(_add_plugin OFF)
|
||||
set(_is_no_install_plugin ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Make sure this is not a subdirectory in a plugin that's already in the install list
|
||||
if(_add_plugin)
|
||||
foreach(_known_install_plugin_dir ${_install_plugin_list})
|
||||
if(_plugin_dir MATCHES "${_known_install_plugin_dir}.+")
|
||||
set(_add_plugin OFF)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(_add_plugin)
|
||||
if(${PRINT_PLUGIN_LIST})
|
||||
message(STATUS "[+] PLUGIN TO INSTALL: ${_rel_plugin_dir}")
|
||||
endif()
|
||||
get_filename_component(_rel_plugin_parent_dir ${_rel_plugin_dir} DIRECTORY)
|
||||
install(DIRECTORY ${_rel_plugin_dir}
|
||||
DESTINATION lib${LIB_SUFFIX}/cura/${_rel_plugin_parent_dir}
|
||||
PATTERN "__pycache__" EXCLUDE
|
||||
PATTERN "*.qmlc" EXCLUDE
|
||||
)
|
||||
list(APPEND _install_plugin_list ${_plugin_dir})
|
||||
elseif(_is_no_install_plugin)
|
||||
if(${PRINT_PLUGIN_LIST})
|
||||
message(STATUS "[-] PLUGIN TO REMOVE : ${_rel_plugin_dir}")
|
||||
endif()
|
||||
execute_process(COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_bundled_packages_json.py
|
||||
-d ${CMAKE_CURRENT_SOURCE_DIR}/resources/bundled_packages
|
||||
${_plugin_dir_name}
|
||||
RESULT_VARIABLE _mod_json_result)
|
||||
endif()
|
||||
endforeach()
|
@ -1,77 +0,0 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
include(CTest)
|
||||
include(CMakeParseArguments)
|
||||
|
||||
add_custom_target(test-verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose)
|
||||
|
||||
function(cura_add_test)
|
||||
set(_single_args NAME DIRECTORY PYTHONPATH)
|
||||
cmake_parse_arguments("" "" "${_single_args}" "" ${ARGN})
|
||||
|
||||
if(NOT _NAME)
|
||||
message(FATAL_ERROR "cura_add_test requires a test name argument")
|
||||
endif()
|
||||
|
||||
if(NOT _DIRECTORY)
|
||||
message(FATAL_ERROR "cura_add_test requires a directory to test")
|
||||
endif()
|
||||
|
||||
if(NOT _PYTHONPATH)
|
||||
set(_PYTHONPATH ${_DIRECTORY})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
string(REPLACE "|" "\\;" _PYTHONPATH ${_PYTHONPATH})
|
||||
set(_PYTHONPATH "${_PYTHONPATH}\\;$ENV{PYTHONPATH}")
|
||||
else()
|
||||
string(REPLACE "|" ":" _PYTHONPATH ${_PYTHONPATH})
|
||||
set(_PYTHONPATH "${_PYTHONPATH}:$ENV{PYTHONPATH}")
|
||||
endif()
|
||||
|
||||
get_test_property(${_NAME} ENVIRONMENT test_exists) #Find out if the test exists by getting a property from it that always exists (such as ENVIRONMENT because we set that ourselves).
|
||||
if (NOT ${test_exists})
|
||||
add_test(
|
||||
NAME ${_NAME}
|
||||
COMMAND ${Python_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
||||
)
|
||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
||||
else()
|
||||
message(WARNING "Duplicate test ${_NAME}!")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
#Add code style test.
|
||||
add_test(
|
||||
NAME "code-style"
|
||||
COMMAND ${Python_EXECUTABLE} run_mypy.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
#Add test for import statements which are not compatible with all builds
|
||||
add_test(
|
||||
NAME "invalid-imports"
|
||||
COMMAND ${Python_EXECUTABLE} scripts/check_invalid_imports.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
cura_add_test(NAME pytest-main DIRECTORY ${CMAKE_SOURCE_DIR}/tests PYTHONPATH "${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
|
||||
|
||||
file(GLOB_RECURSE _plugins plugins/*/__init__.py)
|
||||
foreach(_plugin ${_plugins})
|
||||
get_filename_component(_plugin_directory ${_plugin} DIRECTORY)
|
||||
if(EXISTS ${_plugin_directory}/tests)
|
||||
get_filename_component(_plugin_name ${_plugin_directory} NAME)
|
||||
cura_add_test(NAME pytest-${_plugin_name} DIRECTORY ${_plugin_directory} PYTHONPATH "${_plugin_directory}|${CMAKE_SOURCE_DIR}|${URANIUM_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
#Add test for whether the shortcut alt-keys are unique in every translation.
|
||||
add_test(
|
||||
NAME "shortcut-keys"
|
||||
COMMAND ${Python_EXECUTABLE} scripts/check_shortcut_keys.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# This script removes the given package entries in the bundled_packages JSON files. This is used by the PluginInstall
|
||||
# CMake module.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def find_json_files(work_dir: str) -> list:
|
||||
"""Finds all JSON files in the given directory recursively and returns a list of those files in absolute paths.
|
||||
|
||||
:param work_dir: The directory to look for JSON files recursively.
|
||||
:return: A list of JSON files in absolute paths that are found in the given directory.
|
||||
"""
|
||||
|
||||
json_file_list = []
|
||||
for root, dir_names, file_names in os.walk(work_dir):
|
||||
for file_name in file_names:
|
||||
abs_path = os.path.abspath(os.path.join(root, file_name))
|
||||
json_file_list.append(abs_path)
|
||||
return json_file_list
|
||||
|
||||
|
||||
def remove_entries_from_json_file(file_path: str, entries: list) -> None:
|
||||
"""Removes the given entries from the given JSON file. The file will modified in-place.
|
||||
|
||||
:param file_path: The JSON file to modify.
|
||||
:param entries: A list of strings as entries to remove.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
try:
|
||||
with open(file_path, "r", encoding = "utf-8") as f:
|
||||
package_dict = json.load(f, object_hook = collections.OrderedDict)
|
||||
except Exception as e:
|
||||
msg = "Failed to load '{file_path}' as a JSON file. This file will be ignored Exception: {e}"\
|
||||
.format(file_path = file_path, e = e)
|
||||
sys.stderr.write(msg + os.linesep)
|
||||
return
|
||||
|
||||
for entry in entries:
|
||||
if entry in package_dict:
|
||||
del package_dict[entry]
|
||||
print("[INFO] Remove entry [{entry}] from [{file_path}]".format(file_path = file_path, entry = entry))
|
||||
|
||||
try:
|
||||
with open(file_path, "w", encoding = "utf-8", newline = "\n") as f:
|
||||
json.dump(package_dict, f, indent = 4)
|
||||
except Exception as e:
|
||||
msg = "Failed to write '{file_path}' as a JSON file. Exception: {e}".format(file_path = file_path, e = e)
|
||||
raise IOError(msg)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser("mod_bundled_packages_json")
|
||||
parser.add_argument("-d", "--dir", dest = "work_dir",
|
||||
help = "The directory to look for bundled packages JSON files, recursively.")
|
||||
parser.add_argument("entries", metavar = "ENTRIES", type = str, nargs = "+")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
json_file_list = find_json_files(args.work_dir)
|
||||
for json_file_path in json_file_list:
|
||||
remove_entries_from_json_file(json_file_path, args.entries)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2016 Richard Hughes <richard@hughsie.com> -->
|
||||
<component type="desktop">
|
||||
<id>com.ultimaker.cura.desktop</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>LGPL-3.0 and CC-BY-SA-4.0</project_license>
|
||||
<name>Cura</name>
|
||||
<summary>The world's most advanced 3d printer software</summary>
|
||||
<description>
|
||||
<p>
|
||||
Cura creates a seamless integration between hardware, software and
|
||||
materials for the best 3D printing experience around.
|
||||
Cura supports the 3MF, OBJ and STL file formats and is available on
|
||||
Windows, Mac and Linux.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Novices can start printing right away</li>
|
||||
<li>Experts are able to customize 300 settings to achieve the best results</li>
|
||||
<li>Optimized profiles for Ultimaker materials</li>
|
||||
<li>Supported by a global network of Ultimaker certified service partners</li>
|
||||
<li>Print multiple objects at once with different settings for each object</li>
|
||||
<li>Cura supports STL, 3MF and OBJ file formats</li>
|
||||
<li>Open source and completely free</li>
|
||||
</ul>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/Ultimaker/Cura/main/cura-logo.PNG</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">https://ultimaker.com/software/ultimaker-cura?utm_source=cura&utm_medium=software&utm_campaign=cura-update-linux</url>
|
||||
<translation type="gettext">Cura</translation>
|
||||
<content_rating type="oars-1.1" />
|
||||
</component>
|
@ -1,19 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=Ultimaker Cura
|
||||
Name[de]=Ultimaker Cura
|
||||
Name[nl]=Ultimaker Cura
|
||||
GenericName=3D Printing Software
|
||||
GenericName[de]=3D-Druck-Software
|
||||
GenericName[nl]=3D-printsoftware
|
||||
Comment=Cura converts 3D models into paths for a 3D printer. It prepares your print for maximum accuracy, minimum printing time and good reliability with many extra features that make your print come out great.
|
||||
Comment[de]=Cura wandelt 3D-Modelle in Pfade für einen 3D-Drucker um. Es bereitet Ihren Druck für maximale Genauigkeit, minimale Druckzeit und guter Zuverlässigkeit mit vielen zusätzlichen Funktionen vor, damit Ihr Druck großartig wird.
|
||||
Comment[nl]=Cura converteert 3D-modellen naar paden voor een 3D printer. Het bereidt je print voor om zeer precies, snel en betrouwbaar te kunnen printen, met veel extra functionaliteit om je print er goed uit te laten komen.
|
||||
Exec=@CMAKE_INSTALL_FULL_BINDIR@/cura %F
|
||||
TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura
|
||||
Icon=cura-icon
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;text/x-gcode;application/x-amf;application/x-ply;application/x-ctm;model/vnd.collada+xml;model/gltf-binary;model/gltf+json;model/vnd.collada+xml+zip;
|
||||
Categories=Graphics;
|
||||
Keywords=3D;Printing;Slicer;
|
||||
StartupWMClass=cura.real
|
107
conandata.yml
107
conandata.yml
@ -1,5 +1,5 @@
|
||||
---
|
||||
# Usage: defaults to None
|
||||
# Usage: defaults to the first entry in this conandata.yml file
|
||||
# If you're on a release branch create an entry for that **version** e.q.: `5.1.0` update the requirements (use pinned versions, not latest)
|
||||
# also create a beta entry for that **version** e.q.: `5.1.0-beta`, update the requirements (use the <dep_name>/(latest)@ultimaker/stable)
|
||||
#
|
||||
@ -10,111 +10,6 @@
|
||||
# requirements (use the <dep_name>/(latest)@ultimaker/testing)
|
||||
#
|
||||
# Subject to change in the future!
|
||||
"None":
|
||||
requirements:
|
||||
- "pyarcus/(latest)@ultimaker/testing"
|
||||
- "curaengine/(latest)@ultimaker/testing"
|
||||
- "pysavitar/(latest)@ultimaker/testing"
|
||||
- "pynest2d/(latest)@ultimaker/testing"
|
||||
- "uranium/(latest)@ultimaker/testing"
|
||||
- "fdm_materials/(latest)@ultimaker/testing"
|
||||
- "cura_binary_data/(latest)@ultimaker/testing"
|
||||
- "cpython/3.10.4"
|
||||
internal_requirements:
|
||||
- "fdm_materials_private/(latest)@ultimaker/testing"
|
||||
- "cura_private_data/(latest)@ultimaker/testing"
|
||||
runinfo:
|
||||
entrypoint: "cura_app.py"
|
||||
pyinstaller:
|
||||
datas:
|
||||
cura_plugins:
|
||||
package: "cura"
|
||||
src: "plugins"
|
||||
dst: "share/cura/plugins"
|
||||
cura_resources:
|
||||
package: "cura"
|
||||
src: "resources"
|
||||
dst: "share/cura/resources"
|
||||
cura_private_data:
|
||||
package: "cura_private_data"
|
||||
src: "resources"
|
||||
dst: "share/cura/resources"
|
||||
internal: true
|
||||
uranium_plugins:
|
||||
package: "uranium"
|
||||
src: "plugins"
|
||||
dst: "share/uranium/plugins"
|
||||
uranium_resources:
|
||||
package: "uranium"
|
||||
src: "resources"
|
||||
dst: "share/uranium/resources"
|
||||
uranium_um_qt_qml_um:
|
||||
package: "uranium"
|
||||
src: "site-packages/UM/Qt/qml/UM"
|
||||
dst: "PyQt6/Qt6/qml/UM"
|
||||
cura_binary_data:
|
||||
package: "cura_binary_data"
|
||||
src: "resources/cura/resources"
|
||||
dst: "share/cura/resources"
|
||||
uranium_binary_data:
|
||||
package: "cura_binary_data"
|
||||
src: "resources/uranium/resources"
|
||||
dst: "share/uranium/resources"
|
||||
windows_binary_data:
|
||||
package: "cura_binary_data"
|
||||
src: "windows"
|
||||
dst: "share/windows"
|
||||
fdm_materials:
|
||||
package: "fdm_materials"
|
||||
src: "materials"
|
||||
dst: "share/cura/resources/materials"
|
||||
fdm_materials_private:
|
||||
package: "fdm_materials_private"
|
||||
src: "resources/materials"
|
||||
dst: "share/cura/resources/materials"
|
||||
internal: true
|
||||
tcl:
|
||||
package: "tcl"
|
||||
src: "lib/tcl8.6"
|
||||
dst: "tcl"
|
||||
tk:
|
||||
package: "tk"
|
||||
src: "lib/tk8.6"
|
||||
dst: "tk"
|
||||
binaries:
|
||||
curaengine:
|
||||
package: "curaengine"
|
||||
src: "bin"
|
||||
dst: "."
|
||||
binary: "CuraEngine"
|
||||
hiddenimports:
|
||||
- "pySavitar"
|
||||
- "pyArcus"
|
||||
- "pynest2d"
|
||||
- "PyQt6"
|
||||
- "PyQt6.QtNetwork"
|
||||
- "PyQt6.sip"
|
||||
- "logging.handlers"
|
||||
- "zeroconf"
|
||||
- "fcntl"
|
||||
- "stl"
|
||||
- "serial"
|
||||
collect_all:
|
||||
- "cura"
|
||||
- "UM"
|
||||
- "serial"
|
||||
- "Charon"
|
||||
- "sqlite3"
|
||||
- "trimesh"
|
||||
- "win32ctypes"
|
||||
- "PyQt6"
|
||||
- "PyQt6.QtNetwork"
|
||||
- "PyQt6.sip"
|
||||
- "stl"
|
||||
icon:
|
||||
Windows: "./icons/Cura.ico"
|
||||
Macos: "./icons/cura.icns"
|
||||
Linux: "./icons/cura-128.png"
|
||||
"5.2.0-alpha":
|
||||
requirements:
|
||||
- "pyarcus/(latest)@ultimaker/testing"
|
||||
|
33
conanfile.py
33
conanfile.py
@ -3,13 +3,13 @@ from pathlib import Path
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from conans import tools
|
||||
from conan import ConanFile
|
||||
from conan.tools import files
|
||||
from conan.tools.files import copy, rmdir, save
|
||||
from conan.tools.env import VirtualRunEnv, Environment
|
||||
from conan.tools.scm import Version
|
||||
from conan.errors import ConanInvalidConfiguration
|
||||
|
||||
required_conan_version = ">=1.48.0"
|
||||
required_conan_version = ">=1.50.0"
|
||||
|
||||
|
||||
class CuraConan(ConanFile):
|
||||
@ -26,7 +26,7 @@ class CuraConan(ConanFile):
|
||||
|
||||
# FIXME: Remove specific branch once merged to main
|
||||
# Extending the conanfile with the UMBaseConanfile https://github.com/Ultimaker/conan-ultimaker-index/tree/CURA-9177_Fix_CI_CD/recipes/umbase
|
||||
python_requires = "umbase/0.1.5@ultimaker/testing"
|
||||
python_requires = "umbase/[>=0.1.7]@ultimaker/stable"
|
||||
python_requires_extend = "umbase.UMBaseConanfile"
|
||||
|
||||
options = {
|
||||
@ -135,7 +135,7 @@ class CuraConan(ConanFile):
|
||||
def _site_packages(self):
|
||||
if self.settings.os == "Windows":
|
||||
return self._base_dir.joinpath("Lib", "site-packages")
|
||||
py_version = tools.Version(self.deps_cpp_info["cpython"].version)
|
||||
py_version = Version(self.deps_cpp_info["cpython"].version)
|
||||
return self._base_dir.joinpath("lib", f"python{py_version.major}.{py_version.minor}", "site-packages")
|
||||
|
||||
@property
|
||||
@ -149,9 +149,9 @@ class CuraConan(ConanFile):
|
||||
with open(Path(__file__).parent.joinpath("CuraVersion.py.jinja"), "r") as f:
|
||||
cura_version_py = Template(f.read())
|
||||
|
||||
cura_version = self.version
|
||||
cura_version = self.conf_info.get("user.cura:version", default = self.version, check_type = str)
|
||||
if self.options.internal:
|
||||
version = tools.Version(self.version)
|
||||
version = Version(cura_version)
|
||||
cura_version = f"{version.major}.{version.minor}.{version.patch}-{version.prerelease.replace('+', '+internal_')}"
|
||||
|
||||
with open(Path(location, "CuraVersion.py"), "w") as f:
|
||||
@ -220,7 +220,8 @@ class CuraConan(ConanFile):
|
||||
with open(Path(__file__).parent.joinpath("Ultimaker-Cura.spec.jinja"), "r") as f:
|
||||
pyinstaller = Template(f.read())
|
||||
|
||||
cura_version = tools.Version(self.version) if self.version else tools.Version("0.0.0")
|
||||
version = self.conf_info.get("user.cura:version", default = self.version, check_type = str)
|
||||
cura_version = Version(version)
|
||||
|
||||
with open(Path(location, "Ultimaker-Cura.spec"), "w") as f:
|
||||
f.write(pyinstaller.render(
|
||||
@ -239,10 +240,14 @@ class CuraConan(ConanFile):
|
||||
strip = False, # This should be possible on Linux and MacOS but, it can also cause issues on some distributions. Safest is to disable it for now
|
||||
target_arch = "'x86_64'" if self.settings.os == "Macos" else "None", # FIXME: Make this dependent on the settings.arch_target
|
||||
macos = self.settings.os == "Macos",
|
||||
version = f"'{self.version}'",
|
||||
version = f"'{version}'",
|
||||
short_version = f"'{cura_version.major}.{cura_version.minor}.{cura_version.patch}'",
|
||||
))
|
||||
|
||||
def set_version(self):
|
||||
if self.version is None:
|
||||
self.version = self._umdefault_version()
|
||||
|
||||
def configure(self):
|
||||
self.options["pyarcus"].shared = True
|
||||
self.options["pysavitar"].shared = True
|
||||
@ -250,7 +255,8 @@ class CuraConan(ConanFile):
|
||||
self.options["cpython"].shared = True
|
||||
|
||||
def validate(self):
|
||||
if self.version and tools.Version(self.version) <= tools.Version("4"):
|
||||
version = self.conf_info.get("user.cura:version", default = self.version, check_type = str)
|
||||
if version and Version(version) <= Version("4"):
|
||||
raise ConanInvalidConfiguration("Only versions 5+ are support")
|
||||
|
||||
def requirements(self):
|
||||
@ -293,7 +299,7 @@ class CuraConan(ConanFile):
|
||||
self.copy("CuraEngine.exe", root_package = "curaengine", src = "@bindirs", dst = "", keep_path = False)
|
||||
self.copy("CuraEngine", root_package = "curaengine", src = "@bindirs", dst = "", keep_path = False)
|
||||
|
||||
files.rmdir(self, "resources/materials")
|
||||
rmdir(self, os.path.join(self.source_folder, "resources", "materials"))
|
||||
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)
|
||||
|
||||
@ -377,7 +383,8 @@ class CuraConan(ConanFile):
|
||||
self.copy("*.txt", src = self.cpp_info.resdirs[-1], dst = self._base_dir.joinpath("pip_requirements"))
|
||||
|
||||
# Generate the GitHub Action version info Environment
|
||||
cura_version = tools.Version(self.version)
|
||||
version = self.conf_info.get("user.cura:version", default = self.version, check_type = str)
|
||||
cura_version = Version(version)
|
||||
env_prefix = "Env:" if self.settings.os == "Windows" else ""
|
||||
activate_github_actions_version_env = Template(r"""echo "CURA_VERSION_MAJOR={{ cura_version_major }}" >> ${{ env_prefix }}GITHUB_ENV
|
||||
echo "CURA_VERSION_MINOR={{ cura_version_minor }}" >> ${{ env_prefix }}GITHUB_ENV
|
||||
@ -392,7 +399,7 @@ echo "CURA_VERSION_FULL={{ cura_version_full }}" >> ${{ env_prefix }}GITHUB_ENV
|
||||
env_prefix = env_prefix)
|
||||
|
||||
ext = ".sh" if self.settings.os != "Windows" else ".ps1"
|
||||
files.save(self, self._script_dir.joinpath(f"activate_github_actions_version_env{ext}"), activate_github_actions_version_env)
|
||||
save(self, self._script_dir.joinpath(f"activate_github_actions_version_env{ext}"), activate_github_actions_version_env)
|
||||
|
||||
self._generate_cura_version(Path(self._site_packages, "cura"))
|
||||
|
||||
|
@ -16,4 +16,6 @@ Making pull requests
|
||||
--------------------
|
||||
If you want to propose a change to Cura's source code, please create a pull request in the appropriate repository (being [Cura](https://github.com/Ultimaker/Cura), [Uranium](https://github.com/Ultimaker/Uranium), [CuraEngine](https://github.com/Ultimaker/CuraEngine), [fdm_materials](https://github.com/Ultimaker/fdm_materials), [libArcus](https://github.com/Ultimaker/libArcus), [cura-build](https://github.com/Ultimaker/cura-build), [cura-build-environment](https://github.com/Ultimaker/cura-build-environment), [libSavitar](https://github.com/Ultimaker/libSavitar), [libCharon](https://github.com/Ultimaker/libCharon) or [cura-binary-data](https://github.com/Ultimaker/cura-binary-data)) and if your change requires changes on multiple of these repositories, please link them together so that we know to merge them together.
|
||||
|
||||
Some of these repositories will have automated tests running when you create a pull request, indicated by green check marks or red crosses in the Github web page. If you see a red cross, that means that a test has failed. If the test doesn't fail on the Master branch but does fail on your branch, that indicates that you've probably made a mistake and you need to do that. Click on the cross for more details, or run the test locally by running `cmake . && ctest --verbose`.
|
||||
The style guide for code contributions to Cura and other Ultimaker projects can be found [here](https://github.com/Ultimaker/Meta/blob/master/general/generic_code_conventions.md).
|
||||
|
||||
Some of these repositories will have automated tests running when you create a pull request, indicated by green check marks or red crosses in the Github web page. If you see a red cross, that means that a test has failed. If the test doesn't fail on the Master branch but does fail on your branch, that indicates that you've probably made a mistake and you need to do that. Click on the cross for more details, or run the test locally by running `cmake . && ctest --verbose`.
|
||||
|
@ -21,6 +21,7 @@ class ArrangeObjectsJob(Job):
|
||||
self._min_offset = min_offset
|
||||
|
||||
def run(self):
|
||||
found_solution_for_all = False
|
||||
status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"),
|
||||
lifetime = 0,
|
||||
dismissable = False,
|
||||
@ -28,18 +29,19 @@ class ArrangeObjectsJob(Job):
|
||||
title = i18n_catalog.i18nc("@info:title", "Finding Location"))
|
||||
status_message.show()
|
||||
|
||||
found_solution_for_all = None
|
||||
try:
|
||||
found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes)
|
||||
except: # If the thread crashes, the message should still close
|
||||
Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.")
|
||||
|
||||
status_message.hide()
|
||||
if found_solution_for_all is not None and not found_solution_for_all:
|
||||
|
||||
if not found_solution_for_all:
|
||||
no_full_solution_message = Message(
|
||||
i18n_catalog.i18nc("@info:status",
|
||||
"Unable to find a location within the build volume for all objects"),
|
||||
title = i18n_catalog.i18nc("@info:title", "Can't Find Location"),
|
||||
message_type = Message.MessageType.ERROR)
|
||||
no_full_solution_message.show()
|
||||
|
||||
self.finished.emit(self)
|
||||
|
@ -115,6 +115,7 @@ from . import CuraActions
|
||||
from . import PlatformPhysics
|
||||
from . import PrintJobPreviewImageProvider
|
||||
from .AutoSave import AutoSave
|
||||
from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel
|
||||
from .Machines.Models.MachineListModel import MachineListModel
|
||||
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
|
||||
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
|
||||
@ -1191,6 +1192,7 @@ class CuraApplication(QtApplication):
|
||||
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
|
||||
qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel")
|
||||
qmlRegisterType(MachineListModel, "Cura", 1, 0, "MachineListModel")
|
||||
qmlRegisterType(CompatibleMachineModel, "Cura", 1, 0, "CompatibleMachineModel")
|
||||
|
||||
self.processEvents()
|
||||
qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel")
|
||||
@ -1445,7 +1447,7 @@ class CuraApplication(QtApplication):
|
||||
bounding_box = node.getBoundingBox()
|
||||
if bounding_box is None or bounding_box.width < self._volume.getBoundingBox().width or bounding_box.depth < self._volume.getBoundingBox().depth:
|
||||
# Arrange only the unlocked nodes and keep the locked ones in place
|
||||
if UM.Util.parseBool(node.getSetting(SceneNodeSettings.LockPosition)):
|
||||
if node.getSetting(SceneNodeSettings.LockPosition):
|
||||
locked_nodes.append(node)
|
||||
else:
|
||||
nodes_to_arrange.append(node)
|
||||
|
@ -24,9 +24,12 @@ class LayerPolygon:
|
||||
PrimeTowerType = 11
|
||||
__number_of_types = 12
|
||||
|
||||
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType, numpy.arange(__number_of_types) == MoveCombingType), numpy.arange(__number_of_types) == MoveRetractionType)
|
||||
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType,
|
||||
numpy.arange(__number_of_types) == MoveCombingType),
|
||||
numpy.arange(__number_of_types) == MoveRetractionType)
|
||||
|
||||
def __init__(self, extruder: int, line_types: numpy.ndarray, data: numpy.ndarray, line_widths: numpy.ndarray, line_thicknesses: numpy.ndarray, line_feedrates: numpy.ndarray) -> None:
|
||||
def __init__(self, extruder: int, line_types: numpy.ndarray, data: numpy.ndarray,
|
||||
line_widths: numpy.ndarray, line_thicknesses: numpy.ndarray, line_feedrates: numpy.ndarray) -> None:
|
||||
"""LayerPolygon, used in ProcessSlicedLayersJob
|
||||
|
||||
:param extruder: The position of the extruder
|
||||
@ -39,10 +42,12 @@ class LayerPolygon:
|
||||
|
||||
self._extruder = extruder
|
||||
self._types = line_types
|
||||
for i in range(len(self._types)):
|
||||
if self._types[i] >= self.__number_of_types: # Got faulty line data from the engine.
|
||||
Logger.log("w", "Found an unknown line type: %s", i)
|
||||
self._types[i] = self.NoneType
|
||||
unknown_types = numpy.where(self._types >= self.__number_of_types, self._types, None)
|
||||
if unknown_types.any():
|
||||
# Got faulty line data from the engine.
|
||||
for idx in unknown_types:
|
||||
Logger.warning(f"Found an unknown line type at: {idx}")
|
||||
self._types[idx] = self.NoneType
|
||||
self._data = data
|
||||
self._line_widths = line_widths
|
||||
self._line_thicknesses = line_thicknesses
|
||||
@ -58,14 +63,16 @@ class LayerPolygon:
|
||||
self._mesh_line_count = len(self._types) - self._jump_count
|
||||
self._vertex_count = self._mesh_line_count + numpy.sum(self._types[1:] == self._types[:-1])
|
||||
|
||||
# Buffering the colors shouldn't be necessary as it is not
|
||||
# Buffering the colors shouldn't be necessary as it is not
|
||||
# re-used and can save a lot of memory usage.
|
||||
self._color_map = LayerPolygon.getColorMap()
|
||||
self._colors = self._color_map[self._types] # type: numpy.ndarray
|
||||
|
||||
# When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType
|
||||
# When type is used as index returns true if type == LayerPolygon.InfillType
|
||||
# or type == LayerPolygon.SkinType
|
||||
# or type == LayerPolygon.SupportInfillType
|
||||
# Should be generated in better way, not hardcoded.
|
||||
self._is_infill_or_skin_type_map = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype = bool)
|
||||
self._is_infill_or_skin_type_map = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype=bool)
|
||||
|
||||
self._build_cache_line_mesh_mask = None # type: Optional[numpy.ndarray]
|
||||
self._build_cache_needed_points = None # type: Optional[numpy.ndarray]
|
||||
@ -80,12 +87,14 @@ class LayerPolygon:
|
||||
# Only if the type of line segment changes do we need to add an extra vertex to change colors
|
||||
self._build_cache_needed_points[1:, 0][:, numpy.newaxis] = self._types[1:] != self._types[:-1]
|
||||
# Mark points as unneeded if they are of types we don't want in the line mesh according to the calculated mask
|
||||
numpy.logical_and(self._build_cache_needed_points, self._build_cache_line_mesh_mask, self._build_cache_needed_points )
|
||||
numpy.logical_and(self._build_cache_needed_points, self._build_cache_line_mesh_mask, self._build_cache_needed_points)
|
||||
|
||||
self._vertex_begin = 0
|
||||
self._vertex_end = cast(int, numpy.sum(self._build_cache_needed_points))
|
||||
|
||||
def build(self, vertex_offset: int, index_offset: int, vertices: numpy.ndarray, colors: numpy.ndarray, line_dimensions: numpy.ndarray, feedrates: numpy.ndarray, extruders: numpy.ndarray, line_types: numpy.ndarray, indices: numpy.ndarray) -> None:
|
||||
def build(self, vertex_offset: int, index_offset: int, vertices: numpy.ndarray,
|
||||
colors: numpy.ndarray, line_dimensions: numpy.ndarray, feedrates: numpy.ndarray,
|
||||
extruders: numpy.ndarray, line_types: numpy.ndarray, indices: numpy.ndarray) -> None:
|
||||
"""Set all the arrays provided by the function caller, representing the LayerPolygon
|
||||
|
||||
The arrays are either by vertex or by indices.
|
||||
@ -111,19 +120,20 @@ class LayerPolygon:
|
||||
line_mesh_mask = self._build_cache_line_mesh_mask
|
||||
needed_points_list = self._build_cache_needed_points
|
||||
|
||||
# Index to the points we need to represent the line mesh. This is constructed by generating simple
|
||||
# start and end points for each line. For line segment n these are points n and n+1. Row n reads [n n+1]
|
||||
# Then then the indices for the points we don't need are thrown away based on the pre-calculated list.
|
||||
index_list = ( numpy.arange(len(self._types)).reshape((-1, 1)) + numpy.array([[0, 1]]) ).reshape((-1, 1))[needed_points_list.reshape((-1, 1))]
|
||||
# Index to the points we need to represent the line mesh.
|
||||
# This is constructed by generating simple start and end points for each line.
|
||||
# For line segment n, these are points n and n+1. Row n reads [n n+1]
|
||||
# Then the indices for the points we don't need are thrown away based on the pre-calculated list.
|
||||
index_list = (numpy.arange(len(self._types)).reshape((-1, 1)) + numpy.array([[0, 1]])).reshape((-1, 1))[needed_points_list.reshape((-1, 1))]
|
||||
|
||||
# The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset.
|
||||
self._vertex_begin += vertex_offset
|
||||
self._vertex_end += vertex_offset
|
||||
|
||||
# Points are picked based on the index list to get the vertices needed.
|
||||
# Points are picked based on the index list to get the vertices needed.
|
||||
vertices[self._vertex_begin:self._vertex_end, :] = self._data[index_list, :]
|
||||
|
||||
# Create an array with colors for each vertex and remove the color data for the points that has been thrown away.
|
||||
# Create an array with colors for each vertex and remove the color data for the points that has been thrown away.
|
||||
colors[self._vertex_begin:self._vertex_end, :] = numpy.tile(self._colors, (1, 2)).reshape((-1, 4))[needed_points_list.ravel()]
|
||||
|
||||
# Create an array with line widths and thicknesses for each vertex.
|
||||
@ -138,14 +148,15 @@ class LayerPolygon:
|
||||
# Convert type per vertex to type per line
|
||||
line_types[self._vertex_begin:self._vertex_end] = numpy.tile(self._types, (1, 2)).reshape((-1, 1))[needed_points_list.ravel()][:, 0]
|
||||
|
||||
# The relative values of begin and end indices have already been set in buildCache, so we only need to offset them to the parents offset.
|
||||
# The relative values of begin and end indices have already been set in buildCache,
|
||||
# so we only need to offset them to the parents offset.
|
||||
self._index_begin += index_offset
|
||||
self._index_end += index_offset
|
||||
|
||||
indices[self._index_begin:self._index_end, :] = numpy.arange(self._index_end-self._index_begin, dtype = numpy.int32).reshape((-1, 1))
|
||||
indices[self._index_begin:self._index_end, :] = numpy.arange(self._index_end-self._index_begin, dtype=numpy.int32).reshape((-1, 1))
|
||||
# When the line type changes the index needs to be increased by 2.
|
||||
indices[self._index_begin:self._index_end, :] += numpy.cumsum(needed_points_list[line_mesh_mask.ravel(), 0], dtype = numpy.int32).reshape((-1, 1))
|
||||
# Each line segment goes from it's starting point p to p+1, offset by the vertex index.
|
||||
# Each line segment goes from it's starting point p to p+1, offset by the vertex index.
|
||||
# The -1 is to compensate for the necessarily True value of needed_points_list[0,0] which causes an unwanted +1 in cumsum above.
|
||||
indices[self._index_begin:self._index_end, :] += numpy.array([self._vertex_begin - 1, self._vertex_begin])
|
||||
|
||||
@ -214,13 +225,12 @@ class LayerPolygon:
|
||||
"""
|
||||
|
||||
normals = numpy.copy(self._data)
|
||||
normals[:, 1] = 0.0 # We are only interested in 2D normals
|
||||
normals[:, 1] = 0.0 # We are only interested in 2D normals
|
||||
|
||||
# Calculate the edges between points.
|
||||
# The call to numpy.roll shifts the entire array by one so that
|
||||
# we end up subtracting each next point from the current, wrapping
|
||||
# around. This gives us the edges from the next point to the current
|
||||
# point.
|
||||
# The call to numpy.roll shifts the entire array by one
|
||||
# so that we end up subtracting each next point from the current, wrapping around.
|
||||
# This gives us the edges from the next point to the current point.
|
||||
normals = numpy.diff(normals, 1, 0)
|
||||
|
||||
# Calculate the length of each edge using standard Pythagoras
|
||||
@ -245,17 +255,17 @@ class LayerPolygon:
|
||||
if cls.__color_map is None:
|
||||
theme = cast(Theme, QtApplication.getInstance().getTheme())
|
||||
cls.__color_map = numpy.array([
|
||||
theme.getColor("layerview_none").getRgbF(), # NoneType
|
||||
theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type
|
||||
theme.getColor("layerview_inset_x").getRgbF(), # InsetXType
|
||||
theme.getColor("layerview_skin").getRgbF(), # SkinType
|
||||
theme.getColor("layerview_support").getRgbF(), # SupportType
|
||||
theme.getColor("layerview_skirt").getRgbF(), # SkirtType
|
||||
theme.getColor("layerview_infill").getRgbF(), # InfillType
|
||||
theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType
|
||||
theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType
|
||||
theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType
|
||||
theme.getColor("layerview_support_interface").getRgbF(), # SupportInterfaceType
|
||||
theme.getColor("layerview_none").getRgbF(), # NoneType
|
||||
theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type
|
||||
theme.getColor("layerview_inset_x").getRgbF(), # InsetXType
|
||||
theme.getColor("layerview_skin").getRgbF(), # SkinType
|
||||
theme.getColor("layerview_support").getRgbF(), # SupportType
|
||||
theme.getColor("layerview_skirt").getRgbF(), # SkirtType
|
||||
theme.getColor("layerview_infill").getRgbF(), # InfillType
|
||||
theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType
|
||||
theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType
|
||||
theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType
|
||||
theme.getColor("layerview_support_interface").getRgbF(), # SupportInterfaceType
|
||||
theme.getColor("layerview_prime_tower").getRgbF() # PrimeTowerType
|
||||
])
|
||||
|
||||
|
@ -94,7 +94,7 @@ class MachineAction(QObject, PluginObject):
|
||||
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||
Logger.error(f"Cannot create QML view: cannot find plugin path for plugin {self.getPluginId()}")
|
||||
return None
|
||||
path = os.path.join(plugin_path, self._qml_url)
|
||||
|
||||
@ -106,7 +106,7 @@ class MachineAction(QObject, PluginObject):
|
||||
def qmlPath(self) -> "QUrl":
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
|
||||
Logger.error(f"Cannot create QML view: cannot find plugin path for plugin {self.getPluginId()}")
|
||||
return QUrl("")
|
||||
path = os.path.join(plugin_path, self._qml_url)
|
||||
return QUrl.fromLocalFile(path)
|
||||
|
@ -43,7 +43,7 @@ class MachineErrorChecker(QObject):
|
||||
self._application = cura.CuraApplication.CuraApplication.getInstance()
|
||||
self._machine_manager = self._application.getMachineManager()
|
||||
|
||||
self._start_time = 0. # measure checking time
|
||||
self._check_start_time = time.time()
|
||||
|
||||
self._setCheckTimer()
|
||||
|
||||
@ -160,7 +160,7 @@ class MachineErrorChecker(QObject):
|
||||
self._stacks_and_keys_to_check.append((stack, key))
|
||||
|
||||
self._application.callLater(self._checkStack)
|
||||
self._start_time = time.time()
|
||||
self._check_start_time = time.time()
|
||||
Logger.log("d", "New error check scheduled.")
|
||||
|
||||
def _checkStack(self) -> None:
|
||||
@ -212,12 +212,10 @@ class MachineErrorChecker(QObject):
|
||||
self._has_errors = result
|
||||
self.hasErrorUpdated.emit()
|
||||
self._machine_manager.stacksValidationChanged.emit()
|
||||
if keys_to_recheck is None:
|
||||
self._keys_to_check = set()
|
||||
else:
|
||||
self._keys_to_check = keys_to_recheck
|
||||
self._keys_to_check = keys_to_recheck if keys_to_recheck else set()
|
||||
self._need_to_check = False
|
||||
self._check_in_progress = False
|
||||
self.needToWaitForResultChanged.emit()
|
||||
self.errorCheckFinished.emit()
|
||||
Logger.log("i", "Error check finished, result = %s, time = %0.1fs", result, time.time() - self._start_time)
|
||||
execution_time = time.time() - self._check_start_time
|
||||
Logger.info(f"Error check finished, result = {result}, time = {execution_time:.2f}s")
|
||||
|
76
cura/Machines/Models/CompatibleMachineModel.py
Normal file
76
cura/Machines/Models/CompatibleMachineModel.py
Normal file
@ -0,0 +1,76 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6.QtCore import Qt, QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
|
||||
class CompatibleMachineModel(ListModel):
|
||||
NameRole = Qt.ItemDataRole.UserRole + 1
|
||||
UniqueIdRole = Qt.ItemDataRole.UserRole + 2
|
||||
ExtrudersRole = Qt.ItemDataRole.UserRole + 3
|
||||
|
||||
def __init__(self, parent: Optional[QObject] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._catalog = i18nCatalog("cura")
|
||||
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
self.addRoleName(self.UniqueIdRole, "unique_id")
|
||||
self.addRoleName(self.ExtrudersRole, "extruders")
|
||||
|
||||
self._update()
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
machine_manager.globalContainerChanged.connect(self._update)
|
||||
machine_manager.outputDevicesChanged.connect(self._update)
|
||||
|
||||
def _update(self) -> None:
|
||||
self.clear()
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
# Loop over the output-devices, not the stacks; need all applicable configurations, not just the current loaded one.
|
||||
for output_device in machine_manager.printerOutputDevices:
|
||||
for printer in output_device.printers:
|
||||
extruder_configs = dict()
|
||||
|
||||
# initialize & add current active material:
|
||||
for extruder in printer.extruders:
|
||||
materials = [{
|
||||
"brand": extruder.activeMaterial.brand,
|
||||
"name": extruder.activeMaterial.name,
|
||||
"hexcolor": extruder.activeMaterial.color,
|
||||
}]
|
||||
extruder_configs[extruder.getPosition()] = {
|
||||
"position": extruder.getPosition(),
|
||||
"core": extruder.hotendID,
|
||||
"materials": materials
|
||||
}
|
||||
|
||||
# add currently inactive, but possible materials:
|
||||
for configuration in printer.availableConfigurations:
|
||||
for extruder in configuration.extruderConfigurations:
|
||||
if not extruder.position in extruder_configs:
|
||||
Logger.log("w", f"No active extruder for position {extruder.position}.")
|
||||
continue
|
||||
|
||||
extruder_configs[extruder.position]["materials"].append({
|
||||
"brand": extruder.material.brand,
|
||||
"name": extruder.material.name,
|
||||
"hexcolor": extruder.material.color
|
||||
})
|
||||
|
||||
if any([len(extruder["materials"]) > 0 for extruder in extruder_configs.values()]):
|
||||
self.appendItem({
|
||||
"name": printer.name,
|
||||
"unique_id": printer.name, # <- Can assume the cloud doesn't have duplicate names?
|
||||
"extruders": list(extruder_configs.values())
|
||||
})
|
@ -44,6 +44,7 @@ class GlobalStacksModel(ListModel):
|
||||
self._filter_connection_type = None # type: Optional[ConnectionType]
|
||||
self._filter_online_only = False
|
||||
self._filter_capabilities: List[str] = [] # Required capabilities that all listed printers must have.
|
||||
self._filter_abstract_machines: Optional[bool] = None
|
||||
|
||||
# Listen to changes
|
||||
CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
|
||||
@ -54,6 +55,7 @@ class GlobalStacksModel(ListModel):
|
||||
filterConnectionTypeChanged = pyqtSignal()
|
||||
filterCapabilitiesChanged = pyqtSignal()
|
||||
filterOnlineOnlyChanged = pyqtSignal()
|
||||
filterAbstractMachinesChanged = pyqtSignal()
|
||||
|
||||
def setFilterConnectionType(self, new_filter: Optional[ConnectionType]) -> None:
|
||||
if self._filter_connection_type != new_filter:
|
||||
@ -98,6 +100,22 @@ class GlobalStacksModel(ListModel):
|
||||
"""
|
||||
return self._filter_capabilities
|
||||
|
||||
def setFilterAbstractMachines(self, new_filter: Optional[bool]) -> None:
|
||||
if self._filter_abstract_machines != new_filter:
|
||||
self._filter_abstract_machines = new_filter
|
||||
self.filterAbstractMachinesChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, fset = setFilterAbstractMachines, notify = filterAbstractMachinesChanged)
|
||||
def filterAbstractMachines(self) -> Optional[bool]:
|
||||
"""
|
||||
Weather we include abstract printers, non-abstract printers or both
|
||||
|
||||
if this is set to None both abstract and non-abstract printers will be included in the list
|
||||
set to True will only include abstract printers
|
||||
set to False will only inclde non-abstract printers
|
||||
"""
|
||||
return self._filter_abstract_machines
|
||||
|
||||
def _onContainerChanged(self, container) -> None:
|
||||
"""Handler for container added/removed events from registry"""
|
||||
|
||||
@ -130,6 +148,10 @@ class GlobalStacksModel(ListModel):
|
||||
if self._filter_online_only and not is_online:
|
||||
continue
|
||||
|
||||
is_abstract_machine = parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False))
|
||||
if self._filter_abstract_machines is not None and self._filter_abstract_machines is not is_abstract_machine:
|
||||
continue
|
||||
|
||||
capabilities = set(container_stack.getMetaDataEntry(META_CAPABILITIES, "").split(","))
|
||||
if set(self._filter_capabilities) - capabilities: # Not all required capabilities are met.
|
||||
continue
|
||||
|
@ -5,12 +5,16 @@
|
||||
# online cloud connected printers are represented within this ListModel. Additional information such as the number of
|
||||
# connected printers for each printer type is gathered.
|
||||
|
||||
from PyQt6.QtCore import Qt, QTimer
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Settings.ContainerStack import ContainerStack
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Util import parseBool
|
||||
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
||||
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
@ -23,11 +27,14 @@ class MachineListModel(ListModel):
|
||||
MetaDataRole = Qt.ItemDataRole.UserRole + 4
|
||||
IsOnlineRole = Qt.ItemDataRole.UserRole + 5
|
||||
MachineCountRole = Qt.ItemDataRole.UserRole + 6
|
||||
IsAbstractMachine = Qt.ItemDataRole.UserRole + 7
|
||||
IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7
|
||||
ComponentTypeRole = Qt.ItemDataRole.UserRole + 8
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
def __init__(self, parent: Optional[QObject] = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._show_cloud_printers = False
|
||||
|
||||
self._catalog = i18nCatalog("cura")
|
||||
|
||||
self.addRoleName(self.NameRole, "name")
|
||||
@ -36,7 +43,8 @@ class MachineListModel(ListModel):
|
||||
self.addRoleName(self.MetaDataRole, "metadata")
|
||||
self.addRoleName(self.IsOnlineRole, "isOnline")
|
||||
self.addRoleName(self.MachineCountRole, "machineCount")
|
||||
self.addRoleName(self.IsAbstractMachine, "isAbstractMachine")
|
||||
self.addRoleName(self.IsAbstractMachineRole, "isAbstractMachine")
|
||||
self.addRoleName(self.ComponentTypeRole, "componentType")
|
||||
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(200)
|
||||
@ -49,7 +57,19 @@ class MachineListModel(ListModel):
|
||||
CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
|
||||
self._updateDelayed()
|
||||
|
||||
def _onContainerChanged(self, container) -> None:
|
||||
showCloudPrintersChanged = pyqtSignal(bool)
|
||||
|
||||
@pyqtProperty(bool, notify=showCloudPrintersChanged)
|
||||
def showCloudPrinters(self) -> bool:
|
||||
return self._show_cloud_printers
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def setShowCloudPrinters(self, show_cloud_printers: bool) -> None:
|
||||
self._show_cloud_printers = show_cloud_printers
|
||||
self._updateDelayed()
|
||||
self.showCloudPrintersChanged.emit(show_cloud_printers)
|
||||
|
||||
def _onContainerChanged(self, container: ContainerInterface) -> None:
|
||||
"""Handler for container added/removed events from registry"""
|
||||
|
||||
# We only need to update when the added / removed container GlobalStack
|
||||
@ -60,40 +80,63 @@ class MachineListModel(ListModel):
|
||||
self._change_timer.start()
|
||||
|
||||
def _update(self) -> None:
|
||||
self.setItems([]) # Clear items
|
||||
self.clear()
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
machines_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
|
||||
|
||||
abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True")
|
||||
abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True)
|
||||
|
||||
for abstract_machine in abstract_machine_stacks:
|
||||
definition_id = abstract_machine.definition.getId()
|
||||
from cura.CuraApplication import CuraApplication
|
||||
machines_manager = CuraApplication.getInstance().getMachineManager()
|
||||
online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True)
|
||||
|
||||
# Create a list item for abstract machine
|
||||
self.addItem(abstract_machine, len(online_machine_stacks))
|
||||
online_machine_stacks = list(filter(lambda machine: machine.hasNetworkedConnection(), online_machine_stacks))
|
||||
|
||||
other_machine_stacks.remove(abstract_machine)
|
||||
if abstract_machine in online_machine_stacks:
|
||||
online_machine_stacks.remove(abstract_machine)
|
||||
|
||||
# Create a list item for abstract machine
|
||||
self.addItem(abstract_machine, True, len(online_machine_stacks))
|
||||
|
||||
# Create list of machines that are children of the abstract machine
|
||||
for stack in online_machine_stacks:
|
||||
self.addItem(stack)
|
||||
if self._show_cloud_printers:
|
||||
self.addItem(stack, True)
|
||||
# Remove this machine from the other stack list
|
||||
other_machine_stacks.remove(stack)
|
||||
if stack in other_machine_stacks:
|
||||
other_machine_stacks.remove(stack)
|
||||
|
||||
if len(abstract_machine_stacks) > 0:
|
||||
if self._show_cloud_printers:
|
||||
self.appendItem({"componentType": "HIDE_BUTTON",
|
||||
"isOnline": True,
|
||||
"isAbstractMachine": False,
|
||||
"machineCount": 0
|
||||
})
|
||||
else:
|
||||
self.appendItem({"componentType": "SHOW_BUTTON",
|
||||
"isOnline": True,
|
||||
"isAbstractMachine": False,
|
||||
"machineCount": 0
|
||||
})
|
||||
|
||||
for stack in other_machine_stacks:
|
||||
self.addItem(stack)
|
||||
self.addItem(stack, False)
|
||||
|
||||
def addItem(self, container_stack: ContainerStack, machine_count: int = 0) -> None:
|
||||
def addItem(self, container_stack: ContainerStack, is_online: bool, machine_count: int = 0) -> None:
|
||||
if parseBool(container_stack.getMetaDataEntry("hidden", False)):
|
||||
return
|
||||
|
||||
self.appendItem({"name": container_stack.getName(),
|
||||
self.appendItem({
|
||||
"componentType": "MACHINE",
|
||||
"name": container_stack.getName(),
|
||||
"id": container_stack.getId(),
|
||||
"metadata": container_stack.getMetaData().copy(),
|
||||
"isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)),
|
||||
"isOnline": is_online,
|
||||
"isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)),
|
||||
"machineCount": machine_count,
|
||||
})
|
||||
})
|
||||
|
@ -50,8 +50,13 @@ class PlatformPhysics:
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
app_instance = Application.getInstance()
|
||||
app_preferences = app_instance.getPreferences()
|
||||
app_automatic_drop_down = app_preferences.getValue("physics/automatic_drop_down")
|
||||
app_automatic_push_free = app_preferences.getValue("physics/automatic_push_free")
|
||||
|
||||
root = self._controller.getScene().getRoot()
|
||||
build_volume = Application.getInstance().getBuildVolume()
|
||||
build_volume = app_instance.getBuildVolume()
|
||||
build_volume.updateNodeBoundaryCheck()
|
||||
|
||||
# Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the
|
||||
@ -75,7 +80,7 @@ class PlatformPhysics:
|
||||
# Move it downwards if bottom is above platform
|
||||
move_vector = Vector()
|
||||
|
||||
if Application.getInstance().getPreferences().getValue("physics/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 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
|
||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
||||
|
||||
@ -84,7 +89,7 @@ class PlatformPhysics:
|
||||
node.addDecorator(ConvexHullDecorator())
|
||||
|
||||
# only push away objects if this node is a printing mesh
|
||||
if not node.callDecoration("isNonPrintingMesh") and Application.getInstance().getPreferences().getValue("physics/automatic_push_free"):
|
||||
if not node.callDecoration("isNonPrintingMesh") and app_automatic_push_free:
|
||||
# Do not move locked nodes
|
||||
if node.getSetting(SceneNodeSettings.LockPosition):
|
||||
continue
|
||||
|
@ -13,9 +13,9 @@ class ExtruderConfigurationModel(QObject):
|
||||
|
||||
def __init__(self, position: int = -1) -> None:
|
||||
super().__init__()
|
||||
self._position = position # type: int
|
||||
self._material = None # type: Optional[MaterialOutputModel]
|
||||
self._hotend_id = None # type: Optional[str]
|
||||
self._position: int = position
|
||||
self._material: Optional[MaterialOutputModel] = None
|
||||
self._hotend_id: Optional[str] = None
|
||||
|
||||
def setPosition(self, position: int) -> None:
|
||||
self._position = position
|
||||
|
@ -350,5 +350,6 @@ class PrinterOutputModel(QObject):
|
||||
self.availableConfigurationsChanged.emit()
|
||||
|
||||
def setAvailableConfigurations(self, new_configurations: List[PrinterConfigurationModel]) -> None:
|
||||
self._available_printer_configurations = new_configurations
|
||||
self.availableConfigurationsChanged.emit()
|
||||
if self._available_printer_configurations != new_configurations:
|
||||
self._available_printer_configurations = new_configurations
|
||||
self.availableConfigurationsChanged.emit()
|
||||
|
@ -50,13 +50,12 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
The assumption is made the printer is a FDM printer.
|
||||
|
||||
Note that a number of settings are marked as "final". This is because decorators
|
||||
are not inherited by children. To fix this we use the private counter part of those
|
||||
are not inherited by children. To fix this we use the private counterpart of those
|
||||
functions to actually have the implementation.
|
||||
|
||||
For all other uses it should be used in the same way as a "regular" OutputDevice.
|
||||
"""
|
||||
|
||||
|
||||
printersChanged = pyqtSignal()
|
||||
connectionStateChanged = pyqtSignal(str)
|
||||
acceptsCommandsChanged = pyqtSignal()
|
||||
@ -183,8 +182,8 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
@pyqtProperty(QObject, constant = True)
|
||||
def monitorItem(self) -> QObject:
|
||||
# Note that we specifically only check if the monitor component is created.
|
||||
# It could be that it failed to actually create the qml item! If we check if the item was created, it will try to
|
||||
# create the item (and fail) every time.
|
||||
# It could be that it failed to actually create the qml item! If we check if the item was created, it will try
|
||||
# to create the item (and fail) every time.
|
||||
if not self._monitor_component:
|
||||
self._createMonitorViewFromQML()
|
||||
return self._monitor_item
|
||||
@ -237,9 +236,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
|
||||
self.acceptsCommandsChanged.emit()
|
||||
|
||||
# Returns the unique configurations of the printers within this output device
|
||||
@pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged)
|
||||
def uniqueConfigurations(self) -> List["PrinterConfigurationModel"]:
|
||||
""" Returns the unique configurations of the printers within this output device """
|
||||
return self._unique_configurations
|
||||
|
||||
def _updateUniqueConfigurations(self) -> None:
|
||||
@ -248,7 +247,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
if printer.printerConfiguration is not None and printer.printerConfiguration.hasAnyMaterialLoaded():
|
||||
all_configurations.add(printer.printerConfiguration)
|
||||
all_configurations.update(printer.availableConfigurations)
|
||||
if None in all_configurations: # Shouldn't happen, but it does. I don't see how it could ever happen. Skip adding that configuration. List could end up empty!
|
||||
if None in all_configurations:
|
||||
# Shouldn't happen, but it does. I don't see how it could ever happen. Skip adding that configuration.
|
||||
# List could end up empty!
|
||||
Logger.log("e", "Found a broken configuration in the synced list!")
|
||||
all_configurations.remove(None)
|
||||
new_configurations = sorted(all_configurations, key = lambda config: config.printerType or "")
|
||||
@ -256,9 +257,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
self._unique_configurations = new_configurations
|
||||
self.uniqueConfigurationsChanged.emit()
|
||||
|
||||
# Returns the unique configurations of the printers within this output device
|
||||
@pyqtProperty("QStringList", notify = uniqueConfigurationsChanged)
|
||||
def uniquePrinterTypes(self) -> List[str]:
|
||||
""" Returns the unique configurations of the printers within this output device """
|
||||
return list(sorted(set([configuration.printerType or "" for configuration in self._unique_configurations])))
|
||||
|
||||
def _onPrintersChanged(self) -> None:
|
||||
|
@ -390,7 +390,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time":
|
||||
# Find the root node that's placed in the scene; the root of the mesh group.
|
||||
ancestor = self.getNode()
|
||||
while ancestor.getParent() != self._root:
|
||||
while ancestor.getParent() != self._root and ancestor.getParent() is not None:
|
||||
ancestor = ancestor.getParent()
|
||||
center = ancestor.getBoundingBox().center
|
||||
else:
|
||||
|
@ -49,7 +49,7 @@ class CuraContainerStack(ContainerStack):
|
||||
self._empty_material = cura_empty_instance_containers.empty_material_container #type: InstanceContainer
|
||||
self._empty_variant = cura_empty_instance_containers.empty_variant_container #type: InstanceContainer
|
||||
|
||||
self._containers = [self._empty_instance_container for i in range(len(_ContainerIndexes.IndexTypeMap))] #type: List[ContainerInterface]
|
||||
self._containers: List[ContainerInterface] = [self._empty_instance_container for i in _ContainerIndexes.IndexTypeMap]
|
||||
self._containers[_ContainerIndexes.QualityChanges] = self._empty_quality_changes
|
||||
self._containers[_ContainerIndexes.Quality] = self._empty_quality
|
||||
self._containers[_ContainerIndexes.Material] = self._empty_material
|
||||
|
@ -1,7 +1,9 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional
|
||||
import copy
|
||||
|
||||
from typing import Optional, cast
|
||||
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
from UM.Logger import Logger
|
||||
@ -275,41 +277,26 @@ class CuraStackBuilder:
|
||||
:return: The new Abstract Machine or None if an error occurred.
|
||||
"""
|
||||
abstract_machine_id = f"{definition_id}_abstract_machine"
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
application = CuraApplication.getInstance()
|
||||
registry = application.getContainerRegistry()
|
||||
container_tree = ContainerTree.getInstance()
|
||||
|
||||
if registry.findContainerStacks(is_abstract_machine = "True", id = abstract_machine_id):
|
||||
# This abstract machine already exists
|
||||
abstract_machines = registry.findContainerStacks(id = abstract_machine_id)
|
||||
if abstract_machines:
|
||||
return cast(GlobalStack, abstract_machines[0])
|
||||
definitions = registry.findDefinitionContainers(id=definition_id)
|
||||
|
||||
name = ""
|
||||
|
||||
if definitions:
|
||||
name = definitions[0].getName()
|
||||
stack = cls.createMachine(abstract_machine_id, definition_id, show_warning_message=False)
|
||||
if not stack:
|
||||
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.setName(name)
|
||||
|
||||
stack = GlobalStack(abstract_machine_id)
|
||||
stack.setMetaDataEntry("is_abstract_machine", True)
|
||||
stack.setMetaDataEntry("is_online", True)
|
||||
stack.setDefinition(machine_definition)
|
||||
cls.createUserContainer(
|
||||
name,
|
||||
machine_definition,
|
||||
stack,
|
||||
application.empty_variant_container,
|
||||
application.empty_material_container,
|
||||
machine_node.preferredGlobalQuality().container,
|
||||
)
|
||||
stack.setMetaDataEntry("is_abstract_machine", True)
|
||||
stack.setMetaDataEntry("is_online", True)
|
||||
|
||||
stack.setName(name)
|
||||
|
||||
registry.addContainer(stack)
|
||||
|
||||
return stack
|
||||
return stack
|
@ -90,7 +90,6 @@ class GlobalStack(CuraContainerStack):
|
||||
@pyqtProperty("QVariantList", notify=configuredConnectionTypesChanged)
|
||||
def configuredConnectionTypes(self) -> List[int]:
|
||||
"""The configured connection types can be used to find out if the global
|
||||
|
||||
stack is configured to be connected with a printer, without having to
|
||||
know all the details as to how this is exactly done (and without
|
||||
actually setting the stack to be active).
|
||||
@ -292,7 +291,6 @@ class GlobalStack(CuraContainerStack):
|
||||
for extruder_train in extruder_trains:
|
||||
extruder_position = extruder_train.getMetaDataEntry("position")
|
||||
extruder_check_position.add(extruder_position)
|
||||
|
||||
for check_position in range(machine_extruder_count):
|
||||
if str(check_position) not in extruder_check_position:
|
||||
return False
|
||||
@ -349,6 +347,12 @@ class GlobalStack(CuraContainerStack):
|
||||
nameChanged = pyqtSignal()
|
||||
name = pyqtProperty(str, fget=getName, fset=setName, notify=nameChanged)
|
||||
|
||||
def hasNetworkedConnection(self) -> bool:
|
||||
has_connection = False
|
||||
for connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value]:
|
||||
has_connection |= connection_type in self.configuredConnectionTypes
|
||||
return has_connection
|
||||
|
||||
## private:
|
||||
global_stack_mime = MimeType(
|
||||
name = "application/x-cura-globalstack",
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import time
|
||||
@ -99,7 +99,7 @@ class MachineManager(QObject):
|
||||
|
||||
self._application.getPreferences().addPreference("cura/active_machine", "")
|
||||
|
||||
self._printer_output_devices = [] # type: List[PrinterOutputDevice]
|
||||
self._printer_output_devices: List[PrinterOutputDevice] = []
|
||||
self._application.getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
# There might already be some output devices by the time the signal is connected
|
||||
self._onOutputDevicesChanged()
|
||||
@ -112,7 +112,7 @@ class MachineManager(QObject):
|
||||
|
||||
self._application.callLater(self.setInitialActiveMachine)
|
||||
|
||||
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) # type: List[InstanceContainer]
|
||||
containers: List[InstanceContainer] = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId)
|
||||
if containers:
|
||||
containers[0].nameChanged.connect(self._onMaterialNameChanged)
|
||||
|
||||
@ -359,6 +359,7 @@ class MachineManager(QObject):
|
||||
extruder_manager = ExtruderManager.getInstance()
|
||||
extruder_manager.fixSingleExtrusionMachineExtruderDefinition(global_stack)
|
||||
if not global_stack.isValid():
|
||||
Logger.warning("Global stack isn't valid, adding it to faulty container list")
|
||||
# Mark global stack as invalid
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(global_stack.getId())
|
||||
return # We're done here
|
||||
@ -530,6 +531,10 @@ class MachineManager(QObject):
|
||||
def printerConnected(self) -> bool:
|
||||
return bool(self._printer_output_devices)
|
||||
|
||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||
def activeMachineIsAbstractCloudPrinter(self) -> bool:
|
||||
return len(self._printer_output_devices) == 1 and self._printer_output_devices[0].__class__.__name__ == "AbstractCloudOutputDevice"
|
||||
|
||||
@pyqtProperty(bool, notify = printerConnectedStatusChanged)
|
||||
def activeMachineIsGroup(self) -> bool:
|
||||
if self.activeMachine is None:
|
||||
@ -899,7 +904,7 @@ class MachineManager(QObject):
|
||||
|
||||
if self._global_container_stack is None \
|
||||
or self._global_container_stack.getProperty(setting_key, "value") == new_value \
|
||||
or self.numberExtrudersEnabled < 2:
|
||||
or self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value") < 2:
|
||||
return
|
||||
|
||||
user_changes_container = self._global_container_stack.userChanges
|
||||
@ -1004,7 +1009,7 @@ class MachineManager(QObject):
|
||||
for position, extruder in enumerate(self._global_container_stack.extruderList):
|
||||
if extruder.isEnabled and int(position) < machine_extruder_count:
|
||||
extruder_count += 1
|
||||
if self.numberExtrudersEnabled != extruder_count:
|
||||
if self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value") != extruder_count:
|
||||
definition_changes_container.setProperty("extruders_enabled_count", "value", extruder_count)
|
||||
self.numberExtrudersEnabledChanged.emit()
|
||||
|
||||
|
@ -23,9 +23,9 @@ catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
class PrintInformation(QObject):
|
||||
"""A class for processing and the print times per build plate as well as managing the job name
|
||||
"""A class for processing the print times per build plate and managing the job name
|
||||
|
||||
This class also mangles the current machine name and the filename of the first loaded mesh into a job name.
|
||||
This class also combines the current machine name and the filename of the first loaded mesh into a job name.
|
||||
This job name is requested by the JobSpecs qml file.
|
||||
"""
|
||||
|
||||
@ -186,7 +186,7 @@ class PrintInformation(QObject):
|
||||
|
||||
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
||||
duration.setDuration(0)
|
||||
Logger.log("w", "Received NaN for print duration message")
|
||||
Logger.warning("Received NaN for print duration message")
|
||||
continue
|
||||
|
||||
total_estimated_time += time
|
||||
@ -368,7 +368,7 @@ class PrintInformation(QObject):
|
||||
mime_type = MimeTypeDatabase.getMimeTypeForFile(name)
|
||||
data = mime_type.stripExtension(name)
|
||||
except MimeTypeNotFoundError:
|
||||
Logger.log("w", "Unsupported Mime Type Database file extension %s", name)
|
||||
Logger.warning(f"Unsupported Mime Type Database file extension {name}")
|
||||
|
||||
if data is not None and check_name is not None:
|
||||
self._base_name = data
|
||||
@ -392,7 +392,7 @@ class PrintInformation(QObject):
|
||||
return self._base_name
|
||||
|
||||
def _defineAbbreviatedMachineName(self) -> None:
|
||||
"""Created an acronym-like abbreviated machine name from the currently active machine name.
|
||||
"""Creates an abbreviated machine name from the currently active machine name.
|
||||
|
||||
Called each time the global stack is switched.
|
||||
"""
|
||||
@ -446,7 +446,7 @@ class PrintInformation(QObject):
|
||||
self.setToZeroPrintInformation(self._active_build_plate)
|
||||
|
||||
def _onOutputStart(self, output_device: OutputDevice) -> None:
|
||||
"""If this is the sort of output 'device' (like local or online file storage, rather than a printer),
|
||||
"""If this is a sort of output 'device' (like local or online file storage, rather than a printer),
|
||||
the user could have altered the file-name, and thus the project name should be altered as well."""
|
||||
if isinstance(output_device, ProjectOutputDevice):
|
||||
new_name = output_device.getLastOutputName()
|
||||
|
@ -62,15 +62,21 @@ class WhatsNewPagesModel(WelcomePagesModel):
|
||||
|
||||
def initialize(self) -> None:
|
||||
self._pages = []
|
||||
self._pages.append({"id": "whats_new",
|
||||
"page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"),
|
||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Skip"),
|
||||
"next_page_id": "changelog"
|
||||
})
|
||||
self._pages.append({"id": "changelog",
|
||||
"page_url": self._getBuiltinWelcomePagePath("ChangelogContent.qml"),
|
||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Close"),
|
||||
})
|
||||
try:
|
||||
self._pages.append({"id": "whats_new",
|
||||
"page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"),
|
||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Skip"),
|
||||
"next_page_id": "changelog"
|
||||
})
|
||||
except FileNotFoundError:
|
||||
Logger.warning("Unable to find what's new page")
|
||||
try:
|
||||
self._pages.append({"id": "changelog",
|
||||
"page_url": self._getBuiltinWelcomePagePath("ChangelogContent.qml"),
|
||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Close"),
|
||||
})
|
||||
except FileNotFoundError:
|
||||
Logger.warning("Unable to find changelog page")
|
||||
self.setItems(self._pages)
|
||||
|
||||
images, max_image = WhatsNewPagesModel._collectOrdinalFiles(Resources.Images, WhatsNewPagesModel.image_formats)
|
||||
|
@ -664,10 +664,22 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
|
||||
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
||||
|
||||
# Create a shadow copy of the preferences (we don't want all of the preferences, but we do want to re-use its
|
||||
# Create a shadow copy of the preferences (We don't want all of the preferences, but we do want to re-use its
|
||||
# parsing code.
|
||||
temp_preferences = Preferences()
|
||||
serialized = archive.open("Cura/preferences.cfg").read().decode("utf-8")
|
||||
try:
|
||||
serialized = archive.open("Cura/preferences.cfg").read().decode("utf-8")
|
||||
except KeyError:
|
||||
# If there is no preferences file, it's not a workspace, so notify user of failure.
|
||||
Logger.log("w", "File %s is not a valid workspace.", file_name)
|
||||
message = Message(i18n_catalog.i18nc("@info:error Don't translate the XML tags <filename> or <message>!",
|
||||
"Project file <filename>{0}</filename> is corrupt: <message>{1}</message>.",
|
||||
file_name, str(e)),
|
||||
title=i18n_catalog.i18nc("@info:title", "Can't Open Project File"),
|
||||
message_type=Message.MessageType.ERROR)
|
||||
message.show()
|
||||
self.setWorkspaceName("")
|
||||
return [], {}
|
||||
temp_preferences.deserialize(serialized)
|
||||
|
||||
# Copy a number of settings from the temp preferences to the global
|
||||
|
@ -156,6 +156,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
|
||||
"connection_type",
|
||||
"capabilities",
|
||||
"octoprint_api_key",
|
||||
"is_online",
|
||||
}
|
||||
serialized_data = container.serialize(ignored_metadata_keys = ignore_keys)
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import re # For escaping characters in the settings.
|
||||
import json
|
||||
import copy
|
||||
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Logger import Logger
|
||||
@ -12,6 +11,8 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.Machines.ContainerTree import ContainerTree
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
@ -96,25 +97,6 @@ class GCodeWriter(MeshWriter):
|
||||
self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting."))
|
||||
return False
|
||||
|
||||
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
|
||||
"""Create a new container with container 2 as base and container 1 written over it."""
|
||||
|
||||
flat_container = InstanceContainer(instance_container2.getName())
|
||||
|
||||
# The metadata includes id, name and definition
|
||||
flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData()))
|
||||
|
||||
if instance_container1.getDefinition():
|
||||
flat_container.setDefinition(instance_container1.getDefinition().getId())
|
||||
|
||||
for key in instance_container2.getAllKeys():
|
||||
flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))
|
||||
|
||||
for key in instance_container1.getAllKeys():
|
||||
flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value"))
|
||||
|
||||
return flat_container
|
||||
|
||||
def _serialiseSettings(self, stack):
|
||||
"""Serialises a container stack to prepare it for writing at the end of the g-code.
|
||||
|
||||
@ -145,22 +127,22 @@ class GCodeWriter(MeshWriter):
|
||||
container_with_profile.setDefinition(machine_definition_id_for_quality)
|
||||
container_with_profile.setMetaDataEntry("setting_version", stack.quality.getMetaDataEntry("setting_version"))
|
||||
|
||||
flat_global_container = self._createFlattenedContainerInstance(stack.userChanges, container_with_profile)
|
||||
merged_global_instance_container = InstanceContainer.createMergedInstanceContainer(stack.userChanges, container_with_profile)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_global_container.getMetaDataEntry("type", None) is None:
|
||||
flat_global_container.setMetaDataEntry("type", "quality_changes")
|
||||
if merged_global_instance_container.getMetaDataEntry("type", None) is None:
|
||||
merged_global_instance_container.setMetaDataEntry("type", "quality_changes")
|
||||
|
||||
# Ensure that quality_type is set. (Can happen if we have empty quality changes).
|
||||
if flat_global_container.getMetaDataEntry("quality_type", None) is None:
|
||||
flat_global_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
if merged_global_instance_container.getMetaDataEntry("quality_type", None) is None:
|
||||
merged_global_instance_container.setMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal"))
|
||||
|
||||
# Get the machine definition ID for quality profiles
|
||||
flat_global_container.setMetaDataEntry("definition", machine_definition_id_for_quality)
|
||||
merged_global_instance_container.setMetaDataEntry("definition", machine_definition_id_for_quality)
|
||||
|
||||
serialized = flat_global_container.serialize()
|
||||
serialized = merged_global_instance_container.serialize()
|
||||
data = {"global_quality": serialized}
|
||||
|
||||
all_setting_keys = flat_global_container.getAllKeys()
|
||||
all_setting_keys = merged_global_instance_container.getAllKeys()
|
||||
for extruder in stack.extruderList:
|
||||
extruder_quality = extruder.qualityChanges
|
||||
if extruder_quality.getId() == "empty_quality_changes":
|
||||
@ -174,7 +156,7 @@ class GCodeWriter(MeshWriter):
|
||||
extruder_quality.setDefinition(machine_definition_id_for_quality)
|
||||
extruder_quality.setMetaDataEntry("setting_version", stack.quality.getMetaDataEntry("setting_version"))
|
||||
|
||||
flat_extruder_quality = self._createFlattenedContainerInstance(extruder.userChanges, extruder_quality)
|
||||
flat_extruder_quality = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder_quality)
|
||||
# If the quality changes is not set, we need to set type manually
|
||||
if flat_extruder_quality.getMetaDataEntry("type", None) is None:
|
||||
flat_extruder_quality.setMetaDataEntry("type", "quality_changes")
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -114,6 +114,7 @@ Rectangle
|
||||
font: UM.Theme.getFont("medium")
|
||||
width: contentWidth
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
anchors
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
from typing import cast, List, Dict
|
||||
|
||||
from Charon.VirtualFile import VirtualFile # To open UFP files.
|
||||
@ -10,6 +10,7 @@ from io import StringIO # For converting g-code to bytes.
|
||||
|
||||
from PyQt6.QtCore import QBuffer
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement.
|
||||
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
@ -17,12 +18,16 @@ from UM.PluginRegistry import PluginRegistry # To get the g-code writer.
|
||||
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
METADATA_OBJECTS_PATH = "metadata/objects"
|
||||
SLICE_METADATA_PATH = "Cura/slicemetadata.json"
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
@ -67,7 +72,21 @@ class UFPWriter(MeshWriter):
|
||||
try:
|
||||
gcode = archive.getStream("/3D/model.gcode")
|
||||
gcode.write(gcode_textio.getvalue().encode("UTF-8"))
|
||||
archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")
|
||||
archive.addRelation(virtual_path = "/3D/model.gcode",
|
||||
relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")
|
||||
except EnvironmentError as e:
|
||||
error_msg = catalog.i18nc("@info:error", "Can't write to UFP file:") + " " + str(e)
|
||||
self.setInformation(error_msg)
|
||||
Logger.error(error_msg)
|
||||
return False
|
||||
|
||||
# Write settings
|
||||
try:
|
||||
archive.addContentType(extension="json", mime_type="application/json")
|
||||
setting_textio = StringIO()
|
||||
json.dump(self._getSliceMetadata(), setting_textio, separators=(", ", ": "), indent=4)
|
||||
steam = archive.getStream(SLICE_METADATA_PATH)
|
||||
steam.write(setting_textio.getvalue().encode("UTF-8"))
|
||||
except EnvironmentError as e:
|
||||
error_msg = catalog.i18nc("@info:error", "Can't write to UFP file:") + " " + str(e)
|
||||
self.setInformation(error_msg)
|
||||
@ -190,3 +209,47 @@ class UFPWriter(MeshWriter):
|
||||
return [{"name": item.getName()}
|
||||
for item in DepthFirstIterator(node)
|
||||
if item.getMeshData() is not None and not item.callDecoration("isNonPrintingMesh")]
|
||||
|
||||
def _getSliceMetadata(self) -> Dict[str, Dict[str, Dict[str, str]]]:
|
||||
"""Get all changed settings and all settings. For each extruder and the global stack"""
|
||||
print_information = CuraApplication.getInstance().getPrintInformation()
|
||||
settings = {
|
||||
"material": {
|
||||
"length": print_information.materialLengths,
|
||||
"weight": print_information.materialWeights,
|
||||
"cost": print_information.materialCosts,
|
||||
},
|
||||
"global": {
|
||||
"changes": {},
|
||||
"all_settings": {},
|
||||
}
|
||||
}
|
||||
|
||||
global_stack = cast(GlobalStack, Application.getInstance().getGlobalContainerStack())
|
||||
|
||||
# Add global user or quality changes
|
||||
global_flattened_changes = InstanceContainer.createMergedInstanceContainer(global_stack.userChanges, global_stack.qualityChanges)
|
||||
for setting in global_flattened_changes.getAllKeys():
|
||||
settings["global"]["changes"][setting] = global_flattened_changes.getProperty(setting, "value")
|
||||
|
||||
# Get global all settings values without user or quality changes
|
||||
for setting in global_stack.getAllKeys():
|
||||
settings["global"]["all_settings"][setting] = global_stack.getProperty(setting, "value")
|
||||
|
||||
for i, extruder in enumerate(global_stack.extruderList):
|
||||
# Add extruder fields to settings dictionary
|
||||
settings[f"extruder_{i}"] = {
|
||||
"changes": {},
|
||||
"all_settings": {},
|
||||
}
|
||||
|
||||
# Add extruder user or quality changes
|
||||
extruder_flattened_changes = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder.qualityChanges)
|
||||
for setting in extruder_flattened_changes.getAllKeys():
|
||||
settings[f"extruder_{i}"]["changes"][setting] = extruder_flattened_changes.getProperty(setting, "value")
|
||||
|
||||
# Get extruder all settings values without user or quality changes
|
||||
for setting in extruder.getAllKeys():
|
||||
settings[f"extruder_{i}"]["all_settings"][setting] = extruder.getProperty(setting, "value")
|
||||
|
||||
return settings
|
||||
|
@ -153,7 +153,7 @@ Item
|
||||
|
||||
MonitorPrinterPill
|
||||
{
|
||||
text: printJob.configuration.printerType
|
||||
text: printJob ? printJob.configuration.printerType : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,8 +172,15 @@ Item
|
||||
{
|
||||
id: printerConfiguration
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
buildplate: catalog.i18nc("@label", "Glass")
|
||||
configurations: base.printJob.configuration.extruderConfigurations
|
||||
buildplate: {
|
||||
switch (printJob.assignedPrinter.buildplate) {
|
||||
case "glass":
|
||||
return catalog.i18nc("@label", "Glass");
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
configurations: base.printJob ? base.printJob.configuration.extruderConfigurations : null
|
||||
height: Math.round(72 * screenScaleFactor) // TODO: Theme!
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,14 @@ Item
|
||||
{
|
||||
id: printerConfiguration
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
buildplate: printer ? catalog.i18nc("@label", "Glass") : null // 'Glass' as a default
|
||||
buildplate: {
|
||||
switch (printer.buildplate) {
|
||||
case "glass":
|
||||
return catalog.i18nc("@label", "Glass");
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
configurations:
|
||||
{
|
||||
var configs = []
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2019 Ultimaker B.V.
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import UM 1.3 as UM
|
||||
import QtQuick 2.15
|
||||
import UM 1.5 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
// This is the root component for the monitor stage.
|
||||
@ -37,6 +37,7 @@ Component
|
||||
Item
|
||||
{
|
||||
id: printers
|
||||
visible: !Cura.MachineManager.activeMachineIsAbstractCloudPrinter
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
@ -69,14 +70,66 @@ Component
|
||||
top: printers.bottom
|
||||
topMargin: 48 * screenScaleFactor // TODO: Theme!
|
||||
}
|
||||
visible: OutputDevice.supportsPrintJobQueue && OutputDevice.canReadPrintJobs
|
||||
visible: OutputDevice.supportsPrintJobQueue && OutputDevice.canReadPrintJobs && !Cura.MachineManager.activeMachineIsAbstractCloudPrinter
|
||||
}
|
||||
|
||||
PrinterVideoStream
|
||||
{
|
||||
anchors.fill: parent
|
||||
cameraUrl: OutputDevice.activeCameraUrl
|
||||
visible: OutputDevice.activeCameraUrl != ""
|
||||
visible: OutputDevice.activeCameraUrl != "" && !Cura.MachineManager.activeMachineIsAbstractCloudPrinter
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: sendToFactoryCard
|
||||
|
||||
visible: Cura.MachineManager.activeMachineIsAbstractCloudPrinter
|
||||
|
||||
color: UM.Theme.getColor("monitor_stage_background")
|
||||
height: childrenRect.height + UM.Theme.getSize("default_margin").height * 2
|
||||
width: childrenRect.width + UM.Theme.getSize("wide_margin").width * 2
|
||||
anchors
|
||||
{
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: UM.Theme.getSize("wide_margin").height * screenScaleFactor * 2
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: UM.Theme.getSize("wide_margin").height
|
||||
padding: UM.Theme.getSize("default_margin").width
|
||||
topPadding: 0
|
||||
|
||||
Image
|
||||
{
|
||||
id: sendToFactoryImage
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
source: UM.Theme.getImage("cura_connected_printers")
|
||||
}
|
||||
|
||||
UM.Label
|
||||
{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@info", "Monitor your printers from everywhere using Ultimaker Digital Factory")
|
||||
font: UM.Theme.getFont("medium")
|
||||
width: sendToFactoryImage.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Cura.PrimaryButton
|
||||
{
|
||||
id: sendToFactoryButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@button", "View printers in Digital Factory")
|
||||
onClicked: Qt.openUrlExternally("https://digitalfactory.ultimaker.com/app/print-jobs?utm_source=cura&utm_medium=software&utm_campaign=monitor-view-cloud-printer-type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
from time import time
|
||||
from typing import Callable, List, Optional
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSlot
|
||||
from PyQt6.QtNetwork import QNetworkReply
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.FileHandler.FileHandler import FileHandler
|
||||
from UM.Resources import Resources
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState
|
||||
from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
||||
from .CloudApiClient import CloudApiClient
|
||||
from ..Models.Http.CloudClusterWithConfigResponse import CloudClusterWithConfigResponse
|
||||
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
|
||||
|
||||
I18N_CATALOG = i18nCatalog("cura")
|
||||
|
||||
|
||||
class AbstractCloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
API_CHECK_INTERVAL = 10.0 # seconds
|
||||
|
||||
def __init__(self, api_client: CloudApiClient, printer_type: str, request_write_callback: Callable, refresh_callback: Callable, parent: QObject = None) -> None:
|
||||
|
||||
self._api = api_client
|
||||
properties = {b"printer_type": printer_type.encode()}
|
||||
super().__init__(
|
||||
device_id=f"ABSTRACT_{printer_type}",
|
||||
address="",
|
||||
connection_type=ConnectionType.CloudConnection,
|
||||
properties=properties,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._on_print_dialog: Optional[QObject] = None
|
||||
self._nodes: List[SceneNode] = None
|
||||
self._request_write_callback = request_write_callback
|
||||
self._refresh_callback = refresh_callback
|
||||
|
||||
self._setInterfaceElements()
|
||||
|
||||
def connect(self) -> None:
|
||||
"""Connects this device."""
|
||||
|
||||
if self.isConnected():
|
||||
return
|
||||
Logger.log("i", "Attempting to connect AbstractCloudOutputDevice %s", self.key)
|
||||
super().connect()
|
||||
|
||||
self._update()
|
||||
|
||||
def disconnect(self) -> None:
|
||||
"""Disconnects the device"""
|
||||
|
||||
if not self.isConnected():
|
||||
return
|
||||
super().disconnect()
|
||||
|
||||
def _update(self) -> None:
|
||||
"""Called when the network data should be updated."""
|
||||
|
||||
super()._update()
|
||||
if time() - self._time_of_last_request < self.API_CHECK_INTERVAL:
|
||||
return # avoid calling the cloud too often
|
||||
self._time_of_last_request = time()
|
||||
if self._api.account.isLoggedIn:
|
||||
self.setAuthenticationState(AuthState.Authenticated)
|
||||
self._last_request_time = time()
|
||||
self._api.getClustersByMachineType(self.printerType, self._onCompleted, self._onError)
|
||||
else:
|
||||
self.setAuthenticationState(AuthState.NotAuthenticated)
|
||||
|
||||
def _setInterfaceElements(self) -> None:
|
||||
"""Set all the interface elements and texts for this output device."""
|
||||
|
||||
self.setPriority(2) # Make sure we end up below the local networking and above 'save to file'.
|
||||
self.setShortDescription(I18N_CATALOG.i18nc("@action:button", "Print via cloud"))
|
||||
self.setDescription(I18N_CATALOG.i18nc("@properties:tooltip", "Print via cloud"))
|
||||
self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via cloud"))
|
||||
|
||||
def _onCompleted(self, clusters: List[CloudClusterWithConfigResponse]) -> None:
|
||||
self._responseReceived()
|
||||
|
||||
all_configurations = []
|
||||
for resp in clusters:
|
||||
if resp.configuration is not None:
|
||||
# Usually when the printer is offline, it doesn't have a configuration...
|
||||
all_configurations.append(resp.configuration)
|
||||
self._updatePrinters(all_configurations)
|
||||
|
||||
def _onError(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
|
||||
Logger.log("w", f"Failed to get clusters by machine type: {str(error)}.")
|
||||
|
||||
@pyqtSlot(str)
|
||||
def printerSelected(self, unique_id: str):
|
||||
self._request_write_callback(unique_id, self._nodes)
|
||||
if self._on_print_dialog:
|
||||
self._on_print_dialog.close()
|
||||
|
||||
@pyqtSlot()
|
||||
def refresh(self):
|
||||
self._refresh_callback()
|
||||
self._update()
|
||||
|
||||
def _openChoosePrinterDialog(self) -> None:
|
||||
if self._on_print_dialog is None:
|
||||
qml_path = Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Dialogs", "ChoosePrinterDialog.qml")
|
||||
self._on_print_dialog = CuraApplication.getInstance().createQmlComponent(qml_path, {})
|
||||
if self._on_print_dialog is None: # Failed to load QML file.
|
||||
return
|
||||
self._on_print_dialog.setProperty("manager", self)
|
||||
self._on_print_dialog.show()
|
||||
|
||||
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs) -> None:
|
||||
if not nodes or len(nodes) < 1:
|
||||
Logger.log("w", "Nothing to print.")
|
||||
return
|
||||
self._nodes = nodes
|
||||
self._openChoosePrinterDialog()
|
@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import json
|
||||
import urllib.parse
|
||||
from json import JSONDecodeError
|
||||
from time import time
|
||||
from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast
|
||||
@ -17,6 +18,7 @@ from cura.UltimakerCloud import UltimakerCloudConstants
|
||||
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
|
||||
from .ToolPathUploader import ToolPathUploader
|
||||
from ..Models.BaseModel import BaseModel
|
||||
from ..Models.Http.CloudClusterWithConfigResponse import CloudClusterWithConfigResponse
|
||||
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||
from ..Models.Http.CloudClusterStatus import CloudClusterStatus
|
||||
from ..Models.Http.CloudError import CloudError
|
||||
@ -48,7 +50,6 @@ class CloudApiClient:
|
||||
"""Initializes a new cloud API client.
|
||||
|
||||
:param app:
|
||||
:param account: The user's account object
|
||||
:param on_error: The callback to be called whenever we receive errors from the server.
|
||||
"""
|
||||
super().__init__()
|
||||
@ -57,12 +58,11 @@ class CloudApiClient:
|
||||
self._scope = JsonDecoratorScope(UltimakerCloudScope(app))
|
||||
self._http = HttpRequestManager.getInstance()
|
||||
self._on_error = on_error
|
||||
self._upload = None # type: Optional[ToolPathUploader]
|
||||
self._upload: Optional[ToolPathUploader] = None
|
||||
|
||||
@property
|
||||
def account(self) -> Account:
|
||||
"""Gets the account used for the API."""
|
||||
|
||||
return self._account
|
||||
|
||||
def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any], failed: Callable) -> None:
|
||||
@ -71,13 +71,31 @@ class CloudApiClient:
|
||||
:param on_finished: The function to be called after the result is parsed.
|
||||
"""
|
||||
|
||||
url = "{}/clusters?status=active".format(self.CLUSTER_API_ROOT)
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters?status=active"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(on_finished, CloudClusterResponse, failed),
|
||||
error_callback = failed,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def getClustersByMachineType(self, machine_type, on_finished: Callable[[List[CloudClusterWithConfigResponse]], Any], failed: Callable) -> None:
|
||||
# HACK: There is something weird going on with the API, as it reports printer types in formats like
|
||||
# "ultimaker_s3", but wants "Ultimaker S3" when using the machine_variant filter query. So we need to do some
|
||||
# conversion!
|
||||
|
||||
machine_type = machine_type.replace("_plus", "+")
|
||||
machine_type = machine_type.replace("_", " ")
|
||||
machine_type = machine_type.replace("ultimaker", "ultimaker ")
|
||||
machine_type = machine_type.replace(" ", " ")
|
||||
machine_type = machine_type.title()
|
||||
machine_type = urllib.parse.quote_plus(machine_type)
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters?machine_variant={machine_type}"
|
||||
self._http.get(url,
|
||||
scope=self._scope,
|
||||
callback=self._parseCallback(on_finished, CloudClusterWithConfigResponse, failed),
|
||||
error_callback=failed,
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None:
|
||||
"""Retrieves the status of the given cluster.
|
||||
|
||||
@ -85,7 +103,7 @@ class CloudApiClient:
|
||||
:param on_finished: The function to be called after the result is parsed.
|
||||
"""
|
||||
|
||||
url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id)
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/status"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(on_finished, CloudClusterStatus),
|
||||
@ -100,7 +118,7 @@ class CloudApiClient:
|
||||
:param on_finished: The function to be called after the result is parsed.
|
||||
"""
|
||||
|
||||
url = "{}/jobs/upload".format(self.CURA_API_ROOT)
|
||||
url = f"{self.CURA_API_ROOT}/jobs/upload"
|
||||
data = json.dumps({"data": request.toDict()}).encode()
|
||||
|
||||
self._http.put(url,
|
||||
@ -131,7 +149,7 @@ class CloudApiClient:
|
||||
# specific to sending print jobs) such as lost connection, unparsable responses, etc. are not returned here, but
|
||||
# handled in a generic way by the CloudApiClient.
|
||||
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any], on_error) -> None:
|
||||
url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id)
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print/{job_id}"
|
||||
self._http.post(url,
|
||||
scope = self._scope,
|
||||
data = b"",
|
||||
@ -150,7 +168,7 @@ class CloudApiClient:
|
||||
"""
|
||||
|
||||
body = json.dumps({"data": data}).encode() if data else b""
|
||||
url = "{}/clusters/{}/print_jobs/{}/action/{}".format(self.CLUSTER_API_ROOT, cluster_id, cluster_job_id, action)
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print_jobs/{cluster_job_id}/action/{action}"
|
||||
self._http.post(url,
|
||||
scope = self._scope,
|
||||
data = body,
|
||||
@ -159,7 +177,7 @@ class CloudApiClient:
|
||||
def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
|
||||
"""We override _createEmptyRequest in order to add the user credentials.
|
||||
|
||||
:param url: The URL to request
|
||||
:param path: The URL to request
|
||||
:param content_type: The type of the body contents.
|
||||
"""
|
||||
|
||||
@ -168,7 +186,7 @@ class CloudApiClient:
|
||||
request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type)
|
||||
access_token = self._account.accessToken
|
||||
if access_token:
|
||||
request.setRawHeader(b"Authorization", "Bearer {}".format(access_token).encode())
|
||||
request.setRawHeader(b"Authorization", f"Bearer {access_token}".encode())
|
||||
return request
|
||||
|
||||
@staticmethod
|
||||
@ -189,9 +207,9 @@ class CloudApiClient:
|
||||
Logger.logException("e", "Could not parse the stardust response: %s", error.toDict())
|
||||
return status_code, {"errors": [error.toDict()]}
|
||||
|
||||
def _parseModels(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
|
||||
Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None:
|
||||
"""Parses the given models and calls the correct callback depending on the result.
|
||||
def _parseResponse(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
|
||||
Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None:
|
||||
"""Parses the given response and calls the correct callback depending on the result.
|
||||
|
||||
:param response: The response from the server, after being converted to a dict.
|
||||
:param on_finished: The callback in case the response is successful.
|
||||
@ -200,7 +218,10 @@ class CloudApiClient:
|
||||
|
||||
if "data" in response:
|
||||
data = response["data"]
|
||||
if isinstance(data, list):
|
||||
if "status" in data and data["status"] == "wait_approval":
|
||||
on_finished_empty = cast(Callable[[List], Any], on_finished)
|
||||
on_finished_empty([])
|
||||
elif isinstance(data, list):
|
||||
results = [model_class(**c) for c in data] # type: List[CloudApiClientModel]
|
||||
on_finished_list = cast(Callable[[List[CloudApiClientModel]], Any], on_finished)
|
||||
on_finished_list(results)
|
||||
@ -242,7 +263,7 @@ class CloudApiClient:
|
||||
if status_code >= 300 and on_error is not None:
|
||||
on_error()
|
||||
else:
|
||||
self._parseModels(response, on_finished, model)
|
||||
self._parseResponse(response, on_finished, model)
|
||||
|
||||
self._anti_gc_callbacks.append(parse)
|
||||
return parse
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
from time import time
|
||||
import os
|
||||
from typing import cast, List, Optional, TYPE_CHECKING
|
||||
from typing import cast, List, Optional
|
||||
|
||||
from PyQt6.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
@ -21,6 +21,7 @@ from cura.PrinterOutput.PrinterOutputDevice import ConnectionType
|
||||
|
||||
from .CloudApiClient import CloudApiClient
|
||||
from ..ExportFileJob import ExportFileJob
|
||||
from ..Messages.PrintJobAwaitingApprovalMessage import PrintJobPendingApprovalMessage
|
||||
from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice
|
||||
from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage
|
||||
from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage
|
||||
@ -41,7 +42,7 @@ I18N_CATALOG = i18nCatalog("cura")
|
||||
class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
"""The cloud output device is a network output device that works remotely but has limited functionality.
|
||||
|
||||
Currently it only supports viewing the printer and print job status and adding a new job to the queue.
|
||||
Currently, it only supports viewing the printer and print job status and adding a new job to the queue.
|
||||
As such, those methods have been implemented here.
|
||||
Note that this device represents a single remote cluster, not a list of multiple clusters.
|
||||
"""
|
||||
@ -58,7 +59,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
PRINT_JOB_ACTIONS_MIN_VERSION = Version("5.2.12")
|
||||
|
||||
# Notify can only use signals that are defined by the class that they are in, not inherited ones.
|
||||
# Therefore we create a private signal used to trigger the printersChanged signal.
|
||||
# Therefore, we create a private signal used to trigger the printersChanged signal.
|
||||
_cloudClusterPrintersChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None:
|
||||
@ -202,7 +203,8 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
# Note that self.writeFinished is called in _onPrintUploadCompleted as well.
|
||||
if self._uploaded_print_job:
|
||||
Logger.log("i", "Current mesh is already attached to a print-job, immediately request reprint.")
|
||||
self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintUploadCompleted, self._onPrintUploadSpecificError)
|
||||
self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintUploadCompleted,
|
||||
self._onPrintUploadSpecificError)
|
||||
return
|
||||
|
||||
# Export the scene to the correct file type.
|
||||
@ -230,6 +232,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
|
||||
:param job_response: The response received from the cloud API.
|
||||
"""
|
||||
|
||||
if not self._tool_path:
|
||||
return self._onUploadError()
|
||||
self._pre_upload_print_job = job_response # store the last uploaded job to prevent re-upload of the same file
|
||||
@ -244,12 +247,15 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
|
||||
self._progress.update(100)
|
||||
print_job = cast(CloudPrintJobResponse, self._pre_upload_print_job)
|
||||
if not print_job: # It's possible that another print job is requested in the meanwhile, which then fails to upload with an error, which sets self._pre_uploaded_print_job to `None`.
|
||||
if not print_job:
|
||||
# It's possible that another print job is requested in the meanwhile, which then fails to upload with an
|
||||
# error, which sets self._pre_uploaded_print_job to `None`.
|
||||
self._pre_upload_print_job = None
|
||||
self._uploaded_print_job = None
|
||||
Logger.log("w", "Interference from another job uploaded at roughly the same time, not uploading print!")
|
||||
return # Prevent a crash.
|
||||
self._api.requestPrint(self.key, print_job.job_id, self._onPrintUploadCompleted, self._onPrintUploadSpecificError)
|
||||
self._api.requestPrint(self.key, print_job.job_id, self._onPrintUploadCompleted,
|
||||
self._onPrintUploadSpecificError)
|
||||
|
||||
def _onPrintUploadCompleted(self, response: CloudPrintResponse) -> None:
|
||||
"""Shows a message when the upload has succeeded
|
||||
@ -258,16 +264,21 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
"""
|
||||
self._uploaded_print_job = self._pre_upload_print_job
|
||||
self._progress.hide()
|
||||
message = PrintJobUploadSuccessMessage()
|
||||
message.addAction("monitor print",
|
||||
name=I18N_CATALOG.i18nc("@action:button", "Monitor print"),
|
||||
icon="",
|
||||
description=I18N_CATALOG.i18nc("@action:tooltip", "Track the print in Ultimaker Digital Factory"),
|
||||
button_align=message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
df_url = f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=message-printjob-sent"
|
||||
message.pyQtActionTriggered.connect(lambda message, action: (QDesktopServices.openUrl(QUrl(df_url)), message.hide()))
|
||||
|
||||
message.show()
|
||||
if response:
|
||||
message = PrintJobUploadSuccessMessage()
|
||||
message.addAction("monitor print",
|
||||
name=I18N_CATALOG.i18nc("@action:button", "Monitor print"),
|
||||
icon="",
|
||||
description=I18N_CATALOG.i18nc("@action:tooltip", "Track the print in Ultimaker Digital Factory"),
|
||||
button_align=message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
df_url = f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=message-printjob-sent"
|
||||
message.pyQtActionTriggered.connect(lambda message, action: (QDesktopServices.openUrl(QUrl(df_url)), message.hide()))
|
||||
|
||||
message.show()
|
||||
else:
|
||||
PrintJobPendingApprovalMessage(self._cluster.cluster_id).show()
|
||||
|
||||
self.writeFinished.emit()
|
||||
|
||||
def _onPrintUploadSpecificError(self, reply: "QNetworkReply", _: "QNetworkReply.NetworkError"):
|
||||
@ -278,7 +289,9 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
if error_code == 409:
|
||||
PrintJobUploadQueueFullMessage().show()
|
||||
else:
|
||||
PrintJobUploadErrorMessage(I18N_CATALOG.i18nc("@error:send", "Unknown error code when uploading print job: {0}", error_code)).show()
|
||||
PrintJobUploadErrorMessage(I18N_CATALOG.i18nc("@error:send",
|
||||
"Unknown error code when uploading print job: {0}",
|
||||
error_code)).show()
|
||||
|
||||
Logger.log("w", "Upload of print job failed specifically with error code {}".format(error_code))
|
||||
|
||||
@ -336,11 +349,13 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
|
||||
@pyqtSlot(name="openPrintJobControlPanel")
|
||||
def openPrintJobControlPanel(self) -> None:
|
||||
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl + "?utm_source=cura&utm_medium=software&utm_campaign=monitor-manage-browser"))
|
||||
QDesktopServices.openUrl(QUrl(f"{self.clusterCloudUrl}?utm_source=cura&utm_medium=software&"
|
||||
f"utm_campaign=monitor-manage-browser"))
|
||||
|
||||
@pyqtSlot(name="openPrinterControlPanel")
|
||||
def openPrinterControlPanel(self) -> None:
|
||||
QDesktopServices.openUrl(QUrl(self.clusterCloudUrl + "?utm_source=cura&utm_medium=software&utm_campaign=monitor-manage-printer"))
|
||||
QDesktopServices.openUrl(QUrl(f"{self.clusterCloudUrl}?utm_source=cura&utm_medium=software"
|
||||
f"&utm_campaign=monitor-manage-printer"))
|
||||
|
||||
permissionsChanged = pyqtSignal()
|
||||
|
||||
@ -362,7 +377,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
@pyqtProperty(bool, notify = permissionsChanged)
|
||||
def canWriteOwnPrintJobs(self) -> bool:
|
||||
"""
|
||||
Whether this user can change things about print jobs made by themself.
|
||||
Whether this user can change things about print jobs made by them.
|
||||
"""
|
||||
return "digital-factory.print-job.write.own" in self._account.permissions
|
||||
|
||||
@ -390,4 +405,4 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
"""Gets the URL on which to monitor the cluster via the cloud."""
|
||||
|
||||
root_url_prefix = "-staging" if self._account.is_staging else ""
|
||||
return "https://digitalfactory{}.ultimaker.com/app/jobs/{}".format(root_url_prefix, self.clusterData.cluster_id)
|
||||
return f"https://digitalfactory{root_url_prefix}.ultimaker.com/app/jobs/{self.clusterData.cluster_id}"
|
||||
|
@ -9,7 +9,6 @@ from PyQt6.QtWidgets import QMessageBox
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Logger import Logger # To log errors talking to the API.
|
||||
from UM.Message import Message
|
||||
from UM.Settings.Interfaces import ContainerInterface
|
||||
from UM.Signal import Signal
|
||||
from UM.Util import parseBool
|
||||
@ -20,16 +19,19 @@ from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To upda
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES, META_UM_LINKED_TO_ACCOUNT
|
||||
from .AbstractCloudOutputDevice import AbstractCloudOutputDevice
|
||||
from .CloudApiClient import CloudApiClient
|
||||
from .CloudOutputDevice import CloudOutputDevice
|
||||
from ..Messages.RemovedPrintersMessage import RemovedPrintersMessage
|
||||
from ..Models.Http.CloudClusterResponse import CloudClusterResponse
|
||||
from ..Messages.NewPrinterDetectedMessage import NewPrinterDetectedMessage
|
||||
|
||||
|
||||
class CloudOutputDeviceManager:
|
||||
"""The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters.
|
||||
|
||||
Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code.
|
||||
API spec is available on https://api.ultimaker.com/docs/connect/spec/.
|
||||
API spec is available on https://docs.api.ultimaker.com/connect/index.html.
|
||||
"""
|
||||
|
||||
META_CLUSTER_ID = "um_cloud_cluster_id"
|
||||
@ -46,21 +48,22 @@ class CloudOutputDeviceManager:
|
||||
|
||||
def __init__(self) -> None:
|
||||
# Persistent dict containing the remote clusters for the authenticated user.
|
||||
self._remote_clusters = {} # type: Dict[str, CloudOutputDevice]
|
||||
self._remote_clusters: Dict[str, CloudOutputDevice] = {}
|
||||
|
||||
self._abstract_clusters: Dict[str, AbstractCloudOutputDevice] = {}
|
||||
|
||||
# Dictionary containing all the cloud printers loaded in Cura
|
||||
self._um_cloud_printers = {} # type: Dict[str, GlobalStack]
|
||||
self._um_cloud_printers: Dict[str, GlobalStack] = {}
|
||||
|
||||
self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account
|
||||
self._account: Account = CuraApplication.getInstance().getCuraAPI().account
|
||||
self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
|
||||
self._account.loginStateChanged.connect(self._onLoginStateChanged)
|
||||
self._removed_printers_message = None # type: Optional[Message]
|
||||
self._removed_printers_message: Optional[RemovedPrintersMessage] = None
|
||||
|
||||
# Ensure we don't start twice.
|
||||
self._running = False
|
||||
|
||||
self._syncing = False
|
||||
|
||||
CuraApplication.getInstance().getContainerRegistry().containerRemoved.connect(self._printerRemoved)
|
||||
|
||||
def start(self):
|
||||
@ -113,8 +116,8 @@ class CloudOutputDeviceManager:
|
||||
CuraApplication.getInstance().getContainerRegistry().findContainerStacks(
|
||||
type = "machine") if m.getMetaDataEntry(self.META_CLUSTER_ID, None)}
|
||||
new_clusters = []
|
||||
all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse]
|
||||
online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse]
|
||||
all_clusters: Dict[str, CloudClusterResponse] = {c.cluster_id: c for c in clusters}
|
||||
online_clusters: Dict[str, CloudClusterResponse] = {c.cluster_id: c for c in clusters if c.is_online}
|
||||
|
||||
# Add the new printers in Cura.
|
||||
for device_id, cluster_data in all_clusters.items():
|
||||
@ -130,8 +133,11 @@ class CloudOutputDeviceManager:
|
||||
self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||
if not self._um_cloud_printers[device_id].getMetaDataEntry(META_CAPABILITIES, None):
|
||||
self._um_cloud_printers[device_id].setMetaDataEntry(META_CAPABILITIES, ",".join(cluster_data.capabilities))
|
||||
self._onDevicesDiscovered(new_clusters)
|
||||
|
||||
# We want a machine stack per remote printer that we discovered. Create them now!
|
||||
self._createMachineStacksForDiscoveredClusters(new_clusters)
|
||||
|
||||
# Update the online vs offline status for all found devices
|
||||
self._updateOnlinePrinters(all_clusters)
|
||||
|
||||
# Hide the current removed_printers_message, if there is any
|
||||
@ -152,6 +158,7 @@ class CloudOutputDeviceManager:
|
||||
|
||||
if new_clusters or offline_device_keys or removed_device_keys:
|
||||
self.discoveredDevicesChanged.emit()
|
||||
|
||||
if offline_device_keys:
|
||||
# If the removed device was active we should connect to the new active device
|
||||
self._connectToActiveMachine()
|
||||
@ -165,54 +172,72 @@ class CloudOutputDeviceManager:
|
||||
self._syncing = False
|
||||
self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR)
|
||||
|
||||
def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None:
|
||||
def _requestWrite(self, unique_id: str, nodes: List["SceneNode"]):
|
||||
for remote in self._remote_clusters.values():
|
||||
if unique_id == remote.name: # No other id-type would match. Assume cloud doesn't have duplicate names.
|
||||
remote.requestWrite(nodes)
|
||||
return
|
||||
Logger.log("e", f"Failed writing to specific cloud printer: {unique_id} not in remote clusters.")
|
||||
|
||||
def _createMachineStacksForDiscoveredClusters(self, discovered_clusters: List[CloudClusterResponse]) -> None:
|
||||
"""**Synchronously** create machines for discovered devices
|
||||
|
||||
Any new machines are made available to the user.
|
||||
May take a long time to complete. As this code needs access to the Application
|
||||
and blocks the GIL, creating a Job for this would not make sense.
|
||||
Shows a Message informing the user of progress.
|
||||
May take a long time to complete. This currently forcefully calls the "processEvents", which isn't
|
||||
the nicest solution out there. We might need to consider moving this into a job later!
|
||||
"""
|
||||
new_devices = []
|
||||
new_output_devices: List[CloudOutputDevice] = []
|
||||
remote_clusters_added = False
|
||||
host_guid_map = {machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id
|
||||
for device_cluster_id, machine in self._um_cloud_printers.items()
|
||||
if machine.getMetaDataEntry(self.META_HOST_GUID)}
|
||||
|
||||
# Create a map that maps the HOST_GUID to the DEVICE_CLUSTER_ID
|
||||
host_guid_map: Dict[str, str] = {machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id
|
||||
for device_cluster_id, machine in self._um_cloud_printers.items()
|
||||
if machine.getMetaDataEntry(self.META_HOST_GUID)}
|
||||
|
||||
machine_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
for cluster_data in clusters:
|
||||
device = CloudOutputDevice(self._api, cluster_data)
|
||||
for cluster_data in discovered_clusters:
|
||||
output_device = CloudOutputDevice(self._api, cluster_data)
|
||||
|
||||
if cluster_data.printer_type not in self._abstract_clusters:
|
||||
self._abstract_clusters[cluster_data.printer_type] = AbstractCloudOutputDevice(self._api, cluster_data.printer_type, self._requestWrite, self.refreshConnections)
|
||||
# Ensure that the abstract machine is added (either because it was never added, or it somehow got
|
||||
# removed)
|
||||
_abstract_machine = CuraStackBuilder.createAbstractMachine(cluster_data.printer_type)
|
||||
|
||||
# If the machine already existed before, it will be present in the host_guid_map
|
||||
if cluster_data.host_guid in host_guid_map:
|
||||
machine = machine_manager.getMachine(device.printerType, {self.META_HOST_GUID: cluster_data.host_guid})
|
||||
if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != device.key:
|
||||
machine = machine_manager.getMachine(output_device.printerType, {self.META_HOST_GUID: cluster_data.host_guid})
|
||||
if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != output_device.key:
|
||||
# If the retrieved device has a different cluster_id than the existing machine, bring the existing
|
||||
# machine up-to-date.
|
||||
self._updateOutdatedMachine(outdated_machine = machine, new_cloud_output_device = device)
|
||||
self._updateOutdatedMachine(outdated_machine = machine, new_cloud_output_device = output_device)
|
||||
|
||||
# Create a machine if we don't already have it. Do not make it the active machine.
|
||||
# We only need to add it if it wasn't already added by "local" network or by cloud.
|
||||
if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
|
||||
and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key.
|
||||
new_devices.append(device)
|
||||
elif device.getId() not in self._remote_clusters:
|
||||
self._remote_clusters[device.getId()] = device
|
||||
if machine_manager.getMachine(output_device.printerType, {self.META_CLUSTER_ID: output_device.key}) is None \
|
||||
and machine_manager.getMachine(output_device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key.
|
||||
new_output_devices.append(output_device)
|
||||
elif output_device.getId() not in self._remote_clusters:
|
||||
self._remote_clusters[output_device.getId()] = output_device
|
||||
remote_clusters_added = True
|
||||
# If a printer that was removed from the account is re-added, change its metadata to mark it not removed
|
||||
# from the account
|
||||
elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||
self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||
elif not parseBool(self._um_cloud_printers[output_device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||
self._um_cloud_printers[output_device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||
# As adding a lot of machines might take some time, ensure that the GUI (and progress message) is updated
|
||||
CuraApplication.getInstance().processEvents()
|
||||
|
||||
# Inform the Cloud printers model about new devices.
|
||||
new_devices_list_of_dicts = [{
|
||||
"key": d.getId(),
|
||||
"name": d.name,
|
||||
"machine_type": d.printerTypeName,
|
||||
"firmware_version": d.firmwareVersion} for d in new_devices]
|
||||
"firmware_version": d.firmwareVersion} for d in new_output_devices]
|
||||
discovered_cloud_printers_model = CuraApplication.getInstance().getDiscoveredCloudPrintersModel()
|
||||
discovered_cloud_printers_model.addDiscoveredCloudPrinters(new_devices_list_of_dicts)
|
||||
|
||||
if not new_devices:
|
||||
if not new_output_devices:
|
||||
if remote_clusters_added:
|
||||
self._connectToActiveMachine()
|
||||
return
|
||||
@ -220,55 +245,29 @@ class CloudOutputDeviceManager:
|
||||
# Sort new_devices on online status first, alphabetical second.
|
||||
# Since the first device might be activated in case there is no active printer yet,
|
||||
# it would be nice to prioritize online devices
|
||||
online_cluster_names = {c.friendly_name.lower() for c in clusters if c.is_online and not c.friendly_name is None}
|
||||
new_devices.sort(key = lambda x: ("a{}" if x.name.lower() in online_cluster_names else "b{}").format(x.name.lower()))
|
||||
online_cluster_names = {c.friendly_name.lower() for c in discovered_clusters if c.is_online and not c.friendly_name is None}
|
||||
new_output_devices.sort(key = lambda x: ("a{}" if x.name.lower() in online_cluster_names else "b{}").format(x.name.lower()))
|
||||
|
||||
message = Message(
|
||||
title = self.i18n_catalog.i18ncp(
|
||||
"info:status",
|
||||
"New printer detected from your Ultimaker account",
|
||||
"New printers detected from your Ultimaker account",
|
||||
len(new_devices)
|
||||
),
|
||||
progress = 0,
|
||||
lifetime = 0,
|
||||
message_type = Message.MessageType.POSITIVE
|
||||
)
|
||||
message = NewPrinterDetectedMessage(num_printers_found = len(new_output_devices))
|
||||
message.show()
|
||||
|
||||
new_devices_added = []
|
||||
|
||||
for idx, device in enumerate(new_devices):
|
||||
message_text = self.i18n_catalog.i18nc("info:status Filled in with printer name and printer model.", "Adding printer {name} ({model}) from your account").format(name = device.name, model = device.printerTypeName)
|
||||
message.setText(message_text)
|
||||
if len(new_devices) > 1:
|
||||
message.setProgress((idx / len(new_devices)) * 100)
|
||||
CuraApplication.getInstance().processEvents()
|
||||
self._remote_clusters[device.getId()] = device
|
||||
for idx, output_device in enumerate(new_output_devices):
|
||||
message.updateProgressText(output_device)
|
||||
|
||||
self._remote_clusters[output_device.getId()] = output_device
|
||||
|
||||
# If there is no active machine, activate the first available cloud printer
|
||||
activate = not CuraApplication.getInstance().getMachineManager().activeMachine
|
||||
|
||||
if self._createMachineFromDiscoveredDevice(device.getId(), activate = activate):
|
||||
new_devices_added.append(device)
|
||||
if self._createMachineFromDiscoveredDevice(output_device.getId(), activate = activate):
|
||||
new_devices_added.append(output_device)
|
||||
|
||||
message.setProgress(None)
|
||||
message.finalize(new_devices_added, new_output_devices)
|
||||
|
||||
max_disp_devices = 3
|
||||
if len(new_devices_added) > max_disp_devices:
|
||||
num_hidden = len(new_devices_added) - max_disp_devices
|
||||
device_name_list = ["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices[0:max_disp_devices]]
|
||||
device_name_list.append("<li>" + self.i18n_catalog.i18ncp("info:{0} gets replaced by a number of printers", "... and {0} other", "... and {0} others", num_hidden) + "</li>")
|
||||
device_names = "".join(device_name_list)
|
||||
else:
|
||||
device_names = "".join(["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices_added])
|
||||
if new_devices_added:
|
||||
message_text = self.i18n_catalog.i18nc("info:status", "Printers added from Digital Factory:") + "<ul>" + device_names + "</ul>"
|
||||
message.setText(message_text)
|
||||
else:
|
||||
message.hide()
|
||||
|
||||
def _updateOnlinePrinters(self, printer_responses: Dict[str, CloudClusterResponse]) -> None:
|
||||
@staticmethod
|
||||
def _updateOnlinePrinters(printer_responses: Dict[str, CloudClusterResponse]) -> None:
|
||||
"""
|
||||
Update the metadata of the printers to store whether they are online or not.
|
||||
:param printer_responses: The responses received from the API about the printer statuses.
|
||||
@ -291,7 +290,8 @@ class CloudOutputDeviceManager:
|
||||
old_cluster_id = outdated_machine.getMetaDataEntry(self.META_CLUSTER_ID)
|
||||
outdated_machine.setMetaDataEntry(self.META_CLUSTER_ID, new_cloud_output_device.key)
|
||||
outdated_machine.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)
|
||||
# Cleanup the remainings of the old CloudOutputDevice(old_cluster_id)
|
||||
|
||||
# Cleanup the remains of the old CloudOutputDevice(old_cluster_id)
|
||||
self._um_cloud_printers[new_cloud_output_device.key] = self._um_cloud_printers.pop(old_cluster_id)
|
||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
if old_cluster_id in output_device_manager.getOutputDeviceIds():
|
||||
@ -321,56 +321,19 @@ class CloudOutputDeviceManager:
|
||||
for device_id in removed_device_ids:
|
||||
if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
|
||||
ignored_device_ids.add(device_id)
|
||||
|
||||
# Keep the reported_device_ids list in a class variable, so that the message button actions can access it and
|
||||
# take the necessary steps to fulfill their purpose.
|
||||
self.reported_device_ids = removed_device_ids - ignored_device_ids
|
||||
if not self.reported_device_ids:
|
||||
return
|
||||
|
||||
# Generate message
|
||||
self._removed_printers_message = Message(
|
||||
title = self.i18n_catalog.i18ncp(
|
||||
"info:status",
|
||||
"A cloud connection is not available for a printer",
|
||||
"A cloud connection is not available for some printers",
|
||||
len(self.reported_device_ids)
|
||||
),
|
||||
message_type = Message.MessageType.WARNING
|
||||
)
|
||||
device_names = "".join(["<li>{} ({})</li>".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids])
|
||||
message_text = self.i18n_catalog.i18ncp(
|
||||
"info:status",
|
||||
"This printer is not linked to the Digital Factory:",
|
||||
"These printers are not linked to the Digital Factory:",
|
||||
len(self.reported_device_ids)
|
||||
)
|
||||
message_text += "<br/><ul>{}</ul><br/>".format(device_names)
|
||||
digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory")
|
||||
|
||||
message_text += self.i18n_catalog.i18nc(
|
||||
"info:status",
|
||||
"To establish a connection, please visit the {website_link}".format(website_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-connect-printer'>{}</a>.".format(digital_factory_string))
|
||||
)
|
||||
self._removed_printers_message.setText(message_text)
|
||||
self._removed_printers_message.addAction("keep_printer_configurations_action",
|
||||
name = self.i18n_catalog.i18nc("@action:button", "Keep printer configurations"),
|
||||
icon = "",
|
||||
description = "Keep cloud printers in Ultimaker Cura when not connected to your account.",
|
||||
button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
self._removed_printers_message.addAction("remove_printers_action",
|
||||
name = self.i18n_catalog.i18nc("@action:button", "Remove printers"),
|
||||
icon = "",
|
||||
description = "Remove cloud printer(s) which aren't linked to your account.",
|
||||
button_style = Message.ActionButtonStyle.SECONDARY,
|
||||
button_align = Message.ActionButtonAlignment.ALIGN_LEFT)
|
||||
self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered)
|
||||
|
||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
|
||||
# Remove the output device from the printers
|
||||
for device_id in removed_device_ids:
|
||||
device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack]
|
||||
if not device:
|
||||
global_stack: Optional[GlobalStack] = self._um_cloud_printers.get(device_id, None)
|
||||
if not global_stack:
|
||||
continue
|
||||
if device_id in output_device_manager.getOutputDeviceIds():
|
||||
output_device_manager.removeOutputDevice(device_id)
|
||||
@ -378,12 +341,19 @@ class CloudOutputDeviceManager:
|
||||
del self._remote_clusters[device_id]
|
||||
|
||||
# Update the printer's metadata to mark it as not linked to the account
|
||||
device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)
|
||||
global_stack.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)
|
||||
|
||||
# Generate message to show
|
||||
device_names = "".join(["<li>{} ({})</li>".format(self._um_cloud_printers[device].name,
|
||||
self._um_cloud_printers[device].definition.name) for device in
|
||||
self.reported_device_ids])
|
||||
self._removed_printers_message = RemovedPrintersMessage(self.reported_device_ids, device_names)
|
||||
self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered)
|
||||
self._removed_printers_message.show()
|
||||
|
||||
def _onDiscoveredDeviceRemoved(self, device_id: str) -> None:
|
||||
device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice]
|
||||
""" Remove the CloudOutputDevices for printers that are offline"""
|
||||
device: Optional[CloudOutputDevice] = self._remote_clusters.pop(device_id, None)
|
||||
if not device:
|
||||
return
|
||||
device.close()
|
||||
@ -392,12 +362,12 @@ class CloudOutputDeviceManager:
|
||||
output_device_manager.removeOutputDevice(device.key)
|
||||
|
||||
def _createMachineFromDiscoveredDevice(self, key: str, activate: bool = True) -> bool:
|
||||
device = self._remote_clusters[key]
|
||||
device = self._remote_clusters.get(key)
|
||||
if not device:
|
||||
return False
|
||||
|
||||
# Create a new machine.
|
||||
# We do not use use MachineManager.addMachine here because we need to set the cluster ID before activating it.
|
||||
# We do not 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)
|
||||
if not new_machine:
|
||||
Logger.error(f"Failed creating a new machine for {device.name}")
|
||||
@ -405,8 +375,6 @@ class CloudOutputDeviceManager:
|
||||
|
||||
self._setOutputDeviceMetadata(device, new_machine)
|
||||
|
||||
_abstract_machine = CuraStackBuilder.createAbstractMachine(device.printerType)
|
||||
|
||||
if activate:
|
||||
CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId())
|
||||
|
||||
@ -414,15 +382,19 @@ class CloudOutputDeviceManager:
|
||||
|
||||
def _connectToActiveMachine(self) -> None:
|
||||
"""Callback for when the active machine was changed by the user"""
|
||||
|
||||
active_machine = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not active_machine:
|
||||
return
|
||||
|
||||
# Check if we should directly connect with a "normal" CloudOutputDevice or that we should connect to an
|
||||
# 'abstract' one
|
||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID)
|
||||
local_network_key = active_machine.getMetaDataEntry(self.META_NETWORK_KEY)
|
||||
for device in list(self._remote_clusters.values()): # Make a copy of the remote devices list, to prevent modifying the list while iterating, if a device gets added asynchronously.
|
||||
|
||||
# Copy of the device list, to prevent modifying the list while iterating, if a device gets added asynchronously.
|
||||
remote_cluster_copy: List[CloudOutputDevice] = list(self._remote_clusters.values())
|
||||
for device in remote_cluster_copy:
|
||||
if device.key == stored_cluster_id:
|
||||
# Connect to it if the stored ID matches.
|
||||
self._connectToOutputDevice(device, active_machine)
|
||||
@ -433,6 +405,14 @@ class CloudOutputDeviceManager:
|
||||
# Remove device if it is not meant for the active machine.
|
||||
output_device_manager.removeOutputDevice(device.key)
|
||||
|
||||
# Update state of all abstract output devices
|
||||
remote_abstract_cluster_copy: List[CloudOutputDevice] = list(self._abstract_clusters.values())
|
||||
for device in remote_abstract_cluster_copy:
|
||||
if device.printerType == active_machine.definition.getId() and parseBool(active_machine.getMetaDataEntry("is_abstract_machine", False)):
|
||||
self._connectToAbstractOutputDevice(device, active_machine)
|
||||
elif device.key in output_device_manager.getOutputDeviceIds():
|
||||
output_device_manager.removeOutputDevice(device.key)
|
||||
|
||||
def _setOutputDeviceMetadata(self, device: CloudOutputDevice, machine: GlobalStack):
|
||||
machine.setName(device.name)
|
||||
machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key)
|
||||
@ -440,13 +420,24 @@ class CloudOutputDeviceManager:
|
||||
machine.setMetaDataEntry("group_name", device.name)
|
||||
machine.setMetaDataEntry("group_size", device.clusterSize)
|
||||
digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory")
|
||||
digital_factory_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-remove-printer'>{digital_factory_string}</a>".format(digital_factory_string = digital_factory_string)
|
||||
digital_factory_link = f"<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&" \
|
||||
f"utm_campaign=change-account-remove-printer'>{digital_factory_string}</a>"
|
||||
removal_warning_string = self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "{printer_name} will be removed until the next account sync.").format(printer_name = device.name) \
|
||||
+ "<br>" + self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "To remove {printer_name} permanently, visit {digital_factory_link}").format(printer_name = device.name, digital_factory_link = digital_factory_link) \
|
||||
+ "<br><br>" + self.i18n_catalog.i18nc("@message {printer_name} is replaced with the name of the printer", "Are you sure you want to remove {printer_name} temporarily?").format(printer_name = device.name)
|
||||
machine.setMetaDataEntry("removal_warning", removal_warning_string)
|
||||
machine.addConfiguredConnectionType(device.connectionType.value)
|
||||
|
||||
def _connectToAbstractOutputDevice(self, device: AbstractCloudOutputDevice, machine: GlobalStack) -> None:
|
||||
Logger.debug(f"Attempting to connect to abstract machine {machine.id}")
|
||||
if not device.isConnected():
|
||||
device.connect()
|
||||
machine.addConfiguredConnectionType(device.connectionType.value)
|
||||
|
||||
output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()
|
||||
if device.key not in output_device_manager.getOutputDeviceIds():
|
||||
output_device_manager.addOutputDevice(device)
|
||||
|
||||
def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None:
|
||||
"""Connects to an output device and makes sure it is registered in the output device manager."""
|
||||
|
||||
@ -472,7 +463,7 @@ class CloudOutputDeviceManager:
|
||||
if container_cluster_id in self._remote_clusters.keys():
|
||||
del self._remote_clusters[container_cluster_id]
|
||||
|
||||
def _onRemovedPrintersMessageActionTriggered(self, removed_printers_message: Message, action: str) -> None:
|
||||
def _onRemovedPrintersMessageActionTriggered(self, removed_printers_message: RemovedPrintersMessage, action: str) -> None:
|
||||
if action == "keep_printer_configurations_action":
|
||||
removed_printers_message.hide()
|
||||
elif action == "remove_printers_action":
|
||||
@ -483,12 +474,16 @@ class CloudOutputDeviceManager:
|
||||
question_title = self.i18n_catalog.i18nc("@title:window", "Remove printers?")
|
||||
question_content = self.i18n_catalog.i18ncp(
|
||||
"@label",
|
||||
"You are about to remove {0} printer from Cura. This action cannot be undone.\nAre you sure you want to continue?",
|
||||
"You are about to remove {0} printers from Cura. This action cannot be undone.\nAre you sure you want to continue?",
|
||||
"You are about to remove {0} printer from Cura. This action cannot be undone.\n"
|
||||
"Are you sure you want to continue?",
|
||||
"You are about to remove {0} printers from Cura. This action cannot be undone.\n"
|
||||
"Are you sure you want to continue?",
|
||||
len(remove_printers_ids)
|
||||
)
|
||||
if remove_printers_ids == all_ids:
|
||||
question_content = self.i18n_catalog.i18nc("@label", "You are about to remove all printers from Cura. This action cannot be undone.\nAre you sure you want to continue?")
|
||||
question_content = self.i18n_catalog.i18nc("@label", "You are about to remove all printers from Cura. "
|
||||
"This action cannot be undone.\n"
|
||||
"Are you sure you want to continue?")
|
||||
result = QMessageBox.question(None, question_title, question_content)
|
||||
if result == QMessageBox.StandardButton.No:
|
||||
return
|
||||
|
@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM import i18nCatalog
|
||||
from UM.Message import Message
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
|
||||
class NewPrinterDetectedMessage(Message):
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def __init__(self, num_printers_found: int) -> None:
|
||||
super().__init__(title = self.i18n_catalog.i18ncp("info:status",
|
||||
"New printer detected from your Ultimaker account",
|
||||
"New printers detected from your Ultimaker account",
|
||||
num_printers_found),
|
||||
progress = 0,
|
||||
lifetime = 0,
|
||||
message_type = Message.MessageType.POSITIVE)
|
||||
self._printers_added = 0
|
||||
self._num_printers_found = num_printers_found
|
||||
|
||||
def updateProgressText(self, output_device):
|
||||
"""
|
||||
While the progress of adding printers is running, update the text displayed.
|
||||
:param output_device: The output device that is being added.
|
||||
:return:
|
||||
"""
|
||||
message_text = self.i18n_catalog.i18nc("info:status Filled in with printer name and printer model.",
|
||||
"Adding printer {name} ({model}) from your account").format(
|
||||
name=output_device.name, model=output_device.printerTypeName)
|
||||
self.setText(message_text)
|
||||
if self._num_printers_found > 1:
|
||||
self.setProgress((self._printers_added / self._num_printers_found) * 100)
|
||||
self._printers_added += 1
|
||||
|
||||
CuraApplication.getInstance().processEvents()
|
||||
|
||||
def finalize(self, new_devices_added, new_output_devices):
|
||||
self.setProgress(None)
|
||||
num_devices_added = len(new_devices_added)
|
||||
max_disp_devices = 3
|
||||
|
||||
if num_devices_added > max_disp_devices:
|
||||
num_hidden = num_devices_added - max_disp_devices
|
||||
device_name_list = ["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in
|
||||
new_output_devices[0: max_disp_devices]]
|
||||
device_name_list.append(
|
||||
"<li>" + self.i18n_catalog.i18ncp("info:{0} gets replaced by a number of printers", "... and {0} other",
|
||||
"... and {0} others", num_hidden) + "</li>")
|
||||
device_names = "".join(device_name_list)
|
||||
else:
|
||||
device_names = "".join(
|
||||
["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices_added])
|
||||
|
||||
if new_devices_added:
|
||||
message_text = self.i18n_catalog.i18nc("info:status",
|
||||
"Printers added from Digital Factory:") + f"<ul>{device_names}</ul>"
|
||||
self.setText(message_text)
|
||||
else:
|
||||
self.hide()
|
@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from PyQt6.QtCore import QUrl
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Message import Message
|
||||
|
||||
|
||||
I18N_CATALOG = i18nCatalog("cura")
|
||||
|
||||
|
||||
class PrintJobPendingApprovalMessage(Message):
|
||||
"""Message shown when waiting for approval on an uploaded print job."""
|
||||
|
||||
def __init__(self, cluster_id: str) -> None:
|
||||
super().__init__(
|
||||
text = I18N_CATALOG.i18nc("@info:status", "You will receive a confirmation via email when the print job is approved"),
|
||||
title=I18N_CATALOG.i18nc("@info:title", "The print job was successfully submitted"),
|
||||
message_type=Message.MessageType.POSITIVE
|
||||
)
|
||||
self.addAction("manage_print_jobs", I18N_CATALOG.i18nc("@action", "Manage print jobs"), "", "")
|
||||
|
||||
self.addAction("learn_more", I18N_CATALOG.i18nc("@action", "Learn more"), "", "",
|
||||
button_style = Message.ActionButtonStyle.LINK,
|
||||
button_align = Message.ActionButtonAlignment.ALIGN_LEFT)
|
||||
|
||||
self.actionTriggered.connect(self._onActionTriggered)
|
||||
|
||||
self.cluster_id = cluster_id
|
||||
|
||||
def _onActionTriggered(self, message: Message, action: str) -> None:
|
||||
""" Callback function for the "Manage print jobs" button on the pending approval notification. """
|
||||
match action:
|
||||
case "manage_print_jobs":
|
||||
QDesktopServices.openUrl(QUrl(f"https://digitalfactory.ultimaker.com/app/jobs/{self._cluster.cluster_id}?utm_source=cura&utm_medium=software&utm_campaign=message-printjob-sent"))
|
||||
case "learn_more":
|
||||
QDesktopServices.openUrl(QUrl("https://support.ultimaker.com/hc/en-us/articles/5329940078620?utm_source=cura&utm_medium=software&utm_campaign=message-printjob-sent"))
|
@ -0,0 +1,52 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM import i18nCatalog
|
||||
from UM.Message import Message
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
|
||||
class RemovedPrintersMessage(Message):
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
def __init__(self, removed_devices, device_names) -> None:
|
||||
self._removed_devices = removed_devices
|
||||
|
||||
message_text = self.i18n_catalog.i18ncp(
|
||||
"info:status",
|
||||
"This printer is not linked to the Digital Factory:",
|
||||
"These printers are not linked to the Digital Factory:",
|
||||
len(self._removed_devices)
|
||||
)
|
||||
message_text += "<br/><ul>{}</ul><br/>".format(device_names)
|
||||
|
||||
digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory")
|
||||
website_link = f"<a href='https://digitalfactory.ultimaker.com?utm_source=cura&" \
|
||||
f"utm_medium=software&utm_campaign=change-account-connect-printer'>{digital_factory_string}</a>."
|
||||
|
||||
message_text += self.i18n_catalog.i18nc(
|
||||
"info:status",
|
||||
f"To establish a connection, please visit the {website_link}"
|
||||
)
|
||||
|
||||
super().__init__(title=self.i18n_catalog.i18ncp("info:status",
|
||||
"A cloud connection is not available for a printer",
|
||||
"A cloud connection is not available for some printers",
|
||||
len(self._removed_devices)),
|
||||
message_type=Message.MessageType.WARNING,
|
||||
text = message_text)
|
||||
|
||||
self.addAction("keep_printer_configurations_action",
|
||||
name=self.i18n_catalog.i18nc("@action:button",
|
||||
"Keep printer configurations"),
|
||||
icon="",
|
||||
description="Keep cloud printers in Ultimaker Cura when not connected to your account.",
|
||||
button_align=Message.ActionButtonAlignment.ALIGN_RIGHT)
|
||||
self.addAction("remove_printers_action",
|
||||
name=self.i18n_catalog.i18nc("@action:button", "Remove printers"),
|
||||
icon="",
|
||||
description="Remove cloud printer(s) which aren't linked to your account.",
|
||||
button_style=Message.ActionButtonStyle.SECONDARY,
|
||||
button_align=Message.ActionButtonAlignment.ALIGN_LEFT)
|
||||
|
||||
|
||||
|
@ -8,7 +8,6 @@ from ..BaseModel import BaseModel
|
||||
class CloudClusterResponse(BaseModel):
|
||||
"""Class representing a cloud connected cluster."""
|
||||
|
||||
|
||||
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
|
||||
host_internal_ip: Optional[str] = None, host_version: Optional[str] = None,
|
||||
friendly_name: Optional[str] = None, printer_type: str = "ultimaker3", printer_count: int = 1,
|
||||
|
@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Optional, List
|
||||
|
||||
from .CloudClusterResponse import CloudClusterResponse
|
||||
from .ClusterPrinterStatus import ClusterPrinterStatus
|
||||
|
||||
|
||||
class CloudClusterWithConfigResponse(CloudClusterResponse):
|
||||
"""Class representing a cloud connected cluster."""
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
self.configuration = self.parseModel(ClusterPrinterStatus, kwargs.get("host_printer"))
|
||||
super().__init__(**kwargs)
|
@ -20,7 +20,6 @@ from ..BaseModel import BaseModel
|
||||
class ClusterPrinterStatus(BaseModel):
|
||||
"""Class representing a cluster printer"""
|
||||
|
||||
|
||||
def __init__(self, enabled: bool, firmware_version: str, friendly_name: str, ip_address: str, machine_variant: str,
|
||||
status: str, unique_name: str, uuid: str,
|
||||
configuration: List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]],
|
||||
@ -28,9 +27,9 @@ class ClusterPrinterStatus(BaseModel):
|
||||
firmware_update_status: Optional[str] = None, latest_available_firmware: Optional[str] = None,
|
||||
build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None,
|
||||
material_station: Union[Dict[str, Any], ClusterPrinterMaterialStation] = None, **kwargs) -> None:
|
||||
"""Creates a new cluster printer status
|
||||
|
||||
:param enabled: A printer can be disabled if it should not receive new jobs. By default every printer is enabled.
|
||||
"""
|
||||
Creates a new cluster printer status
|
||||
:param enabled: A printer can be disabled if it should not receive new jobs. By default, every printer is enabled.
|
||||
:param firmware_version: Firmware version installed on the printer. Can differ for each printer in a cluster.
|
||||
:param friendly_name: Human readable name of the printer. Can be used for identification purposes.
|
||||
:param ip_address: The IP address of the printer in the local network.
|
||||
|
@ -3,6 +3,8 @@
|
||||
import os
|
||||
from typing import Optional, Dict, List, Callable, Any
|
||||
|
||||
from time import time
|
||||
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
from PyQt6.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject
|
||||
from PyQt6.QtNetwork import QNetworkReply
|
||||
@ -32,6 +34,8 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
|
||||
activeCameraUrlChanged = pyqtSignal()
|
||||
|
||||
CHECK_CLUSTER_INTERVAL = 10.0 # seconds
|
||||
|
||||
def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], parent=None) -> None:
|
||||
|
||||
super().__init__(
|
||||
@ -107,6 +111,8 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice):
|
||||
|
||||
def _update(self) -> None:
|
||||
super()._update()
|
||||
if time() - self._time_of_last_request < self.CHECK_CLUSTER_INTERVAL:
|
||||
return # avoid calling the cluster too often
|
||||
self._getApiClient().getPrinters(self._updatePrinters)
|
||||
self._getApiClient().getPrintJobs(self._updatePrintJobs)
|
||||
self._updatePrintJobPreviewImages()
|
||||
|
@ -221,7 +221,7 @@ class LocalClusterOutputDeviceManager:
|
||||
return
|
||||
|
||||
# Create a new machine and activate it.
|
||||
# We do not use use MachineManager.addMachine here because we need to set the network key before activating it.
|
||||
# We do not use MachineManager.addMachine here because we need to set the network key before activating it.
|
||||
# If we do not do this the auto-pairing with the cloud-equivalent device will not work.
|
||||
new_machine = CuraStackBuilder.createMachine(device.name, device.printerType)
|
||||
if not new_machine:
|
||||
@ -234,7 +234,6 @@ class LocalClusterOutputDeviceManager:
|
||||
|
||||
_abstract_machine = CuraStackBuilder.createAbstractMachine(device.printerType)
|
||||
|
||||
|
||||
def _storeManualAddress(self, address: str) -> None:
|
||||
"""Add an address to the stored preferences."""
|
||||
|
||||
|
@ -398,7 +398,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
line = line[:line.find(";")]
|
||||
|
||||
line = line.strip()
|
||||
|
||||
|
||||
# Don't send empty lines. But we do have to send something, so send M105 instead.
|
||||
# Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||
if line == "" or line == "M0" or line == "M1":
|
||||
@ -429,7 +429,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
print_job.updateTimeElapsed(elapsed_time)
|
||||
estimated_time = self._print_estimated_time
|
||||
if progress > .1:
|
||||
estimated_time = self._print_estimated_time * (1 - progress) + elapsed_time
|
||||
estimated_time = int(self._print_estimated_time * (1 - progress) + elapsed_time)
|
||||
print_job.updateTimeTotal(estimated_time)
|
||||
|
||||
self._gcode_position += 1
|
||||
|
@ -64,7 +64,7 @@ class VersionUpgrade22to24(VersionUpgrade):
|
||||
|
||||
config.remove_option("general", "containers")
|
||||
|
||||
for idx in range(len(container_list)):
|
||||
for idx, _ in enumerate(container_list):
|
||||
config.set("containers", str(idx), container_list[idx])
|
||||
|
||||
output = io.StringIO()
|
||||
|
@ -866,13 +866,13 @@ def readIndex(node, attr):
|
||||
v = readIntArray(node, attr, [])
|
||||
chunks = []
|
||||
chunk = []
|
||||
for i in range(len(v)):
|
||||
if v[i] == -1:
|
||||
for i in v:
|
||||
if i == -1:
|
||||
if chunk:
|
||||
chunks.append(chunk)
|
||||
chunk = []
|
||||
else:
|
||||
chunk.append(v[i])
|
||||
chunk.append(i)
|
||||
if chunk:
|
||||
chunks.append(chunk)
|
||||
return chunks
|
||||
|
@ -228,6 +228,9 @@ SecretStorage==3.3.1 \
|
||||
keyring==23.0.1 \
|
||||
--hash=sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8 \
|
||||
--hash=sha256:8f607d7d1cc502c43a932a275a56fe47db50271904513a379d39df1af277ac48
|
||||
networkx==2.6.2 \
|
||||
--hash=sha256:2306f1950ce772c5a59a57f5486d59bb9cab98497c45fc49cbc45ac0dec119bb \
|
||||
--hash=sha256:5fcb7004be69e8fbdf07dcb502efa5c77cadcaad6982164134eeb9721f826c2e
|
||||
pywin32==303; \
|
||||
sys_platform=="win32" \
|
||||
--hash=sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51
|
||||
|
@ -63,9 +63,6 @@
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"value": "speed_wall"
|
||||
},
|
||||
|
@ -45,16 +45,12 @@
|
||||
"acceleration_enabled": { "value": false },
|
||||
"jerk_enabled": { "value": false },
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"infill_pattern": { "value": "'lines'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
|
@ -28,8 +28,7 @@
|
||||
"retraction_speed": { "default_value": 50},
|
||||
"gantry_height": { "value": "30" },
|
||||
"speed_print": { "default_value": 50 },
|
||||
"material_print_temperature": { "value": 195 },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature" },
|
||||
"default_material_print_temperature": { "value": 195 },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": 195 },
|
||||
"machine_max_feedrate_x": { "value": 500 },
|
||||
@ -41,11 +40,9 @@
|
||||
"machine_max_acceleration_z": { "value": 100 },
|
||||
"machine_max_acceleration_e": { "value": 500 },
|
||||
"machine_acceleration": { "value": 500 },
|
||||
"machine_max_jerk_xy": { "value": 8 },
|
||||
"machine_max_jerk_z": { "value": 0.4 },
|
||||
"machine_max_jerk_e": { "value": 5 },
|
||||
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"infill_overlap": { "default_value": 15 },
|
||||
|
||||
"acceleration_print": { "value": 500 },
|
||||
"acceleration_travel": { "value": 500 },
|
||||
"acceleration_travel_layer_0": { "value": "acceleration_travel" },
|
||||
|
@ -45,16 +45,12 @@
|
||||
"acceleration_enabled": { "value": false },
|
||||
"jerk_enabled": { "value": false },
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"infill_pattern": { "value": "'lines'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
|
@ -7,6 +7,7 @@
|
||||
"author": "Abax 3D Technologies",
|
||||
"manufacturer": "Abax 3D Technologies",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_machine_quality": "true",
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "abax_pri3_extruder_0"
|
||||
|
@ -7,6 +7,7 @@
|
||||
"author": "Abax 3D Technologies",
|
||||
"manufacturer": "Abax 3D Technologies",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_machine_quality": "true",
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "abax_pri5_extruder_0"
|
||||
|
@ -10,7 +10,7 @@
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "alya_platform.3mf",
|
||||
"platform_offset": [-60, -45, 75 ],
|
||||
"exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "generic_tpu_175", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "ultimaker_abs_black", "ultimaker_abs_blue", "ultimaker_abs_green", "ultimaker_abs_grey", "ultimaker_abs_orange", "ultimaker_abs_pearl-gold", "ultimaker_abs_red", "ultimaker_abs_silver-metallic", "ultimaker_abs_white", "ultimaker_abs_yellow", "ultimaker_bam", "ultimaker_cpe_black", "ultimaker_cpe_blue", "ultimaker_cpe_dark-grey", "ultimaker_cpe_green", "ultimaker_cpe_light-grey", "ultimaker_cpe_plus_black", "ultimaker_cpe_plus_transparent", "ultimaker_cpe_plus_white", "ultimaker_cpe_red", "ultimaker_cpe_transparent", "ultimaker_cpe_white", "ultimaker_cpe_yellow", "ultimaker_nylon_black", "ultimaker_nylon_transparent", "ultimaker_pc_black", "ultimaker_pc_transparent", "ultimaker_pc_white", "ultimaker_pla_black", "ultimaker_pla_blue", "ultimaker_pla_green", "ultimaker_pla_magenta", "ultimaker_pla_orange", "ultimaker_pla_pearl-white", "ultimaker_pla_red", "ultimaker_pla_silver-metallic", "ultimaker_pla_transparent", "ultimaker_pla_white", "ultimaker_pla_yellow", "ultimaker_pp_transparent", "ultimaker_pva", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white", "ultimaker_tpu_black", "ultimaker_tpu_blue", "ultimaker_tpu_red", "ultimaker_tpu_white", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla","tizyx_pla","tizyx_abs","tizyx_pla_bois" ],
|
||||
"exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "generic_tpu_175", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla","tizyx_pla","tizyx_abs","tizyx_pla_bois" ],
|
||||
"preferred_material": "generic_pla",
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
|
@ -10,7 +10,7 @@
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "alya_nx_platform.3mf",
|
||||
"platform_offset": [-104, 0, 93 ],
|
||||
"exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "generic_tpu_175", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "ultimaker_abs_black", "ultimaker_abs_blue", "ultimaker_abs_green", "ultimaker_abs_grey", "ultimaker_abs_orange", "ultimaker_abs_pearl-gold", "ultimaker_abs_red", "ultimaker_abs_silver-metallic", "ultimaker_abs_white", "ultimaker_abs_yellow", "ultimaker_bam", "ultimaker_cpe_black", "ultimaker_cpe_blue", "ultimaker_cpe_dark-grey", "ultimaker_cpe_green", "ultimaker_cpe_light-grey", "ultimaker_cpe_plus_black", "ultimaker_cpe_plus_transparent", "ultimaker_cpe_plus_white", "ultimaker_cpe_red", "ultimaker_cpe_transparent", "ultimaker_cpe_white", "ultimaker_cpe_yellow", "ultimaker_nylon_black", "ultimaker_nylon_transparent", "ultimaker_pc_black", "ultimaker_pc_transparent", "ultimaker_pc_white", "ultimaker_pla_black", "ultimaker_pla_blue", "ultimaker_pla_green", "ultimaker_pla_magenta", "ultimaker_pla_orange", "ultimaker_pla_pearl-white", "ultimaker_pla_red", "ultimaker_pla_silver-metallic", "ultimaker_pla_transparent", "ultimaker_pla_white", "ultimaker_pla_yellow", "ultimaker_pp_transparent", "ultimaker_pva", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white", "ultimaker_tpu_black", "ultimaker_tpu_blue", "ultimaker_tpu_red", "ultimaker_tpu_white", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla","tizyx_pla","tizyx_abs","tizyx_pla_bois" ],
|
||||
"exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "generic_tpu_175", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla","tizyx_pla","tizyx_abs","tizyx_pla_bois" ],
|
||||
"preferred_material": "generic_pla",
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
|
@ -51,14 +51,11 @@
|
||||
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print * 2" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" },
|
||||
"speed_layer_0": { "value": "speed_print / 2" },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_prime_tower": { "value": "speed_print" },
|
||||
"speed_support": { "value": "speed_print" },
|
||||
@ -67,18 +64,14 @@
|
||||
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
|
||||
"optimize_wall_printing_order": { "value": true },
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_before_walls": { "value": true },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
@ -88,34 +81,34 @@
|
||||
|
||||
"fill_outline_gaps": { "value": false },
|
||||
|
||||
"retraction_speed": {
|
||||
"retraction_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_retract_speed": {
|
||||
"retraction_retract_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"retraction_prime_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
|
||||
"retraction_hop_enabled": { "value": "False" },
|
||||
|
||||
"retraction_hop": { "value": 1 },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
"travel_avoid_other_parts": { "value": true },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
"adaptive_layer_height_variation": { "value": 0.04 },
|
||||
@ -133,16 +126,12 @@
|
||||
|
||||
"extruder_prime_pos_y":{"minimum_value": "0","maximum_value": "machine_depth"},
|
||||
"extruder_prime_pos_x":{"minimum_value": "0","maximum_value": "machine_width"},
|
||||
"relative_extrusion":{"value": false,"enabled": false},
|
||||
"relative_extrusion":{"value": false, "enabled": false},
|
||||
|
||||
"machine_use_extruder_offset_to_offset_coords": {"default_value": true},
|
||||
"machine_gcode_flavor": {"default_value": "RepRap (Marlin/Sprinter)"},
|
||||
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"gantry_height": {
|
||||
"value": "0"
|
||||
}
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"gantry_height": { "value": "0"}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
"machine_max_jerk_xy": { "default_value": 11.0 },
|
||||
"machine_max_jerk_z": { "default_value": 0.4 },
|
||||
"machine_max_jerk_e": { "default_value": 11.0 },
|
||||
|
||||
"acceleration_enabled": { "value": true },
|
||||
"jerk_enabled": { "value": "True" },
|
||||
"jerk_layer_0": { "value": "jerk_topbottom" },
|
||||
"jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" },
|
||||
@ -51,7 +51,6 @@
|
||||
"gantry_height": { "value": "25.0" },
|
||||
"skin_overlap": { "value": "10" },
|
||||
|
||||
"acceleration_enabled": { "value": "True" },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
|
||||
"acceleration_print": { "value": "900" },
|
||||
|
@ -53,8 +53,6 @@
|
||||
"material_bed_temperature": { "maximum_value_warning": 110 },
|
||||
"material_bed_temperature_layer_0": { "maximum_value_warning": 110 },
|
||||
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
"top_bottom_thickness": { "value": "layer_height_0 + layer_height * math.floor(1.2 / layer_height)" },
|
||||
"wall_thickness": { "value": "line_width * 3 if line_width < 0.6 else line_width * 2" },
|
||||
|
||||
@ -70,15 +68,11 @@
|
||||
"jerk_enabled": { "value": true },
|
||||
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": 100.0, "maximum_value_warning": 150.0, "maximum_value": 200.0 },
|
||||
"speed_layer_0": { "value": "speed_topbottom if speed_topbottom < 20 else 20" },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "speed_travel" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_support": { "value": "speed_wall_0" },
|
||||
@ -101,7 +95,7 @@
|
||||
"retraction_hop": { "value": 0.075 },
|
||||
"retraction_hop_only_when_collides": { "value": true },
|
||||
"retraction_amount": { "value": 6 },
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
"retraction_combing": { "value": "'off'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
@ -114,7 +108,6 @@
|
||||
"cool_fan_speed": { "value": 100 },
|
||||
"cool_fan_speed_0": { "value": 30 },
|
||||
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
"adhesion_type": { "value": "'none' if support_enable else 'skirt'" },
|
||||
|
@ -76,8 +76,8 @@
|
||||
"acceleration_enabled": { "value": false },
|
||||
|
||||
"machine_max_jerk_xy": { "value": 10 },
|
||||
"machine_max_jerk_z": { "value": 0.4 },
|
||||
"machine_max_jerk_e": { "value": 5 },
|
||||
"machine_max_jerk_z": { "value": 0.3 },
|
||||
"machine_max_jerk_e": { "value": 15 },
|
||||
"jerk_print": { "value": 10 },
|
||||
"jerk_travel": { "value": "jerk_print" },
|
||||
"jerk_travel_layer_0": { "value": "jerk_travel" },
|
||||
@ -99,7 +99,6 @@
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_enable": { "value": true },
|
||||
"retraction_speed": { "value": 30 },
|
||||
"retraction_amount": { "value": 7 },
|
||||
"retraction_count_max": { "value": 100 },
|
||||
|
@ -64,55 +64,6 @@
|
||||
"tizyx_abs",
|
||||
"tizyx_pla",
|
||||
"tizyx_pla_bois",
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"verbatim_bvoh_175",
|
||||
"zyyx_pro_flex",
|
||||
"zyyx_pro_pla"
|
||||
@ -155,35 +106,25 @@
|
||||
"jerk_enabled": { "value": false },
|
||||
|
||||
"speed_print": { "value": 60.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" },
|
||||
"speed_layer_0": { "value": 20.0 },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_support": { "value": "speed_wall_0" },
|
||||
"speed_support_interface": { "value": "speed_topbottom" },
|
||||
"speed_z_hop": { "value": 5 },
|
||||
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
|
||||
"line_width": { "value": "machine_nozzle_size * 1.1" },
|
||||
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
"optimize_wall_printing_order": { "value": true },
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_none'" },
|
||||
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
@ -193,15 +134,18 @@
|
||||
|
||||
"fill_outline_gaps": { "value": false },
|
||||
|
||||
"retraction_speed": {
|
||||
"retraction_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_retract_speed": {
|
||||
"retraction_retract_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"retraction_prime_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
@ -215,13 +159,12 @@
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_amount": { "value": 2 },
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
"adhesion_type": { "value": "'none' if support_enable else 'skirt'" },
|
||||
@ -236,14 +179,13 @@
|
||||
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
|
||||
|
||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width / 2.0 / layer_height)))" },
|
||||
"support_pattern": { "value": "'zigzag'" },
|
||||
|
||||
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
|
||||
"support_use_towers": { "value": false },
|
||||
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
|
||||
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
|
||||
"support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" },
|
||||
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
|
||||
"support_wall_count": { "value": 1 },
|
||||
"support_brim_enable": { "value": true },
|
||||
"support_brim_width": { "value": 4 },
|
||||
|
||||
@ -256,7 +198,6 @@
|
||||
"minimum_interface_area": { "value": 10 },
|
||||
"top_bottom_thickness": {"value": "layer_height_0 + layer_height * 3" },
|
||||
"wall_thickness": {"value": "line_width * 2" }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,55 +23,6 @@
|
||||
"supports_usb_connection": false,
|
||||
"supports_network_connection": false,
|
||||
"exclude_materials": [
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"chromatik_pla",
|
||||
"dsm_arnitel2045_175",
|
||||
"dsm_novamid1070_175",
|
||||
@ -128,55 +79,6 @@
|
||||
"tizyx_pla",
|
||||
"tizyx_pla_bois",
|
||||
"tizyx_pva",
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"verbatim_bvoh_175",
|
||||
"Vertex_Delta_ABS",
|
||||
"Vertex_Delta_PET",
|
||||
|
@ -2,7 +2,8 @@
|
||||
"version": 2,
|
||||
"name": "Atom 3",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Daniel Kurth",
|
||||
"manufacturer": "Layer One",
|
||||
@ -22,10 +23,8 @@
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
"overrides": {
|
||||
|
||||
"overrides":
|
||||
{
|
||||
"machine_name": { "default_value": "Atom 3" },
|
||||
"machine_show_variants": { "default_value": true},
|
||||
"machine_shape": { "default_value": "elliptic" },
|
||||
@ -48,45 +47,36 @@
|
||||
"machine_gcode_flavor": { "RepRap (Marlin/Sprinter)": "Marlin" },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"material_diameter": { "default_value": 1.75},
|
||||
"machine_start_gcode": {
|
||||
"machine_start_gcode":
|
||||
{
|
||||
"default_value": ";MACHINE START CODE\nG21 ;metric values\nG90 ;absolute positioning\nG28 ;home\nG1 Z5 F9000\n;MACHINE START CODE"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"machine_end_gcode":
|
||||
{
|
||||
"default_value": ";MACHINE END CODE\nG91 ;relative positioning\nG1 E-1 F300 ;retract filament release pressure\nG1 Z+1.0 E-5 F9000 ;move up a and retract more\nG90 ;absolute positioning\nG28; home\nM84 ;steppers off\n;MACHINE END CODE"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
"layer_height": {"default_value": 0.2 },
|
||||
"layer_height_0": {
|
||||
"layer_height_0":
|
||||
{
|
||||
"default_value": 0.2,
|
||||
"value": "layer_height"
|
||||
},
|
||||
"line_width": { "value": "machine_nozzle_size"},
|
||||
"infill_line_width": { "value":"line_width"},
|
||||
|
||||
"initial_layer_line_width_factor": { "default_value": 100},
|
||||
|
||||
|
||||
|
||||
|
||||
"top_bottom_thickness": { "default_value": 1.0},
|
||||
|
||||
|
||||
|
||||
|
||||
"infill_sparse_density": { "default_value": 17},
|
||||
"infill_before_walls": { "value": false},
|
||||
"zig_zaggify_infill": { "value": true},
|
||||
|
||||
|
||||
|
||||
|
||||
"default_material_print_temperature": { "default_value": 200 },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature + 0"},
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature_layer_0"},
|
||||
"material_final_print_temperature": { "value": "material_print_temperature"},
|
||||
"default_material_bed_temperature": {
|
||||
"default_material_bed_temperature":
|
||||
{
|
||||
"default_value": 60,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "build_volume_temperature",
|
||||
@ -95,16 +85,13 @@
|
||||
},
|
||||
"material_bed_temperature":
|
||||
{
|
||||
"value": "round(default_material_bed_temperature-((-0.202*default_material_bed_temperature)+7.16)) if default_material_bed_temperature > 40 else default_material_bed_temperature",
|
||||
"value": "round(default_material_bed_temperature-(-0.202 * default_material_bed_temperature + 7.16)) if default_material_bed_temperature > 40 else default_material_bed_temperature",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "build_volume_temperature",
|
||||
"maximum_value_warning": "115",
|
||||
"maximum_value": "120"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
"speed_print": { "default_value": 40},
|
||||
"speed_wall": { "value": "speed_print * 0.75"},
|
||||
"speed_wall_0": { "value": "speed_print * 0.5"},
|
||||
@ -112,48 +99,42 @@
|
||||
"speed_layer_0": { "value": "20"},
|
||||
"speed_slowdown_layers": { "default_value": 1},
|
||||
|
||||
|
||||
|
||||
|
||||
"retraction_amount": {
|
||||
"retraction_amount":
|
||||
{
|
||||
"default_value": 7,
|
||||
"maximum_value_warning": 9 },
|
||||
"retraction_speed": {
|
||||
"maximum_value_warning": 9
|
||||
},
|
||||
"retraction_speed":
|
||||
{
|
||||
"default_value": 70,
|
||||
"maximum_value_warning": 80
|
||||
},
|
||||
"retraction_hop_enabled": { "default_value": true},
|
||||
"retraction_hop": { "default_value": 0.5},
|
||||
|
||||
|
||||
|
||||
|
||||
"cool_min_layer_time": { "default_value": 5},
|
||||
"cool_min_speed": { "default_value": 10},
|
||||
"cool_lift_head": { "default_value": false},
|
||||
|
||||
|
||||
|
||||
|
||||
"support_type": { "default_value": "everywhere"},
|
||||
"support_angle": { "default_value": 60},
|
||||
"support_z_distance": { "value": "layer_height"},
|
||||
"support_xy_distance_overhang":{"value": "machine_nozzle_size"},
|
||||
|
||||
|
||||
|
||||
|
||||
"adhesion_type": { "default_value": "skirt"},
|
||||
"skirt_brim_minimal_length": {
|
||||
"skirt_brim_minimal_length":
|
||||
{
|
||||
"default_value": 750,
|
||||
"value": "60/(layer_height_0*line_width)",
|
||||
"value": "60 / (layer_height_0 * line_width)",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "25",
|
||||
"maximum_value_warning": "4000"
|
||||
},
|
||||
"skirt_gap": {
|
||||
"default_value": "1`",
|
||||
"value": "3*wall_line_width_0"
|
||||
"skirt_gap":
|
||||
{
|
||||
"default_value": 1,
|
||||
"value": "3 * wall_line_width_0"
|
||||
}
|
||||
}
|
||||
}
|
@ -59,15 +59,11 @@
|
||||
"jerk_enabled": { "value": false },
|
||||
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" },
|
||||
"speed_layer_0": { "value": 20.0 },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_support": { "value": "speed_wall_0" },
|
||||
@ -76,19 +72,16 @@
|
||||
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
"z_seam_type": { "value": "'sharpest_corner'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_inner'" },
|
||||
|
||||
"infill_line_width": { "value": "line_width * 1.2" },
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
@ -111,7 +104,6 @@
|
||||
"maximum_value": 200
|
||||
},
|
||||
|
||||
"retraction_hop_enabled": { "value": "False" },
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
@ -119,13 +111,11 @@
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_enable": { "value": true },
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
|
@ -165,9 +165,6 @@
|
||||
"skin_overlap": {
|
||||
"value": 10.0
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_travel_layer_0": {
|
||||
"value": "math.ceil(speed_travel * 0.4)"
|
||||
},
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
"switch_extruder_retraction_speeds": {"default_value": 15 },
|
||||
"switch_extruder_retraction_amount": {"value": 1 },
|
||||
|
||||
"acceleration_enabled": { "value": true },
|
||||
"speed_travel": { "value": "100" },
|
||||
"speed_layer_0": { "value": "20" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
@ -54,7 +54,6 @@
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
"acceleration_enabled": { "value": "True" },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
|
||||
"acceleration_print": { "value": "3000" },
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
"infill_pattern": {"value": "'triangles'" },
|
||||
"infill_before_walls": {"value": false },
|
||||
|
||||
"acceleration_enabled": { "value": true },
|
||||
"default_material_print_temperature": { "value": "215" },
|
||||
"material_print_temperature_layer_0": { "value": "material_print_temperature + 5" },
|
||||
"material_standby_temperature": { "value": "material_print_temperature" },
|
||||
@ -54,7 +54,6 @@
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
"acceleration_enabled": { "value": "True" },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
|
||||
"acceleration_print": { "value": "3000" },
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
"switch_extruder_retraction_speeds": {"default_value": 15 },
|
||||
"switch_extruder_retraction_amount": {"value": 1 },
|
||||
|
||||
"acceleration_enabled": { "value": true },
|
||||
"speed_travel": { "value": "100" },
|
||||
"speed_layer_0": { "value": "20" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
@ -53,7 +53,6 @@
|
||||
|
||||
"prime_blob_enable": { "enabled": true },
|
||||
|
||||
"acceleration_enabled": { "value": "True" },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" },
|
||||
"acceleration_print": { "value": "3000" },
|
||||
|
@ -41,7 +41,6 @@
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"material_print_temp_wait": { "default_value": false },
|
||||
"material_bed_temp_wait": { "default_value": false },
|
||||
"prime_tower_enable": { "default_value": false },
|
||||
"prime_tower_min_volume": { "value": "0.7" },
|
||||
"prime_tower_size": { "value": 24.0 },
|
||||
"prime_tower_position_x": { "value": "125" },
|
||||
|
@ -36,9 +36,8 @@
|
||||
"min_infill_area": { "default_value": 2.0 },
|
||||
"retract_at_layer_change": { "default_value": true },
|
||||
"default_material_print_temperature": { "default_value": 210 },
|
||||
"material_print_temperature": { "value": 210 },
|
||||
"material_final_print_temperature": { "value": 210 },
|
||||
"material_bed_temperature": { "value": 70 },
|
||||
"default_material_bed_temperature": { "value": 70 },
|
||||
"material_bed_temperature_layer_0": { "value": 70 },
|
||||
"material_flow_layer_0": {"value": 140},
|
||||
"retraction_amount": { "default_value": 10 },
|
||||
|
@ -68,61 +68,13 @@
|
||||
"tizyx_abs",
|
||||
"tizyx_pla",
|
||||
"tizyx_pla_bois",
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"verbatim_bvoh_175",
|
||||
"zyyx_pro_flex",
|
||||
"zyyx_pro_pla"
|
||||
]
|
||||
},
|
||||
"overrides": {
|
||||
"overrides":
|
||||
{
|
||||
"machine_name": { "default_value": "Creawsome Base Printer" },
|
||||
"machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" },
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
|
||||
@ -158,36 +110,25 @@
|
||||
"acceleration_enabled": { "value": false },
|
||||
"jerk_enabled": { "value": false },
|
||||
|
||||
"speed_print": { "value": 50.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
"speed_print": { "value": 50.0 },
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" },
|
||||
"speed_layer_0": { "value": 20.0 },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_support": { "value": "speed_wall_0" },
|
||||
"speed_support_interface": { "value": "speed_topbottom" },
|
||||
"speed_z_hop": { "value": 5 },
|
||||
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
@ -197,34 +138,33 @@
|
||||
|
||||
"fill_outline_gaps": { "value": false },
|
||||
|
||||
"retraction_speed": {
|
||||
"retraction_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_retract_speed": {
|
||||
"retraction_retract_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
"retraction_prime_speed": {
|
||||
"retraction_prime_speed":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
|
||||
"retraction_hop_enabled": { "value": "False" },
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
"travel_avoid_other_parts": { "value": true },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
@ -238,7 +178,7 @@
|
||||
"meshfix_maximum_resolution": { "value": "0.25" },
|
||||
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
|
||||
|
||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width/2.0/layer_height)))" },
|
||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width / 2.0 /layer_height)))" },
|
||||
"support_pattern": { "value": "'zigzag'" },
|
||||
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
|
||||
"support_use_towers": { "value": false },
|
||||
|
@ -9,17 +9,6 @@
|
||||
"machine_width": { "default_value": 260 },
|
||||
"machine_depth": { "default_value": 260 },
|
||||
"machine_height": { "default_value": 400 },
|
||||
"z_seam_type": { "value": "'sharpest_corner'"},
|
||||
"z_seam_corner": { "value": "'z_seam_corner_inner'"},
|
||||
"infill_sparse_density": { "value": "10"},
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'grid'"},
|
||||
"infill_overlap":{"value": 10},
|
||||
"material_print_temperature":{"value": 220},
|
||||
"material_bed_temperature":{"value": 50},
|
||||
"retraction_amount":{"value": 10},
|
||||
"speed_travel": { "value": 80.0 },
|
||||
"coasting_enable": { "value": true},
|
||||
"coasting_min_volume": { "value": 0.5},
|
||||
"machine_head_with_fans_polygon": { "default_value": [
|
||||
[-26, 34],
|
||||
[-26, -32],
|
||||
@ -30,9 +19,8 @@
|
||||
|
||||
"gantry_height": { "value": 25 },
|
||||
|
||||
"speed_print": { "value": 50.0 },
|
||||
"speed_wall": { "value": 30.0 }
|
||||
|
||||
"speed_print": { "value": 80.0 }
|
||||
|
||||
},
|
||||
"metadata": {
|
||||
"quality_definition": "creality_base",
|
||||
|
@ -24,7 +24,7 @@
|
||||
"xy_offset": { "value": 0.1 },
|
||||
"xy_offset_layer_0": { "value": -0.1 },
|
||||
"hole_xy_offset": { "value": 0.15 },
|
||||
"material_print_temperature": { "value": 200 },
|
||||
"material_print_temperature_layer_0": { "value": "default_material_bed_temperature + 15" },
|
||||
"speed_travel": { "value": 100 },
|
||||
"speed_layer_0": { "value": 25 },
|
||||
"acceleration_enabled": { "value": true },
|
||||
|
@ -30,9 +30,6 @@
|
||||
},
|
||||
|
||||
"cool_fan_speed": { "value": 50 },
|
||||
"coasting_enable": { "value": true },
|
||||
"coasting_volume": { "value": 0.05 },
|
||||
"coasting_min_volume": { "value": 1.0 },
|
||||
"jerk_enabled": { "value": false }
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
"machine_max_jerk_e": { "value": 5 },
|
||||
"machine_max_feedrate_e": { "default_value": 200 },
|
||||
"machine_steps_per_mm_e": { "default_value": 139.5 },
|
||||
"acceleration_enabled": { "value": "True" },
|
||||
"acceleration_enabled": { "value": true },
|
||||
"acceleration_print": { "value": "1250", "maximum_value": 1500 },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom", "maximum_value": 1500 },
|
||||
"acceleration_topbottom": { "value": "math.ceil(acceleration_print * 1000 / 1250)", "maximum_value": 1500 },
|
||||
|
@ -25,7 +25,7 @@
|
||||
"machine_end_gcode": { "default_value": ";---------------------------------------\n;Deltacomb end script\n;---------------------------------------\nG91 ;relative positioning\nG1 X8.0 E-10 F6000 ;wipe filament+material retraction\nG1 Z2 E9 ;Lift and start filament shaping\nG1 E-9\nG1 E8\nG1 E-8\nG1 E-10 F110\nG1 E-40 F5000 ; move to park position\nG28 ;home all axes (max endstops)\nM84 ;steppers off" },
|
||||
"switch_extruder_retraction_amount": { "value": "0" },
|
||||
"prime_tower_min_volume": { "value": "50" },
|
||||
"prime_tower_enable": { "value": "1" },
|
||||
"prime_tower_enable": { "value": true },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_standby_temperature": { "value": "material_print_temperature" }
|
||||
|
@ -25,7 +25,7 @@
|
||||
"machine_end_gcode": { "default_value": ";---------------------------------------\n;Deltacomb end script\n;---------------------------------------\nG91 ;relative positioning\nG1 X8.0 E-10 F6000 ;wipe filament+material retraction\nG1 Z2 E9 ;Lift and start filament shaping\nG1 E-9\nG1 E8\nG1 E-8\nG1 E-10 F110\nG1 E-40 F5000 ; move to park position\nG28 ;home all axes (max endstops)\nM84 ;steppers off" },
|
||||
"switch_extruder_retraction_amount": { "value": "0" },
|
||||
"prime_tower_min_volume": { "value": "50" },
|
||||
"prime_tower_enable": { "value": "1" },
|
||||
"prime_tower_enable": { "value": true },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_standby_temperature": { "value": "material_print_temperature" }
|
||||
|
@ -70,9 +70,6 @@
|
||||
"machine_heated_bed": {
|
||||
"default_value": true
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall_x": {
|
||||
"value": "speed_wall"
|
||||
},
|
||||
|
@ -53,7 +53,7 @@
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91 ;Relative positioning\nG1 Z5 F720 ;Raise Z\nG1 E-5 F300 ;Retract a bit to protect nozzle\nM104 S0 ;Turn off extruder\nM140 S0 ;Turn off bed\nM107 ;Turn off all fans\nG90 ;Absolute positioning\nG1 X230 Y200 F4800 ;Parking the hotend\nM84 X Y E ;All steppers off but left Z\n"
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"default_material_print_temperature": {
|
||||
"value": 205
|
||||
},
|
||||
"material_print_temperature_layer_0": {
|
||||
@ -103,7 +103,7 @@
|
||||
"infill_before_walls": {
|
||||
"value": false
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"default_material_bed_temperature": {
|
||||
"value": 60
|
||||
},
|
||||
"material_bed_temperature_layer_0": {
|
||||
@ -156,9 +156,6 @@
|
||||
"skirt_line_count": {
|
||||
"value": 3
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_topbottom": {
|
||||
"value": "math.ceil(speed_print * 20 / 50)"
|
||||
},
|
||||
|
@ -138,15 +138,11 @@
|
||||
"value": "material_print_temperature"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"value": "default_material_bed_temperature",
|
||||
"maximum_value_warning": 100
|
||||
},
|
||||
"material_bed_temperature_layer_0": {
|
||||
"value": "material_bed_temperature"
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
@ -253,9 +249,6 @@
|
||||
},
|
||||
"smooth_spiralized_contours": {
|
||||
"value": false
|
||||
},
|
||||
"roofing_monotonic": {
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
}
|
@ -426,9 +426,7 @@
|
||||
"label": "Disallowed Areas",
|
||||
"description": "A list of polygons with areas the print head is not allowed to enter.",
|
||||
"type": "polygons",
|
||||
"default_value":
|
||||
[
|
||||
],
|
||||
"default_value": [],
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
@ -438,9 +436,7 @@
|
||||
"label": "Nozzle Disallowed Areas",
|
||||
"description": "A list of polygons with areas the nozzle is not allowed to enter.",
|
||||
"type": "polygons",
|
||||
"default_value":
|
||||
[
|
||||
],
|
||||
"default_value": [],
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
@ -452,22 +448,10 @@
|
||||
"type": "polygon",
|
||||
"default_value":
|
||||
[
|
||||
[
|
||||
-20,
|
||||
10
|
||||
],
|
||||
[
|
||||
10,
|
||||
10
|
||||
],
|
||||
[
|
||||
10,
|
||||
-10
|
||||
],
|
||||
[
|
||||
-20,
|
||||
-10
|
||||
]
|
||||
[ -20, 10 ],
|
||||
[ 10, 10 ],
|
||||
[ 10, -10 ],
|
||||
[ -20, -10 ]
|
||||
],
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
@ -1027,7 +1011,8 @@
|
||||
"settable_per_meshgroup": true,
|
||||
"settable_globally": true,
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
"children": {
|
||||
"children":
|
||||
{
|
||||
"wall_0_extruder_nr":
|
||||
{
|
||||
"label": "Outer Wall Extruder",
|
||||
@ -1184,9 +1169,10 @@
|
||||
"inset_direction":
|
||||
{
|
||||
"label": "Wall Ordering",
|
||||
"description": "Determines the order in which walls are printed. Printing outer walls earlier helps with dimensional accuracy, as faults from inner walls cannot propagate to the outside. However printing them later allows them to stack better when overhangs are printed.",
|
||||
"description": "Determines the order in which walls are printed. Printing outer walls earlier helps with dimensional accuracy, as faults from inner walls cannot propagate to the outside. However printing them later allows them to stack better when overhangs are printed. When there is an uneven amount of total innner walls, the 'center last line' is always printed last.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"options":
|
||||
{
|
||||
"inside_out": "Inside To Outside",
|
||||
"outside_in": "Outside To Inside"
|
||||
},
|
||||
@ -1241,7 +1227,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"fill_outline_gaps": {
|
||||
"fill_outline_gaps":
|
||||
{
|
||||
"label": "Print Thin Walls",
|
||||
"description": "Print pieces of the model which are horizontally thinner than the nozzle size.",
|
||||
"type": "bool",
|
||||
@ -1443,7 +1430,63 @@
|
||||
"value": "0",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "top_layers > 0"
|
||||
"enabled": "top_layers > 0",
|
||||
"children":
|
||||
{
|
||||
"roofing_line_width":
|
||||
{
|
||||
"label": "Top Surface Skin Line Width",
|
||||
"description": "Width of a single line of the areas at the top of the print.",
|
||||
"unit": "mm",
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
|
||||
"maximum_value_warning": "2 * machine_nozzle_size",
|
||||
"default_value": 0.4,
|
||||
"type": "float",
|
||||
"value": "skin_line_width",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0"
|
||||
},
|
||||
"roofing_pattern":
|
||||
{
|
||||
"label": "Top Surface Skin Pattern",
|
||||
"description": "The pattern of the top most layers.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"lines": "Lines",
|
||||
"concentric": "Concentric",
|
||||
"zigzag": "Zig Zag"
|
||||
},
|
||||
"default_value": "lines",
|
||||
"value": "top_bottom_pattern",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0"
|
||||
},
|
||||
"roofing_monotonic":
|
||||
{
|
||||
"label": "Monotonic Top Surface Order",
|
||||
"description": "Print top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes flat surfaces look more consistent.",
|
||||
"type": "bool",
|
||||
"value": true,
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0 and roofing_pattern != 'concentric'",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"roofing_angles":
|
||||
{
|
||||
"label": "Top Surface Skin Line Directions",
|
||||
"description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).",
|
||||
"type": "[int]",
|
||||
"default_value": "[ ]",
|
||||
"value": "skin_angles",
|
||||
"enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"top_bottom_extruder_nr":
|
||||
{
|
||||
@ -2943,6 +2986,49 @@
|
||||
"maximum_value_warning": "150",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"wall_x_material_flow_layer_0":
|
||||
{
|
||||
"label": "Initial Layer Inner Wall Flow",
|
||||
"description": "Flow compensation on the outermost wall line of the first layer.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"default_value": 100,
|
||||
"value": "material_flow_layer_0",
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"wall_0_material_flow_layer_0":
|
||||
{
|
||||
"label": "Initial Layer Outer Wall Flow",
|
||||
"description": "Flow compensation on the outermost wall line of the first layer.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"default_value": 100,
|
||||
"value": "material_flow_layer_0",
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"skin_material_flow_layer_0":
|
||||
{
|
||||
"label": "Initial Layer Bottom Flow",
|
||||
"description": "Flow compensation on bottom lines of the first layer",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"default_value": 100,
|
||||
"value": "material_flow_layer_0",
|
||||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "bottom_layers > 0",
|
||||
"limit_to_extruder": "top_bottom_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"material_standby_temperature":
|
||||
{
|
||||
"label": "Standby Temperature",
|
||||
@ -4362,7 +4448,8 @@
|
||||
"enabled": "(support_enable or support_meshes_present) and extruders_enabled_count > 1",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"children": {
|
||||
"children":
|
||||
{
|
||||
"support_infill_extruder_nr":
|
||||
{
|
||||
"label": "Support Infill Extruder",
|
||||
@ -4482,6 +4569,20 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_max_diameter":
|
||||
{
|
||||
"label": "Tree Support Trunk Diameter",
|
||||
"description": "The diameter of the widest branches of tree support. A thicker trunk is more sturdy; a thinner trunk takes up less space on the build plate.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "support_tree_branch_diameter",
|
||||
"minimum_value_warning": "support_line_width * 5",
|
||||
"default_value": 15,
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"enabled": "support_enable and support_structure=='tree'",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_tree_branch_diameter_angle":
|
||||
{
|
||||
"label": "Tree Support Branch Diameter Angle",
|
||||
@ -4513,6 +4614,42 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_conical_enabled": {
|
||||
"label": "Enable Conical Support",
|
||||
"description": "Make support areas smaller at the bottom than at the overhang.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "support_enable and support_structure != 'tree'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_conical_angle": {
|
||||
"label": "Conical Support Angle",
|
||||
"description": "The angle of the tilt of conical support. With 0 degrees being vertical, and 90 degrees being horizontal. Smaller angles cause the support to be more sturdy, but consist of more material. Negative angles cause the base of the support to be wider than the top.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "-90",
|
||||
"minimum_value_warning": "-45",
|
||||
"maximum_value_warning": "45",
|
||||
"maximum_value": "90",
|
||||
"default_value": 30,
|
||||
"enabled": "support_conical_enabled and support_enable and support_structure != 'tree'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_conical_min_width": {
|
||||
"label": "Conical Support Minimum Width",
|
||||
"description": "Minimum width to which the base of the conical support area is reduced. Small widths can lead to unstable support structures.",
|
||||
"unit": "mm",
|
||||
"default_value": 5.0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "machine_nozzle_size * 3",
|
||||
"maximum_value_warning": "100.0",
|
||||
"type": "float",
|
||||
"enabled": "support_conical_enabled and support_enable and support_structure != 'tree' and support_conical_angle > 0",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_type":
|
||||
{
|
||||
"label": "Support Placement",
|
||||
@ -6335,6 +6472,7 @@
|
||||
"description": "Remove the holes in each layer and keep only the outside shape. This will ignore any invisible internal geometry. However, it also ignores layer holes which can be viewed from above or below.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": "magic_spiralize",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"meshfix_extensive_stitching":
|
||||
@ -6645,60 +6783,6 @@
|
||||
"default_value": "middle",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"roofing_line_width":
|
||||
{
|
||||
"label": "Top Surface Skin Line Width",
|
||||
"description": "Width of a single line of the areas at the top of the print.",
|
||||
"unit": "mm",
|
||||
"minimum_value": "0.001",
|
||||
"minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
|
||||
"maximum_value_warning": "2 * machine_nozzle_size",
|
||||
"default_value": 0.4,
|
||||
"type": "float",
|
||||
"value": "skin_line_width",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0"
|
||||
},
|
||||
"roofing_pattern":
|
||||
{
|
||||
"label": "Top Surface Skin Pattern",
|
||||
"description": "The pattern of the top most layers.",
|
||||
"type": "enum",
|
||||
"options":
|
||||
{
|
||||
"lines": "Lines",
|
||||
"concentric": "Concentric",
|
||||
"zigzag": "Zig Zag"
|
||||
},
|
||||
"default_value": "lines",
|
||||
"value": "top_bottom_pattern",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0"
|
||||
},
|
||||
"roofing_monotonic":
|
||||
{
|
||||
"label": "Monotonic Top Surface Order",
|
||||
"description": "Print top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes flat surfaces look more consistent.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": "skin_monotonic",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0 and roofing_pattern != 'concentric'",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"roofing_angles":
|
||||
{
|
||||
"label": "Top Surface Skin Line Directions",
|
||||
"description": "A list of integer line directions to use when the top surface skin layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).",
|
||||
"type": "[int]",
|
||||
"default_value": "[ ]",
|
||||
"value": "skin_angles",
|
||||
"enabled": "roofing_pattern != 'concentric' and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "roofing_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"infill_enable_travel_optimization":
|
||||
{
|
||||
"label": "Infill Travel Optimization",
|
||||
@ -6950,45 +7034,6 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_conical_enabled":
|
||||
{
|
||||
"label": "Enable Conical Support",
|
||||
"description": "Make support areas smaller at the bottom than at the overhang.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "support_enable and support_structure != 'tree'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_conical_angle":
|
||||
{
|
||||
"label": "Conical Support Angle",
|
||||
"description": "The angle of the tilt of conical support. With 0 degrees being vertical, and 90 degrees being horizontal. Smaller angles cause the support to be more sturdy, but consist of more material. Negative angles cause the base of the support to be wider than the top.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"minimum_value": "-90",
|
||||
"minimum_value_warning": "-45",
|
||||
"maximum_value_warning": "45",
|
||||
"maximum_value": "90",
|
||||
"default_value": 30,
|
||||
"enabled": "support_conical_enabled and support_enable and support_structure != 'tree'",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"support_conical_min_width":
|
||||
{
|
||||
"label": "Conical Support Minimum Width",
|
||||
"description": "Minimum width to which the base of the conical support area is reduced. Small widths can lead to unstable support structures.",
|
||||
"unit": "mm",
|
||||
"default_value": 5.0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "machine_nozzle_size * 3",
|
||||
"maximum_value_warning": "100.0",
|
||||
"type": "float",
|
||||
"enabled": "support_conical_enabled and support_enable and support_structure != 'tree' and support_conical_angle > 0",
|
||||
"limit_to_extruder": "support_infill_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"magic_fuzzy_skin_enabled":
|
||||
{
|
||||
"label": "Fuzzy Skin",
|
||||
@ -7524,6 +7569,7 @@
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"default_value": 5,
|
||||
"value": "line_width + support_xy_distance + 1.0",
|
||||
"enabled": "bridge_settings_enabled",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false
|
||||
@ -8048,7 +8094,8 @@
|
||||
"description": "Settings which are only used if CuraEngine isn't called from the Cura frontend.",
|
||||
"type": "category",
|
||||
"enabled": false,
|
||||
"children": {
|
||||
"children":
|
||||
{
|
||||
"center_object":
|
||||
{
|
||||
"description": "Whether to center the object on the middle of the build platform (0,0), instead of using the coordinate system in which the object was saved.",
|
||||
|
@ -65,7 +65,7 @@
|
||||
"default_value": 15
|
||||
},
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G21 ; millimeter units\nG90 ; absolute coordinates\nM82 ; E absolute\nM140 S{material_bed_temperature}\nM104 S{material_print_temperature}\nM190 S{material_bed_temperature}\nM109 S{material_print_temperature}\nG28 ; home axis\nM420 S1 ; enable mesh leveling\n; Lower nozzle and move to start position\nG1 Z150\nG1 X-130 Y0 Z0.4 F3000\n; Extrude about 40 mm by printing a 90 degree arc\nG3 X0 Y-130 I130 Z0.3 E40 F2700\n; Retract and move nozzle up\nG92 E0\nG1 E-1.5 F1800\nG0 Z0.5\nG1 E0 F300\n"
|
||||
"default_value": "G21 ; millimeter units\nG90 ; absolute coordinates\nM82 ; E absolute\nM140 S{material_bed_temperature_layer_0}\nM104 S{material_print_temperature_layer_0}\nM190 S{material_bed_temperature_layer_0}\nM109 S{material_initial_print_temperature}\nG28 ; home axis\nM420 S1 ; enable mesh leveling\n; Lower nozzle and move to start position\nG1 Z150\nG1 X-130 Y0 Z0.4 F3000\n; Extrude about 40 mm by printing a 90 degree arc\nG3 X0 Y-130 I130 Z0.3 E40 F2700\n; Retract and move nozzle up\nG92 E0\nG1 E-1.5 F1800\nG0 Z0.5\nG1 E0 F300\n"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91 ; relative coordinates\nG1 E-1 F300 ; retract filament a bit before lifting\nG1 Z+5 E-5 F6000 ; raise platform from current position\nG28 X0 Y0 ; home axis\nG90 ; absolute coordinates\nG92 E0 ; reset extruder\nM104 S0 ; turn off hotend\nM140 S0 ; turn off heat bed\nM107 ; turn off fans\nM84 ; disable motors\n"
|
||||
@ -74,4 +74,4 @@
|
||||
"default_value": "Marlin"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,55 +88,6 @@
|
||||
"tizyx_pla",
|
||||
"tizyx_pla_bois",
|
||||
"tizyx_pva",
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"verbatim_bvoh_175",
|
||||
"Vertex_Delta_ABS",
|
||||
"Vertex_Delta_PET",
|
||||
@ -165,7 +116,6 @@
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
|
||||
"layer_height_0": { "value": 0.2 },
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
"skin_line_width": { "value": "machine_nozzle_size" },
|
||||
"infill_line_width": { "value": "line_width + 0.1" },
|
||||
"skirt_brim_line_width": { "value": "line_width + 0.1" },
|
||||
@ -189,25 +139,19 @@
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_flow": { "value": 100 },
|
||||
"retraction_enable": { "value": true },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
|
||||
"speed_print": { "value": 60 } ,
|
||||
"speed_infill": { "value": "speed_print * 1.5" },
|
||||
"speed_wall": { "value": "speed_print / 2" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
"speed_wall_x": { "value": "speed_print" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_topbottom": { "value": "speed_print / 2" },
|
||||
"speed_support": { "value": "speed_print" },
|
||||
"speed_support_interface": { "value": "speed_topbottom" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" },
|
||||
"speed_layer_0": { "value": "speed_print / 2" },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
"speed_z_hop": { "value": 5 },
|
||||
@ -220,7 +164,6 @@
|
||||
"retraction_hop_enabled": { "value": false },
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
|
59
resources/definitions/geeetech_Mizar_S.def.json
Normal file
59
resources/definitions/geeetech_Mizar_S.def.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "Geeetech Mizar_S",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata": {
|
||||
"visible": true,
|
||||
"author": "Amit L",
|
||||
"manufacturer": "Geeetech",
|
||||
"file_formats": "text/x-gcode",
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "geeetech_Mizar_S_1"
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
"machine_name": { "default_value": "Geeetech Mizar_S" },
|
||||
"machine_width": {
|
||||
"default_value": 255
|
||||
},
|
||||
"machine_height": {
|
||||
"default_value": 260
|
||||
},
|
||||
"machine_depth": {
|
||||
"default_value": 255
|
||||
},
|
||||
"machine_center_is_zero": {
|
||||
"default_value": false
|
||||
},
|
||||
"layer_height": { "default_value": 0.1 },
|
||||
"layer_height_0": { "default_value": 0.15 },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_speed": { "default_value": 35 },
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"machine_head_with_fans_polygon": { "default_value": [[-31,31],[34,31],[34,-40],[-31,-40]] },
|
||||
"gantry_height": { "value": "28" },
|
||||
"machine_max_feedrate_z": { "default_value": 12 },
|
||||
"machine_max_feedrate_e": { "default_value": 120 },
|
||||
"machine_max_acceleration_z": { "default_value": 500 },
|
||||
"machine_acceleration": { "default_value": 1000 },
|
||||
"machine_max_jerk_xy": { "default_value": 10 },
|
||||
"machine_max_jerk_z": { "default_value": 0.2 },
|
||||
"machine_max_jerk_e": { "default_value": 2.5 },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": {
|
||||
"default_value": "G28 ; Auto home\nG1 Z15 F300 ;Move up slightly\nM107 ;Off fans\nG90 ;Switch to absolute positioning\nM82 ;Extruder absolute mode\nG92 E0 ;Set position of extruder to 0\nG0 X10 Y20 F1500 ;Move to X10 Y20 at 1500mms\nG1 Z0.8 ;Move Z to 0.8\nG1 F300 X180 E40 ;Extrude a line of filament\nG1 F1200 Z2 ;Raise Z\nG92 E0 ;Set extruder position to zero\nG28 ;Auto home"
|
||||
},
|
||||
"machine_end_gcode": {
|
||||
"default_value": "G91 ;Switch to relative positioning\nG1 E-1 ;Retract filament to lower pressure\nG0 X0 Y200 ;Move hotend to left and bed forward\nM104 S0 ;Cooldown hotend\nG90 ;Switch to absolute mode\nG92 E0 ;Set extruder to zero\nM140 S0 ;Cooldown bed\nM84 ; Disable steppers"
|
||||
},
|
||||
"machine_extruder_count": {
|
||||
"default_value": 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -59,16 +59,13 @@
|
||||
"acceleration_enabled": { "value": false },
|
||||
"jerk_enabled": { "value": false },
|
||||
|
||||
"speed_print": { "value": 40.0 } ,
|
||||
"speed_infill": { "value": "speed_print" },
|
||||
"speed_print": { "value": 40.0 },
|
||||
"speed_wall": { "value": "speed_print" },
|
||||
"speed_wall_0": { "value": "speed_wall" },
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_topbottom": { "value": "speed_print" },
|
||||
"speed_roofing": { "value": "speed_topbottom" },
|
||||
"speed_travel": { "value": "80" },
|
||||
"speed_layer_0": { "value": 20.0 },
|
||||
"speed_print_layer_0": { "value": "speed_layer_0" },
|
||||
"speed_travel_layer_0": { "value": "60" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_support": { "value": "speed_wall_0" },
|
||||
@ -77,18 +74,21 @@
|
||||
|
||||
"skirt_brim_speed": { "value": "speed_layer_0" },
|
||||
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
|
||||
"material_print_temperature": { "maximum_value_warning": "421", "maximum_value": "500" },
|
||||
"material_print_temperature_layer_0": { "maximum_value_warning": "421", "maximum_value": "500" },
|
||||
"material_standby_temperature": { "maximum_value_warning": "421", "maximum_value": "500" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature", "maximum_value_warning": "421", "maximum_value": "500" },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature", "maximum_value_warning": "421", "maximum_value": "500" },
|
||||
"material_flow": { "value": 100 },
|
||||
|
||||
|
||||
"z_seam_type": { "value": "'back'" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
|
||||
"infill_sparse_density": { "value": "20" },
|
||||
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30.0 },
|
||||
@ -110,26 +110,20 @@
|
||||
"maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value": 200
|
||||
},
|
||||
|
||||
"retraction_hop_enabled": { "value": "False" },
|
||||
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
"travel_avoid_other_parts": { "value": true },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_min_travel": { "value": 1.5 },
|
||||
|
||||
"cool_fan_full_at_height": { "value": "3 * layer_height" },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
"cool_min_layer_time": { "value": 10 },
|
||||
|
||||
|
||||
|
||||
|
||||
"top_bottom_thickness": {"value": "layer_height_0 + layer_height * 3" },
|
||||
"wall_thickness": {"value": "line_width * 2" }
|
||||
|
@ -36,7 +36,6 @@
|
||||
"retraction_speed": { "default_value": 35},
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"gantry_height": { "value": "55" },
|
||||
"speed_print": { "default_value": 60.0 } ,
|
||||
@ -48,8 +47,6 @@
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-1 F2700 ;Retract a bit\nG1 Z1 E-1 F2400 ;Retract and raise Z\nG1 Z2 ;Raise Z\nG90 ;Absolute positioning\nG12 P1;clean nozzle\nG1 X5 Y300 ;Return to Start Point\nM106 S0 ;Switch off part cooling fan\nM104 S0 ;turn off temperature\nM140 S0 ;turn off Heated Bed\nM84 X Y E ;Disable all steppers but Z\nM81 ;turn-off power" },
|
||||
|
||||
"cool_fan_speed": { "value": 75 },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
|
||||
"line_width": { "value": 0.38 },
|
||||
"travel_avoid_distance": { "value": 1 },
|
||||
"infill_before_walls": { "value": false }
|
||||
|
@ -37,7 +37,6 @@
|
||||
"retraction_speed": { "default_value": 35},
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"gantry_height": { "value": "55" },
|
||||
"speed_print": { "default_value": 60.0 } ,
|
||||
@ -49,7 +48,6 @@
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-1 F2700 ;Retract a bit\nG1 Z1 E-1 F2400 ;Retract and raise Z\nG1 Z2 ;Raise Z\nG90 ;Absolute positioning\nG12 ;clean nozzle\nG1 X5 Y319 ;Return to Start Point\nM106 S0 ;Switch off part cooling fan\nM104 S0 ;turn off temperature\nM140 S0 ;turn off Heated Bed\nM84 X Y E ;Disable all steppers but Z\nM81 ;turn-off power" },
|
||||
|
||||
"cool_fan_speed": { "value": 75 },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
|
||||
"line_width": { "value": 0.38 },
|
||||
"travel_avoid_distance": { "value": 1 },
|
||||
|
@ -38,7 +38,6 @@
|
||||
"retraction_speed": { "default_value": 35},
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
"retraction_enable": { "value": true },
|
||||
|
||||
"gantry_height": { "value": "55" },
|
||||
"speed_print": { "default_value": 60.0 } ,
|
||||
@ -49,7 +48,6 @@
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 Z1 E-2 F2400 ;Retract and raise Z\nG1 Z2 ;Raise Z\nG90 ;Absolute positioning\nG1 X5 Y290 ;Return to Start Point\nM106 S0 ;Switch off part cooling fan\nM104 S0 ;turn off temperature\nM140 S0 ;turn off Heated Bed\nM84 X Y E ;Disable all steppers but Z" },
|
||||
|
||||
"cool_fan_speed": { "value": 75 },
|
||||
"cool_fan_enabled": { "value": true },
|
||||
|
||||
"line_width": { "value": 0.38 },
|
||||
"travel_avoid_distance": { "value": 1 },
|
||||
|
@ -108,12 +108,10 @@
|
||||
|
||||
"default_material_print_temperature": {"maximum_value": "401" },
|
||||
"material_print_temperature": {"maximum_value": "401" },
|
||||
"material_print_temperature_layer_0": {"value": "material_print_temperature",
|
||||
"maximum_value": "401" },
|
||||
"material_print_temperature_layer_0": {"maximum_value": "401" },
|
||||
"material_initial_print_temperature": {"value": "material_print_temperature",
|
||||
"maximum_value_warning": "material_print_temperature + 15",
|
||||
"maximum_value": "401" },
|
||||
"material_initial_print_temperature": {"maximum_value": "401" },
|
||||
"material_final_print_temperature": {"value": "material_print_temperature",
|
||||
"maximum_value": "401" },
|
||||
"material_break_preparation_temperature": {"maximum_value": "401" },
|
||||
@ -126,10 +124,9 @@
|
||||
"switch_extruder_prime_speed": {"value": "(retraction_prime_speed)"},
|
||||
|
||||
"speed_print": {"value": "50"},
|
||||
"speed_infill": {"value": "speed_print"},
|
||||
"speed_wall": {"value": "(speed_print/5*3) if speed_print > 45 else speed_print"},
|
||||
"speed_wall": {"value": "(speed_print/ 5 * 3) if speed_print > 45 else speed_print"},
|
||||
"speed_wall_x": {"value": "speed_wall"},
|
||||
"speed_layer_0": {"value": "(speed_print/5*4) if speed_print > 45 else speed_print"},
|
||||
"speed_layer_0": {"value": "(speed_print / 5 * 4) if speed_print > 45 else speed_print"},
|
||||
"speed_topbottom": {"value": "speed_layer_0"},
|
||||
"speed_travel": {"value": "250"},
|
||||
"speed_travel_layer_0": {"value": "speed_travel"},
|
||||
@ -167,7 +164,6 @@
|
||||
"skirt_gap": {"value": 1},
|
||||
"skirt_brim_minimal_length": {"value": 50},
|
||||
|
||||
"prime_tower_enable": {"value": false },
|
||||
"prime_tower_size": {"value": 20.6 },
|
||||
"prime_tower_position_x": {"value": 125 },
|
||||
"prime_tower_position_y": {"value": 70 },
|
||||
|
@ -53,55 +53,6 @@
|
||||
"tizyx_abs",
|
||||
"tizyx_pla_bois",
|
||||
"tizyx_pla",
|
||||
"ultimaker_abs_black",
|
||||
"ultimaker_abs_blue",
|
||||
"ultimaker_abs_green",
|
||||
"ultimaker_abs_grey",
|
||||
"ultimaker_abs_orange",
|
||||
"ultimaker_abs_pearl-gold",
|
||||
"ultimaker_abs_red",
|
||||
"ultimaker_abs_silver-metallic",
|
||||
"ultimaker_abs_white",
|
||||
"ultimaker_abs_yellow",
|
||||
"ultimaker_bam",
|
||||
"ultimaker_cpe_black",
|
||||
"ultimaker_cpe_blue",
|
||||
"ultimaker_cpe_dark-grey",
|
||||
"ultimaker_cpe_green",
|
||||
"ultimaker_cpe_light-grey",
|
||||
"ultimaker_cpe_plus_black",
|
||||
"ultimaker_cpe_plus_transparent",
|
||||
"ultimaker_cpe_plus_white",
|
||||
"ultimaker_cpe_red",
|
||||
"ultimaker_cpe_transparent",
|
||||
"ultimaker_cpe_white",
|
||||
"ultimaker_cpe_yellow",
|
||||
"ultimaker_nylon_black",
|
||||
"ultimaker_nylon_transparent",
|
||||
"ultimaker_pc_black",
|
||||
"ultimaker_pc_transparent",
|
||||
"ultimaker_pc_white",
|
||||
"ultimaker_pla_black",
|
||||
"ultimaker_pla_blue",
|
||||
"ultimaker_pla_green",
|
||||
"ultimaker_pla_magenta",
|
||||
"ultimaker_pla_orange",
|
||||
"ultimaker_pla_pearl-white",
|
||||
"ultimaker_pla_red",
|
||||
"ultimaker_pla_silver-metallic",
|
||||
"ultimaker_pla_transparent",
|
||||
"ultimaker_pla_white",
|
||||
"ultimaker_pla_yellow",
|
||||
"ultimaker_pp_transparent",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_tough_pla_black",
|
||||
"ultimaker_tough_pla_green",
|
||||
"ultimaker_tough_pla_red",
|
||||
"ultimaker_tough_pla_white",
|
||||
"ultimaker_tpu_black",
|
||||
"ultimaker_tpu_blue",
|
||||
"ultimaker_tpu_red",
|
||||
"ultimaker_tpu_white",
|
||||
"verbatim_bvoh_175",
|
||||
"Vertex_Delta_ABS",
|
||||
"Vertex_Delta_PET",
|
||||
|
@ -151,43 +151,32 @@
|
||||
"value": "4"
|
||||
},
|
||||
"default_material_print_temperature": {
|
||||
"maximum_value_warning": "470",
|
||||
"maximum_value": "470"
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"maximum_value_warning": "470",
|
||||
"maximum_value": "470"
|
||||
},
|
||||
"material_print_temperature_layer_0": {
|
||||
"value": "material_print_temperature",
|
||||
"maximum_value_warning": "470",
|
||||
"maximum_value": "470"
|
||||
},
|
||||
"material_initial_print_temperature": {
|
||||
"value": "material_print_temperature",
|
||||
"maximum_value_warning": "470",
|
||||
"maximum_value": "470"
|
||||
},
|
||||
"material_final_print_temperature": {
|
||||
"value": "material_print_temperature",
|
||||
"maximum_value_warning": "470",
|
||||
"maximum_value": "470"
|
||||
},
|
||||
"default_material_bed_temperature": {
|
||||
"maximum_value_warning": "150",
|
||||
"maximum_value": "150"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"maximum_value_warning": "150",
|
||||
"maximum_value": "150"
|
||||
},
|
||||
"material_bed_temperature_layer_0": {
|
||||
"maximum_value_warning": "150",
|
||||
"maximum_value": "150"
|
||||
},
|
||||
"speed_infill": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall": {
|
||||
"value": "speed_print"
|
||||
},
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user