mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-29 17:45:45 +08:00
Merge branch 'Ultimaker:main' into main
This commit is contained in:
commit
c04b96d721
2
.github/ISSUE_TEMPLATE/featurerequest.yaml
vendored
2
.github/ISSUE_TEMPLATE/featurerequest.yaml
vendored
@ -1,6 +1,6 @@
|
||||
name: Feature Request
|
||||
description: Suggest an idea for this project.
|
||||
labels: "Type: New Feature"
|
||||
labels: ["Type: New Feature", "Status: Triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
78
.github/workflows/conan-package-create.yml
vendored
78
.github/workflows/conan-package-create.yml
vendored
@ -3,10 +3,23 @@ name: Create and Upload Conan package
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
project_name:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
recipe_id_full:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
build_id:
|
||||
required: true
|
||||
type: number
|
||||
|
||||
build_info:
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
recipe_id_latest:
|
||||
required: false
|
||||
type: string
|
||||
@ -37,11 +50,6 @@ on:
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
create_from_source:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }}
|
||||
CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }}
|
||||
@ -84,7 +92,7 @@ jobs:
|
||||
path: |
|
||||
$HOME/.conan/data
|
||||
$HOME/.conan/conan_download_cache
|
||||
key: conan-${{ runner.os }}-${{ runner.arch }}-create-cache
|
||||
key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache
|
||||
|
||||
- name: Cache Conan local repository packages (Powershell)
|
||||
uses: actions/cache@v3
|
||||
@ -94,7 +102,7 @@ jobs:
|
||||
C:\Users\runneradmin\.conan\data
|
||||
C:\.conan
|
||||
C:\Users\runneradmin\.conan\conan_download_cache
|
||||
key: conan-${{ runner.os }}-${{ runner.arch }}-create-cache
|
||||
key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache
|
||||
|
||||
- name: Install MacOS system requirements
|
||||
if: ${{ runner.os == 'Macos' }}
|
||||
@ -106,7 +114,7 @@ jobs:
|
||||
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
|
||||
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 flex bison -y
|
||||
|
||||
- name: Install GCC-12 on ubuntu-22.04
|
||||
if: ${{ startsWith(inputs.runs_on, 'ubuntu-22.04') }}
|
||||
@ -115,6 +123,12 @@ jobs:
|
||||
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: Use GCC-10 on ubuntu-20.04
|
||||
if: ${{ startsWith(inputs.runs_on, 'ubuntu-20.04') }}
|
||||
run: |
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10
|
||||
|
||||
- name: Create the default Conan profile
|
||||
run: conan profile new default --detect
|
||||
|
||||
@ -126,28 +140,48 @@ jobs:
|
||||
if: ${{ inputs.conan_config_branch == '' }}
|
||||
run: conan config install https://github.com/Ultimaker/conan-config.git
|
||||
|
||||
- name: Create the lock file
|
||||
if: ${{ inputs.build_info }}
|
||||
run: |
|
||||
conan_build_info --v2 start ${{ inputs.project_name }} ${{ github.run_number }}000${{ inputs.build_id }}
|
||||
conan lock create --reference ${{ inputs.recipe_id_full }} --lockfile-out=conan.lock
|
||||
|
||||
- name: Create the Packages using lockfile
|
||||
if: ${{ inputs.build_info }}
|
||||
run: conan install ${{ inputs.recipe_id_full }} --build=missing --update --lockfile=conan.lock --lockfile-out=conan.lock
|
||||
|
||||
- name: Create the Packages
|
||||
if: ${{ !inputs.create_from_source }}
|
||||
if: ${{ ! inputs.build_info }}
|
||||
run: conan install ${{ inputs.recipe_id_full }} --build=missing --update
|
||||
|
||||
- name: Create the Packages (from source)
|
||||
if: ${{ inputs.create_from_source }}
|
||||
run: conan create . ${{ inputs.recipe_id_full }} --build=missing --update
|
||||
|
||||
- name: Remove the latest alias
|
||||
if: ${{ inputs.create_from_source && inputs.recipe_id_latest != '' && runner.os == 'Linux' }}
|
||||
run: |
|
||||
conan remove ${{ inputs.recipe_id_latest }} -r cura -f || true
|
||||
conan remove ${{ inputs.recipe_id_latest }} -r cura-ce -f || true
|
||||
|
||||
- name: Create the latest alias
|
||||
if: ${{ inputs.create_from_source && inputs.recipe_id_latest != '' && always() }}
|
||||
run: conan alias ${{ inputs.recipe_id_latest }} ${{ inputs.recipe_id_full }}
|
||||
- name: Create the build info
|
||||
if: ${{ inputs.build_info }}
|
||||
run: conan_build_info --v2 create buildinfo.json --lockfile conan.lock --user ${{ secrets.CONAN_USER }} --password ${{ secrets.CONAN_PASS }}
|
||||
|
||||
- name: Upload the Package(s)
|
||||
if: always()
|
||||
run: conan upload "*" -r cura --all -c
|
||||
|
||||
- name: Upload the build info
|
||||
if: ${{ inputs.build_info }}
|
||||
run: |
|
||||
conan_build_info --v2 publish buildinfo.json --url https://ultimaker.jfrog.io/artifactory --user ${{ secrets.CONAN_USER }} --password ${{ secrets.CONAN_PASS }}
|
||||
conan_build_info --v2 stop
|
||||
|
||||
- name: Upload the Package(s) community
|
||||
if: ${{ always() && inputs.conan_upload_community == true }}
|
||||
run: conan upload "*" -r cura-ce -c
|
||||
|
||||
- name: Upload the log and build artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: log-${{ inputs.runs_on }}-${{ runner.arch }}
|
||||
path: |
|
||||
buildinfo.json
|
||||
conan.lock
|
||||
conanbuildinfo.txt
|
||||
conaninfo.txt
|
||||
graph_info.json
|
||||
build/**
|
||||
retention-days: 1
|
||||
|
2
.github/workflows/conan-package.yml
vendored
2
.github/workflows/conan-package.yml
vendored
@ -81,7 +81,9 @@ jobs:
|
||||
|
||||
uses: ultimaker/cura/.github/workflows/conan-package-create.yml@main
|
||||
with:
|
||||
project_name: ${{ needs.conan-recipe-version.outputs.project_name }}
|
||||
recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }}
|
||||
build_id: 1
|
||||
runs_on: 'ubuntu-20.04'
|
||||
python_version: '3.10.x'
|
||||
conan_logging_level: 'info'
|
||||
|
61
.github/workflows/conan-recipe-version.yml
vendored
61
.github/workflows/conan-recipe-version.yml
vendored
@ -29,14 +29,18 @@ on:
|
||||
description: "is current branch a release branch?"
|
||||
value: ${{ jobs.get-semver.outputs.release_branch }}
|
||||
|
||||
recipe_user:
|
||||
user:
|
||||
description: "The conan user"
|
||||
value: ${{ jobs.get-semver.outputs.user }}
|
||||
|
||||
recipe_channel:
|
||||
channel:
|
||||
description: "The conan channel"
|
||||
value: ${{ jobs.get-semver.outputs.channel }}
|
||||
|
||||
project_name:
|
||||
description: "The conan projectname"
|
||||
value: ${{ inputs.project_name }}
|
||||
|
||||
jobs:
|
||||
get-semver:
|
||||
|
||||
@ -63,8 +67,7 @@ jobs:
|
||||
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 }}
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
- name: Setup Python and pip
|
||||
uses: actions/setup-python@v4
|
||||
@ -82,6 +85,7 @@ jobs:
|
||||
name: Get Conan broadcast data
|
||||
run: |
|
||||
import subprocess
|
||||
import os
|
||||
from conans import tools
|
||||
from conans.errors import ConanException
|
||||
from git import Repo
|
||||
@ -93,12 +97,13 @@ jobs:
|
||||
issue_number = "${{ github.ref }}".split('/')[2]
|
||||
is_tag = "${{ github.ref_type }}" == "tag"
|
||||
is_release_branch = False
|
||||
ref_name = "${{ github.base_ref }}" if event_name == "pull_request" else "${{ github.ref_name }}"
|
||||
buildmetadata = "" if "${{ inputs.additional_buildmetadata }}" == "" else "${{ inputs.additional_buildmetadata }}_"
|
||||
|
||||
# FIXME: for when we push a tag (such as an release)
|
||||
channel = "testing"
|
||||
if is_tag:
|
||||
branch_version = tools.Version("${{ github.ref_name }}")
|
||||
branch_version = tools.Version(ref_name)
|
||||
is_release_branch = True
|
||||
channel = "_"
|
||||
user = "_"
|
||||
@ -108,13 +113,13 @@ jobs:
|
||||
branch_version = tools.Version(repo.active_branch.name)
|
||||
except ConanException:
|
||||
branch_version = tools.Version('0.0.0')
|
||||
if "${{ github.ref_name }}" == f"{branch_version.major}.{branch_version.minor}":
|
||||
if ref_name == f"{branch_version.major}.{branch_version.minor}":
|
||||
channel = 'stable'
|
||||
is_release_branch = True
|
||||
elif "${{ github.ref_name }}" in ("main", "master"):
|
||||
elif ref_name in ("main", "master"):
|
||||
channel = 'testing'
|
||||
else:
|
||||
channel = repo.active_branch.name.split("_")[0].replace("-", "_").lower()
|
||||
channel = "_".join(repo.active_branch.name.replace("-", "_").split("_")[:2]).lower()
|
||||
|
||||
if "pull_request" in event_name:
|
||||
channel = f"pr_{issue_number}"
|
||||
@ -165,38 +170,30 @@ jobs:
|
||||
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:
|
||||
# FIXME: for external PR's
|
||||
actual_version = f"5.3.0-alpha+{buildmetadata}pr_{issue_number}"
|
||||
|
||||
if is_tag and "${{ github.ref_name }}" == "5.2.0-beta":
|
||||
actual_version = "5.2.0-beta"
|
||||
is_release_branch = True
|
||||
user = "_"
|
||||
channel = "_"
|
||||
# %% Set the environment output
|
||||
output_env = os.environ["GITHUB_OUTPUT"]
|
||||
content = ""
|
||||
if os.path.exists(output_env):
|
||||
with open(output_env, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# %% print to output
|
||||
cmd_name = ["echo", f"::set-output name=name::{project_name}"]
|
||||
subprocess.call(cmd_name)
|
||||
cmd_version = ["echo", f"::set-output name=version::{actual_version}"]
|
||||
subprocess.call(cmd_version)
|
||||
cmd_channel = ["echo", f"::set-output name=channel::{channel}"]
|
||||
subprocess.call(cmd_channel)
|
||||
cmd_id_full= ["echo", f"::set-output name=recipe_id_full::{project_name}/{actual_version}@{user}/{channel}"]
|
||||
subprocess.call(cmd_id_full)
|
||||
cmd_id_latest = ["echo", f"::set-output name=recipe_id_latest::{project_name}/latest@{user}/{channel}"]
|
||||
subprocess.call(cmd_id_latest)
|
||||
cmd_semver_full = ["echo", f"::set-output name=semver_full::{actual_version}"]
|
||||
subprocess.call(cmd_semver_full)
|
||||
cmd_is_release_branch = ["echo", f"::set-output name=is_release_branch::{str(is_release_branch).lower()}"]
|
||||
subprocess.call(cmd_is_release_branch)
|
||||
with open(output_env, "w") as f:
|
||||
f.write(content)
|
||||
f.writelines(f"name={project_name}\n")
|
||||
f.writelines(f"version={actual_version}\n")
|
||||
f.writelines(f"channel={channel}\n")
|
||||
f.writelines(f"recipe_id_full={project_name}/{actual_version}@{user}/{channel}\n")
|
||||
f.writelines(f"recipe_id_latest={project_name}/latest@{user}/{channel}\n")
|
||||
f.writelines(f"semver_full={actual_version}\n")
|
||||
f.writelines(f"is_release_branch={str(is_release_branch).lower()}\n")
|
||||
|
||||
print("::group::Conan Recipe Information")
|
||||
print(f"name = {project_name}")
|
||||
print(f"version = {actual_version}")
|
||||
print(f"user = {user}")
|
||||
print(f"channel = {channel}")
|
||||
print(f"= {project_name}/{actual_version}@{user}/{channel}")
|
||||
print(f"recipe_id_full = {project_name}/{actual_version}@{user}/{channel}")
|
||||
print(f"recipe_id_latest = {project_name}/latest@{user}/{channel}")
|
||||
print(f"semver_full = {actual_version}")
|
||||
print(f"is_release_branch = {str(is_release_branch).lower()}")
|
||||
|
112
.github/workflows/cura-all-installers.yml
vendored
Normal file
112
.github/workflows/cura-all-installers.yml
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
name: Cura All Installers
|
||||
run-name: ${{ inputs.cura_conan_version }} by @${{ github.actor }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
cura_conan_version:
|
||||
description: 'Cura Conan Version'
|
||||
default: 'cura/latest@ultimaker/testing'
|
||||
required: true
|
||||
type: string
|
||||
conan_args:
|
||||
description: 'Conan args: eq.: --require-override'
|
||||
default: ''
|
||||
required: false
|
||||
type: string
|
||||
conan_config:
|
||||
description: 'Conan config branch to use'
|
||||
default: ''
|
||||
required: false
|
||||
type: string
|
||||
enterprise:
|
||||
description: 'Build Cura as an Enterprise edition'
|
||||
default: false
|
||||
required: true
|
||||
type: boolean
|
||||
staging:
|
||||
description: 'Use staging API'
|
||||
default: false
|
||||
required: true
|
||||
type: boolean
|
||||
installer:
|
||||
description: 'Create the installer'
|
||||
default: true
|
||||
required: true
|
||||
type: boolean
|
||||
build_windows:
|
||||
description: 'Build for Windows'
|
||||
default: true
|
||||
required: true
|
||||
type: boolean
|
||||
build_linux:
|
||||
description: 'Build for Linux'
|
||||
default: true
|
||||
required: true
|
||||
type: boolean
|
||||
build_macos:
|
||||
description: 'Build for MacOs'
|
||||
default: true
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
# Run the nightly at 3:25 UTC on working days
|
||||
schedule:
|
||||
- cron: '25 3 * * 1-5'
|
||||
|
||||
jobs:
|
||||
windows-installer-create:
|
||||
if: ${{ inputs.build_windows }}
|
||||
uses: ./.github/workflows/cura-installer.yml
|
||||
with:
|
||||
platform: 'windows-2022'
|
||||
os_name: 'win64'
|
||||
cura_conan_version: ${{ inputs.cura_conan_version }}
|
||||
conan_args: ${{ inputs.conan_args }}
|
||||
conan_config: ${{ inputs.conan_config }}
|
||||
enterprise: ${{ inputs.enterprise }}
|
||||
staging: ${{ inputs.staging }}
|
||||
installer: ${{ inputs.installer }}
|
||||
secrets: inherit
|
||||
|
||||
linux-installer-create:
|
||||
if: ${{ inputs.build_linux }}
|
||||
uses: ./.github/workflows/cura-installer.yml
|
||||
with:
|
||||
platform: 'ubuntu-20.04'
|
||||
os_name: 'linux'
|
||||
cura_conan_version: ${{ inputs.cura_conan_version }}
|
||||
conan_args: ${{ inputs.conan_args }}
|
||||
conan_config: ${{ inputs.conan_config }}
|
||||
enterprise: ${{ inputs.enterprise }}
|
||||
staging: ${{ inputs.staging }}
|
||||
installer: ${{ inputs.installer }}
|
||||
secrets: inherit
|
||||
|
||||
linux-modern-installer-create:
|
||||
if: ${{ inputs.build_linux }}
|
||||
uses: ./.github/workflows/cura-installer.yml
|
||||
with:
|
||||
platform: 'ubuntu-22.04'
|
||||
os_name: 'linux-modern'
|
||||
cura_conan_version: ${{ inputs.cura_conan_version }}
|
||||
conan_args: ${{ inputs.conan_args }}
|
||||
conan_config: ${{ inputs.conan_config }}
|
||||
enterprise: ${{ inputs.enterprise }}
|
||||
staging: ${{ inputs.staging }}
|
||||
installer: ${{ inputs.installer }}
|
||||
secrets: inherit
|
||||
|
||||
macos-installer-create:
|
||||
if: ${{ inputs.build_macos }}
|
||||
uses: ./.github/workflows/cura-installer.yml
|
||||
with:
|
||||
platform: 'macos-11'
|
||||
os_name: 'mac'
|
||||
cura_conan_version: ${{ inputs.cura_conan_version }}
|
||||
conan_args: ${{ inputs.conan_args }}
|
||||
conan_config: ${{ inputs.conan_config }}
|
||||
enterprise: ${{ inputs.enterprise }}
|
||||
staging: ${{ inputs.staging }}
|
||||
installer: ${{ inputs.installer }}
|
||||
secrets: inherit
|
61
.github/workflows/cura-installer.yml
vendored
61
.github/workflows/cura-installer.yml
vendored
@ -1,42 +1,50 @@
|
||||
name: Cura Installer
|
||||
run-name: ${{ inputs.cura_conan_version }} by @${{ github.actor }}
|
||||
run-name: ${{ inputs.cura_conan_version }} for ${{ inputs.platform }} by @${{ github.actor }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
platform:
|
||||
description: 'Selected Installer OS'
|
||||
default: 'ubuntu-20.04'
|
||||
required: true
|
||||
type: string
|
||||
os_name:
|
||||
description: 'OS Friendly Name'
|
||||
default: 'linux'
|
||||
required: true
|
||||
type: string
|
||||
cura_conan_version:
|
||||
description: 'Cura Conan Version'
|
||||
default: 'cura/latest@ultimaker/testing'
|
||||
required: true
|
||||
type: string
|
||||
conan_args:
|
||||
description: 'Conan args: eq.: --require-override'
|
||||
default: ''
|
||||
required: false
|
||||
type: string
|
||||
conan_config:
|
||||
description: 'Conan config branch to use'
|
||||
default: ''
|
||||
required: false
|
||||
type: string
|
||||
enterprise:
|
||||
description: 'Build Cura as an Enterprise edition'
|
||||
required: true
|
||||
default: false
|
||||
required: true
|
||||
type: boolean
|
||||
staging:
|
||||
description: 'Use staging API'
|
||||
required: true
|
||||
default: false
|
||||
required: true
|
||||
type: boolean
|
||||
installer:
|
||||
description: 'Create the installer'
|
||||
required: true
|
||||
default: true
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
# Run the nightly at 3:25 UTC on working days
|
||||
#FIXME: Provide the same default values as the workflow dispatch
|
||||
schedule:
|
||||
- cron: '25 3 * * 1-5'
|
||||
|
||||
env:
|
||||
CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }}
|
||||
CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }}
|
||||
@ -59,16 +67,7 @@ env:
|
||||
|
||||
jobs:
|
||||
cura-installer-create:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: macos-11, os_id: 'mac' }
|
||||
- { os: windows-2022, os_id: 'win64' }
|
||||
- { os: ubuntu-20.04, os_id: 'linux' }
|
||||
- { os: ubuntu-22.04, os_id: 'linux-modern' }
|
||||
runs-on: ${{ inputs.platform }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -127,12 +126,18 @@ jobs:
|
||||
echo "APPIMAGETOOL_LOCATION=$GITHUB_WORKSPACE/appimagetool" >> $GITHUB_ENV
|
||||
|
||||
- name: Install GCC-12 on ubuntu-22.04
|
||||
if: ${{ matrix.os == '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: Use GCC-10 on ubuntu-20.04
|
||||
if: ${{ startsWith(inputs.runs_on, 'ubuntu-20.04') }}
|
||||
run: |
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10
|
||||
|
||||
- name: Create the default Conan profile
|
||||
run: conan profile new default --detect
|
||||
|
||||
@ -207,38 +212,38 @@ jobs:
|
||||
cp openssl/lib/*.lib ./cura_inst/Lib/
|
||||
|
||||
- name: Create the Cura dist
|
||||
run: pyinstaller ./cura_inst/Ultimaker-Cura.spec
|
||||
run: pyinstaller ./cura_inst/UltiMaker-Cura.spec
|
||||
|
||||
- name: Archive the artifacts (bash)
|
||||
if: ${{ github.event.inputs.installer == 'false' && runner.os != 'Windows' }}
|
||||
run: tar -zcf "./Ultimaker-Cura-$CURA_VERSION_FULL-${{ matrix.os_id }}.tar.gz" "./Ultimaker-Cura/"
|
||||
run: tar -zcf "./UltiMaker-Cura-$CURA_VERSION_FULL-${{ inputs.os_name }}.tar.gz" "./UltiMaker-Cura/"
|
||||
working-directory: dist
|
||||
|
||||
- name: Archive the artifacts (Powershell)
|
||||
if: ${{ github.event.inputs.installer == 'false' && runner.os == 'Windows' }}
|
||||
run: Compress-Archive -Path ".\Ultimaker-Cura" -DestinationPath ".\Ultimaker-Cura-$Env:CURA_VERSION_FULL-${{ matrix.os_id }}.zip"
|
||||
run: Compress-Archive -Path ".\UltiMaker-Cura" -DestinationPath ".\UltiMaker-Cura-$Env:CURA_VERSION_FULL-${{ inputs.os_name }}.zip"
|
||||
working-directory: dist
|
||||
|
||||
- name: Create the Windows exe installer (Powershell)
|
||||
if: ${{ github.event.inputs.installer == 'true' && runner.os == 'Windows' }}
|
||||
run: |
|
||||
python ..\cura_inst\packaging\NSIS\create_windows_installer.py ../cura_inst . "Ultimaker-Cura-$Env:CURA_VERSION_FULL-${{ matrix.os_id }}.exe"
|
||||
python ..\cura_inst\packaging\NSIS\create_windows_installer.py ../cura_inst . "UltiMaker-Cura-$Env:CURA_VERSION_FULL-${{ inputs.os_name }}.exe"
|
||||
working-directory: dist
|
||||
|
||||
- name: Create the Linux AppImage (Bash)
|
||||
if: ${{ github.event.inputs.installer == 'true' && runner.os == 'Linux' }}
|
||||
run: python ../cura_inst/packaging/AppImage/create_appimage.py ./Ultimaker-Cura $CURA_VERSION_FULL "Ultimaker-Cura-$CURA_VERSION_FULL-${{ matrix.os_id }}.AppImage"
|
||||
run: python ../cura_inst/packaging/AppImage/create_appimage.py ./UltiMaker-Cura $CURA_VERSION_FULL "UltiMaker-Cura-$CURA_VERSION_FULL-${{ inputs.os_name }}.AppImage"
|
||||
working-directory: dist
|
||||
|
||||
- name: Create the MacOS dmg (Bash)
|
||||
if: ${{ github.event.inputs.installer == 'true' && runner.os == 'Macos' }}
|
||||
run: python ../cura_inst/packaging/dmg/dmg_sign_noterize.py ../cura_inst . "Ultimaker-Cura-$CURA_VERSION_FULL-${{ matrix.os_id }}.dmg"
|
||||
run: python ../cura_inst/packaging/dmg/dmg_sign_noterize.py ../cura_inst . "UltiMaker-Cura-$CURA_VERSION_FULL-${{ inputs.os_name }}.dmg"
|
||||
working-directory: dist
|
||||
|
||||
- name: Upload the artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Ultimaker-Cura-${{ env.CURA_VERSION_FULL }}-${{ matrix.os_id }}
|
||||
name: UltiMaker-Cura-${{ env.CURA_VERSION_FULL }}-${{ inputs.os_name }}
|
||||
path: |
|
||||
dist/*.tar.gz
|
||||
dist/*.zip
|
||||
|
@ -19,6 +19,7 @@ on:
|
||||
- 'resources/intent/ultimaker**'
|
||||
- 'resources/quality/ultimaker**'
|
||||
- 'resources/variants/ultimaker**'
|
||||
permissions: {}
|
||||
jobs:
|
||||
slackNotification:
|
||||
name: Slack Notification
|
||||
|
45
.github/workflows/printer-linter-format.yml
vendored
Normal file
45
.github/workflows/printer-linter-format.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: printer-linter-format
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- '[1-9].[0-9]'
|
||||
- '[1-9].[0-9][0-9]'
|
||||
path:
|
||||
- 'resources/**'
|
||||
|
||||
jobs:
|
||||
printer-linter-format:
|
||||
name: Printer linter auto format
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python and pip
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11.x
|
||||
cache: 'pip'
|
||||
cache-dependency-path: .github/workflows/requirements-printer-linter.txt
|
||||
|
||||
- uses: technote-space/get-diff-action@v6
|
||||
with:
|
||||
PATTERNS: |
|
||||
resources/+(definitions|extruders)/*.def.json
|
||||
resources/+(intent|quality|variants)/**/*.inst.cfg
|
||||
|
||||
- name: Install Python requirements for runner
|
||||
if: env.GIT_DIFF && !env.MATCHED_FILES
|
||||
run: pip install -r .github/workflows/requirements-printer-linter.txt
|
||||
|
||||
# - name: Format file
|
||||
# if: env.GIT_DIFF && !env.MATCHED_FILES
|
||||
# run: python printer-linter/src/terminal.py --format ${{ env.GIT_DIFF_FILTERED }}
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
if: env.GIT_DIFF && !env.MATCHED_FILES
|
||||
with:
|
||||
commit_message: "Applied printer-linter format"
|
59
.github/workflows/printer-linter-pr-diagnose.yml
vendored
Normal file
59
.github/workflows/printer-linter-pr-diagnose.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
name: printer-linter-pr-diagnose
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
path:
|
||||
- 'resources/**'
|
||||
|
||||
jobs:
|
||||
printer-linter-diagnose:
|
||||
name: Printer linter PR diagnose
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Setup Python and pip
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.11.x
|
||||
cache: 'pip'
|
||||
cache-dependency-path: .github/workflows/requirements-printer-linter.txt
|
||||
|
||||
- uses: technote-space/get-diff-action@v6
|
||||
with:
|
||||
PATTERNS: |
|
||||
resources/+(extruders|definitions)/*.def.json
|
||||
resources/+(intent|quality|variants)/**/*.inst.cfg
|
||||
|
||||
- name: Install Python requirements for runner
|
||||
if: env.GIT_DIFF && !env.MATCHED_FILES
|
||||
run: pip install -r .github/workflows/requirements-printer-linter.txt
|
||||
|
||||
- name: Create results directory
|
||||
run: mkdir printer-linter-result
|
||||
|
||||
- name: Diagnose file(s)
|
||||
if: env.GIT_DIFF && !env.MATCHED_FILES
|
||||
run: python printer-linter/src/terminal.py --diagnose --report printer-linter-result/fixes.yml ${{ env.GIT_DIFF_FILTERED }}
|
||||
|
||||
- name: Save PR metadata
|
||||
run: |
|
||||
echo ${{ github.event.number }} > printer-linter-result/pr-id.txt
|
||||
echo ${{ github.event.pull_request.head.repo.full_name }} > printer-linter-result/pr-head-repo.txt
|
||||
echo ${{ github.event.pull_request.head.ref }} > printer-linter-result/pr-head-ref.txt
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: printer-linter-result
|
||||
path: printer-linter-result/
|
||||
|
||||
- name: Run clang-tidy-pr-comments action
|
||||
uses: platisd/clang-tidy-pr-comments@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
clang_tidy_fixes: result.yml
|
||||
request_changes: true
|
80
.github/workflows/printer-linter-pr-post.yml
vendored
Normal file
80
.github/workflows/printer-linter-pr-post.yml
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
name: printer-linter-pr-post
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "printer-linter-pr-diagnose" ]
|
||||
types: [ completed ]
|
||||
|
||||
jobs:
|
||||
clang-tidy-results:
|
||||
# Trigger the job only if the previous (insecure) workflow completed successfully
|
||||
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download analysis results
|
||||
uses: actions/github-script@v3.1.0
|
||||
with:
|
||||
script: |
|
||||
let artifacts = await github.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "printer-linter-result"
|
||||
})[0];
|
||||
let download = await github.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: "zip",
|
||||
});
|
||||
let fs = require("fs");
|
||||
fs.writeFileSync("${{github.workspace}}/printer-linter-result.zip", Buffer.from(download.data));
|
||||
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
mkdir printer-linter-result
|
||||
unzip printer-linter-result.zip -d printer-linter-result
|
||||
echo "pr_id=$(cat printer-linter-result/pr-id.txt)" >> $GITHUB_ENV
|
||||
echo "pr_head_repo=$(cat printer-linter-result/pr-head-repo.txt)" >> $GITHUB_ENV
|
||||
echo "pr_head_ref=$(cat printer-linter-result/pr-head-ref.txt)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ env.pr_head_repo }}
|
||||
ref: ${{ env.pr_head_ref }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Redownload analysis results
|
||||
uses: actions/github-script@v3.1.0
|
||||
with:
|
||||
script: |
|
||||
let artifacts = await github.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{github.event.workflow_run.id }},
|
||||
});
|
||||
let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "printer-linter-result"
|
||||
})[0];
|
||||
let download = await github.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: "zip",
|
||||
});
|
||||
let fs = require("fs");
|
||||
fs.writeFileSync("${{github.workspace}}/printer-linter-result.zip", Buffer.from(download.data));
|
||||
|
||||
- name: Extract analysis results
|
||||
run: |
|
||||
mkdir printer-linter-result
|
||||
unzip printer-linter-result.zip -d printer-linter-result
|
||||
|
||||
- name: Run clang-tidy-pr-comments action
|
||||
uses: platisd/clang-tidy-pr-comments@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
clang_tidy_fixes: printer-linter-result/fixes.yml
|
||||
pull_request_id: ${{ env.pr_id }}
|
1
.github/workflows/requirements-printer-linter.txt
vendored
Normal file
1
.github/workflows/requirements-printer-linter.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
pyyaml
|
9
.github/workflows/unit-test.yml
vendored
9
.github/workflows/unit-test.yml
vendored
@ -107,17 +107,14 @@ jobs:
|
||||
- name: Install Linux system requirements
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
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') }}
|
||||
- name: Use GCC-10 on ubuntu-20.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
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10
|
||||
|
||||
- name: Get Conan configuration
|
||||
run: conan config install https://github.com/Ultimaker/conan-config.git
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -99,3 +99,4 @@ conanbuildinfo.txt
|
||||
graph_info.json
|
||||
Ultimaker-Cura.spec
|
||||
.run/
|
||||
/printer-linter/src/printerlinter.egg-info/
|
||||
|
15
.printer-linter
Normal file
15
.printer-linter
Normal file
@ -0,0 +1,15 @@
|
||||
checks:
|
||||
diagnostic-mesh-file-extension: true
|
||||
diagnostic-mesh-file-size: true
|
||||
diagnostic-definition-redundant-override: true
|
||||
fixes:
|
||||
diagnostic-definition-redundant-override: true
|
||||
format:
|
||||
format-definition-bracket-newline: true
|
||||
format-definition-paired-coordinate-array: true
|
||||
format-definition-sort-keys: true
|
||||
format-definition-indent: 4
|
||||
format-definition-single-value-single-line: true # Format dicts and lists with a single item on one line "dict": { "value": 10 }
|
||||
format-profile-space-around-delimiters: true
|
||||
format-profile-sort-keys: true
|
||||
diagnostic-mesh-file-size: 1200000
|
14
CITATION.cff
14
CITATION.cff
@ -14,17 +14,17 @@ authors:
|
||||
contact:
|
||||
- email: info@ultimaker.com
|
||||
name: "Ultimaker B.V."
|
||||
url: 'https://ultimaker.com/software/ultimaker-cura'
|
||||
repository-code: 'https://github.com/Ultimaker/Cura'
|
||||
url: "https://ultimaker.com/software/ultimaker-cura"
|
||||
repository-code: "https://github.com/Ultimaker/Cura"
|
||||
license: LGPL-3.0
|
||||
license-url: "https://github.com/Ultimaker/Cura/blob/main/LICENSE"
|
||||
version: 5.0.0
|
||||
date-released: '2022-05-17'
|
||||
version: 5.2.1
|
||||
date-released: "2022-10-19"
|
||||
keywords:
|
||||
- Ultimaker
|
||||
- Cura
|
||||
- Slicer
|
||||
- Uranium
|
||||
- Arachne
|
||||
- 3DPrinting
|
||||
- Slicer
|
||||
...
|
||||
- 3D Printing
|
||||
- Additive Manufacturing
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
CuraAppName = "{{ cura_app_name }}"
|
||||
|
215
conandata.yml
215
conandata.yml
@ -10,6 +10,221 @@
|
||||
# requirements (use the <dep_name>/(latest)@ultimaker/testing)
|
||||
#
|
||||
# Subject to change in the future!
|
||||
"5.3.0-alpha":
|
||||
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
|
||||
cura_private_data_plugins:
|
||||
package: "cura_private_data"
|
||||
src: "plugins"
|
||||
dst: "share/cura/plugins"
|
||||
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.1":
|
||||
requirements:
|
||||
- "pyarcus/5.2.0"
|
||||
- "curaengine/5.2.1"
|
||||
- "pysavitar/5.2.0"
|
||||
- "pynest2d/5.2.0"
|
||||
- "uranium/5.2.0"
|
||||
- "fdm_materials/5.2.0"
|
||||
- "cura_binary_data/5.2.1"
|
||||
- "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-beta.2":
|
||||
requirements:
|
||||
- "pyarcus/(latest)@ultimaker/stable"
|
||||
|
55
conanfile.py
55
conanfile.py
@ -4,23 +4,24 @@ from pathlib import Path
|
||||
from jinja2 import Template
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.files import copy, rmdir, save
|
||||
from conan.tools.files import copy, rmdir, save, mkdir
|
||||
from conan.tools.microsoft import unix_path
|
||||
from conan.tools.env import VirtualRunEnv, Environment
|
||||
from conan.tools.scm import Version
|
||||
from conan.errors import ConanInvalidConfiguration, ConanException
|
||||
|
||||
required_conan_version = ">=1.50.0"
|
||||
required_conan_version = ">=1.52.0"
|
||||
|
||||
|
||||
class CuraConan(ConanFile):
|
||||
name = "cura"
|
||||
license = "LGPL-3.0"
|
||||
author = "Ultimaker B.V."
|
||||
author = "UltiMaker"
|
||||
url = "https://github.com/Ultimaker/cura"
|
||||
description = "3D printer / slicing GUI built on top of the Uranium framework"
|
||||
topics = ("conan", "python", "pyqt5", "qt", "qml", "3d-printing", "slicer")
|
||||
build_policy = "missing"
|
||||
exports = "LICENSE*", "Ultimaker-Cura.spec.jinja", "CuraVersion.py.jinja"
|
||||
exports = "LICENSE*", "UltiMaker-Cura.spec.jinja", "CuraVersion.py.jinja"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
no_copy_source = True # We won't build so no need to copy sources to the build folder
|
||||
|
||||
@ -43,7 +44,7 @@ class CuraConan(ConanFile):
|
||||
"staging": "False",
|
||||
"devtools": False,
|
||||
"cloud_api_version": "1",
|
||||
"display_name": "Ultimaker Cura",
|
||||
"display_name": "UltiMaker Cura",
|
||||
"cura_debug_mode": False, # Not yet implemented
|
||||
"internal": False,
|
||||
}
|
||||
@ -225,13 +226,13 @@ class CuraConan(ConanFile):
|
||||
# Collect all dll's from PyQt6 and place them in the root
|
||||
binaries.extend([(f"{p}", ".") for p in Path(self._site_packages, "PyQt6", "Qt6").glob("**/*.dll")])
|
||||
|
||||
with open(Path(__file__).parent.joinpath("Ultimaker-Cura.spec.jinja"), "r") as f:
|
||||
with open(Path(__file__).parent.joinpath("UltiMaker-Cura.spec.jinja"), "r") as f:
|
||||
pyinstaller = Template(f.read())
|
||||
|
||||
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:
|
||||
with open(Path(location, "UltiMaker-Cura.spec"), "w") as f:
|
||||
f.write(pyinstaller.render(
|
||||
name = str(self.options.display_name).replace(" ", "-"),
|
||||
display_name = self.options.display_name,
|
||||
@ -274,6 +275,12 @@ class CuraConan(ConanFile):
|
||||
for req in self._um_data()["internal_requirements"]:
|
||||
self.requires(req)
|
||||
|
||||
def build_requirements(self):
|
||||
if self.options.devtools:
|
||||
if self.settings.os != "Windows" or self.conf.get("tools.microsoft.bash:path", check_type = str):
|
||||
# FIXME: once m4, autoconf, automake are Conan V2 ready use self.win_bash and add gettext as base tool_requirement
|
||||
self.tool_requires("gettext/0.21", force_host_context=True)
|
||||
|
||||
def layout(self):
|
||||
self.folders.source = "."
|
||||
self.folders.build = "venv"
|
||||
@ -284,7 +291,14 @@ class CuraConan(ConanFile):
|
||||
self.cpp.package.resdirs = ["resources", "plugins", "packaging", "pip_requirements"] # pip_requirements should be the last item in the list
|
||||
|
||||
def build(self):
|
||||
pass
|
||||
if self.options.devtools:
|
||||
if self.settings.os != "Windows" or self.conf.get("tools.microsoft.bash:path", check_type = str):
|
||||
# FIXME: once m4, autoconf, automake are Conan V2 ready use self.win_bash and add gettext as base tool_requirement
|
||||
cpp_info = self.dependencies["gettext"].cpp_info
|
||||
for po_file in self.source_path.joinpath("resources", "i18n").glob("**/*.po"):
|
||||
mo_file = self.build_path.joinpath(po_file.with_suffix('.mo').relative_to(self.source_path))
|
||||
mkdir(self, str(unix_path(self, mo_file.parent)))
|
||||
self.run(f"{cpp_info.bindirs[0]}/msgfmt {po_file} -o {mo_file} -f", env="conanbuild", ignore_errors=True)
|
||||
|
||||
def generate(self):
|
||||
cura_run_envvars = self._cura_run_env.vars(self, scope = "run")
|
||||
@ -303,6 +317,16 @@ class CuraConan(ConanFile):
|
||||
icon_path = "'{}'".format(Path(self.source_folder, "packaging", self._um_data()["pyinstaller"]["icon"][str(self.settings.os)])).replace("\\", "\\\\"),
|
||||
entitlements_file = entitlements_file if self.settings.os == "Macos" else "None")
|
||||
|
||||
# Update the po files
|
||||
if self.settings.os != "Windows" or self.conf.get("tools.microsoft.bash:path", check_type = str):
|
||||
# FIXME: once m4, autoconf, automake are Conan V2 ready use self.win_bash and add gettext as base tool_requirement
|
||||
cpp_info = self.dependencies["gettext"].cpp_info
|
||||
for po_file in self.source_path.joinpath("resources", "i18n").glob("**/*.po"):
|
||||
pot_file = self.source_path.joinpath("resources", "i18n", po_file.with_suffix('.pot').name)
|
||||
mkdir(self, str(unix_path(self, pot_file.parent)))
|
||||
self.run(f"{cpp_info.bindirs[0]}/msgmerge --no-wrap --no-fuzzy-matching -width=140 -o {po_file} {po_file} {pot_file}",
|
||||
env = "conanbuild", ignore_errors = True)
|
||||
|
||||
def imports(self):
|
||||
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)
|
||||
@ -357,6 +381,8 @@ class CuraConan(ConanFile):
|
||||
dst = self._share_dir.joinpath("cura", "resources", "materials"), keep_path = False)
|
||||
self.copy_deps("*", root_package = "cura_private_data", src = self.deps_cpp_info["cura_private_data"].resdirs[0],
|
||||
dst = self._share_dir.joinpath("cura", "resources"), keep_path = True)
|
||||
self.copy_deps("*", root_package = "cura_private_data", src = self.deps_cpp_info["cura_private_data"].resdirs[1],
|
||||
dst = self._share_dir.joinpath("cura", "plugins"), keep_path = True)
|
||||
|
||||
# Copy resources of Uranium (keep folder structure)
|
||||
self.copy_deps("*", root_package = "uranium", src = self.deps_cpp_info["uranium"].resdirs[0],
|
||||
@ -418,12 +444,13 @@ echo "CURA_VERSION_FULL={{ cura_version_full }}" >> ${{ env_prefix }}GITHUB_ENV
|
||||
entitlements_file = entitlements_file if self.settings.os == "Macos" else "None")
|
||||
|
||||
def package(self):
|
||||
self.copy("cura_app.py", src = ".", dst = self.cpp.package.bindirs[0])
|
||||
self.copy("*", src = "cura", dst = self.cpp.package.libdirs[0])
|
||||
self.copy("*", src = "resources", dst = self.cpp.package.resdirs[0])
|
||||
self.copy("*", src = "plugins", dst = self.cpp.package.resdirs[1])
|
||||
self.copy("requirement*.txt", src = ".", dst = self.cpp.package.resdirs[-1])
|
||||
self.copy("*", src = "packaging", dst = self.cpp.package.resdirs[2])
|
||||
copy(self, "cura_app.py", src = self.source_path, dst = self.package_path.joinpath(self.cpp.package.bindirs[0]))
|
||||
copy(self, "*", src = self.source_path.joinpath("cura"), dst = self.package_path.joinpath(self.cpp.package.libdirs[0]))
|
||||
copy(self, "*", src = self.source_path.joinpath("resources"), dst = self.package_path.joinpath(self.cpp.package.resdirs[0]), excludes="*.po")
|
||||
copy(self, "*", src = self.build_path.joinpath("resources"), dst = self.package_path.joinpath(self.cpp.package.resdirs[0]))
|
||||
copy(self, "*", src = self.source_path.joinpath("plugins"), dst = self.package_path.joinpath(self.cpp.package.resdirs[1]))
|
||||
copy(self, "requirement*.txt", src = self.source_path, dst = self.package_path.joinpath(self.cpp.package.resdirs[-1]))
|
||||
copy(self, "*", src = self.source_path.joinpath("packaging"), dst = self.package_path.joinpath(self.cpp.package.resdirs[2]))
|
||||
|
||||
def package_info(self):
|
||||
self.user_info.pip_requirements = "requirements.txt"
|
||||
|
@ -1,11 +1,11 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
# ---------
|
||||
# General constants used in Cura
|
||||
# ---------
|
||||
DEFAULT_CURA_APP_NAME = "cura"
|
||||
DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura"
|
||||
DEFAULT_CURA_DISPLAY_NAME = "UltiMaker Cura"
|
||||
DEFAULT_CURA_VERSION = "dev"
|
||||
DEFAULT_CURA_BUILD_TYPE = ""
|
||||
DEFAULT_CURA_DEBUG_MODE = False
|
||||
|
@ -810,11 +810,6 @@ class BuildVolume(SceneNode):
|
||||
break
|
||||
if prime_tower_collision: # Already found a collision.
|
||||
break
|
||||
if self._global_container_stack.getProperty("prime_tower_brim_enable", "value") and self._global_container_stack.getProperty("adhesion_type", "value") != "raft":
|
||||
brim_size = self._calculateBedAdhesionSize(used_extruders, "brim")
|
||||
# Use 2x the brim size, since we need 1x brim size distance due to the object brim and another
|
||||
# times the brim due to the brim of the prime tower
|
||||
prime_tower_areas[extruder_id][area_index] = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(2 * brim_size, num_segments = 24))
|
||||
if not prime_tower_collision:
|
||||
result_areas[extruder_id].extend(prime_tower_areas[extruder_id])
|
||||
result_areas_no_brim[extruder_id].extend(prime_tower_areas[extruder_id])
|
||||
@ -840,9 +835,13 @@ class BuildVolume(SceneNode):
|
||||
|
||||
result = {}
|
||||
skirt_brim_extruder: ExtruderStack = None
|
||||
skirt_brim_extruder_nr = self._global_container_stack.getProperty("skirt_brim_extruder_nr", "value")
|
||||
|
||||
for extruder in used_extruders:
|
||||
if int(extruder.getProperty("extruder_nr", "value")) == int(self._global_container_stack.getProperty("skirt_brim_extruder_nr", "value")):
|
||||
skirt_brim_extruder = extruder
|
||||
if skirt_brim_extruder_nr == -1:
|
||||
skirt_brim_extruder = used_extruders[0] # The prime tower brim is always printed with the first extruder
|
||||
elif int(extruder.getProperty("extruder_nr", "value")) == int(skirt_brim_extruder_nr):
|
||||
skirt_brim_extruder = extruder
|
||||
result[extruder.getId()] = []
|
||||
|
||||
# Currently, the only normally printed object is the prime tower.
|
||||
@ -856,15 +855,6 @@ class BuildVolume(SceneNode):
|
||||
prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left.
|
||||
prime_tower_y = prime_tower_y + machine_depth / 2
|
||||
|
||||
if skirt_brim_extruder is not None and self._global_container_stack.getProperty("prime_tower_brim_enable", "value") and self._global_container_stack.getProperty("adhesion_type", "value") != "raft":
|
||||
brim_size = (
|
||||
skirt_brim_extruder.getProperty("brim_line_count", "value") *
|
||||
skirt_brim_extruder.getProperty("skirt_brim_line_width", "value") / 100.0 *
|
||||
skirt_brim_extruder.getProperty("initial_layer_line_width_factor", "value")
|
||||
)
|
||||
prime_tower_x -= brim_size
|
||||
prime_tower_y += brim_size
|
||||
|
||||
radius = prime_tower_size / 2
|
||||
prime_tower_area = Polygon.approximatedCircle(radius, num_segments = 24)
|
||||
prime_tower_area = prime_tower_area.translate(prime_tower_x - radius, prime_tower_y - radius)
|
||||
@ -1076,7 +1066,7 @@ class BuildVolume(SceneNode):
|
||||
all_values[i] = 0
|
||||
return all_values
|
||||
|
||||
def _calculateBedAdhesionSize(self, used_extruders, adhesion_override = None):
|
||||
def _calculateBedAdhesionSize(self, used_extruders):
|
||||
"""Get the bed adhesion size for the global container stack and used extruders
|
||||
|
||||
:param adhesion_override: override adhesion type.
|
||||
@ -1086,52 +1076,12 @@ class BuildVolume(SceneNode):
|
||||
return None
|
||||
|
||||
container_stack = self._global_container_stack
|
||||
adhesion_type = adhesion_override
|
||||
if adhesion_type is None:
|
||||
adhesion_type = container_stack.getProperty("adhesion_type", "value")
|
||||
adhesion_type = container_stack.getProperty("adhesion_type", "value")
|
||||
|
||||
# Skirt_brim_line_width is a bit of an odd one out. The primary bit of the skirt/brim is printed
|
||||
# with the adhesion extruder, but it also prints one extra line by all other extruders. As such, the
|
||||
# setting does *not* have a limit_to_extruder setting (which means that we can't ask the global extruder what
|
||||
# the value is.
|
||||
skirt_brim_extruder_nr = self._global_container_stack.getProperty("skirt_brim_extruder_nr", "value")
|
||||
try:
|
||||
skirt_brim_stack = self._global_container_stack.extruderList[int(skirt_brim_extruder_nr)]
|
||||
except IndexError:
|
||||
Logger.warning(f"Couldn't find extruder with index '{skirt_brim_extruder_nr}', defaulting to 0 instead.")
|
||||
skirt_brim_stack = self._global_container_stack.extruderList[0]
|
||||
skirt_brim_line_width = skirt_brim_stack.getProperty("skirt_brim_line_width", "value")
|
||||
|
||||
initial_layer_line_width_factor = skirt_brim_stack.getProperty("initial_layer_line_width_factor", "value")
|
||||
# Use brim width if brim is enabled OR the prime tower has a brim.
|
||||
if adhesion_type == "brim":
|
||||
brim_line_count = skirt_brim_stack.getProperty("brim_line_count", "value")
|
||||
brim_gap = skirt_brim_stack.getProperty("brim_gap", "value")
|
||||
bed_adhesion_size = brim_gap + skirt_brim_line_width * brim_line_count * initial_layer_line_width_factor / 100.0
|
||||
|
||||
for extruder_stack in used_extruders:
|
||||
bed_adhesion_size += extruder_stack.getProperty("skirt_brim_line_width", "value") * extruder_stack.getProperty("initial_layer_line_width_factor", "value") / 100.0
|
||||
|
||||
# We don't create an additional line for the extruder we're printing the brim with.
|
||||
bed_adhesion_size -= skirt_brim_line_width * initial_layer_line_width_factor / 100.0
|
||||
elif adhesion_type == "skirt":
|
||||
skirt_distance = skirt_brim_stack.getProperty("skirt_gap", "value")
|
||||
skirt_line_count = skirt_brim_stack.getProperty("skirt_line_count", "value")
|
||||
|
||||
bed_adhesion_size = skirt_distance + (
|
||||
skirt_brim_line_width * skirt_line_count) * initial_layer_line_width_factor / 100.0
|
||||
|
||||
for extruder_stack in used_extruders:
|
||||
bed_adhesion_size += extruder_stack.getProperty("skirt_brim_line_width", "value") * extruder_stack.getProperty("initial_layer_line_width_factor", "value") / 100.0
|
||||
|
||||
# We don't create an additional line for the extruder we're printing the skirt with.
|
||||
bed_adhesion_size -= skirt_brim_line_width * initial_layer_line_width_factor / 100.0
|
||||
elif adhesion_type == "raft":
|
||||
if adhesion_type == "raft":
|
||||
bed_adhesion_size = self._global_container_stack.getProperty("raft_margin", "value") # Should refer to the raft extruder if set.
|
||||
elif adhesion_type == "none":
|
||||
else: # raft, brim or skirt. Those last two are handled by CuraEngine.
|
||||
bed_adhesion_size = 0
|
||||
else:
|
||||
raise Exception("Unknown bed adhesion type. Did you forget to update the build volume calculations for your new bed adhesion type?")
|
||||
|
||||
max_length_available = 0.5 * min(
|
||||
self._global_container_stack.getProperty("machine_width", "value"),
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2019 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import platform
|
||||
import traceback
|
||||
@ -110,7 +110,7 @@ class CrashHandler:
|
||||
layout = QVBoxLayout(dialog)
|
||||
|
||||
label = QLabel()
|
||||
label.setText(catalog.i18nc("@label crash message", """<p><b>Oops, Ultimaker Cura has encountered something that doesn't seem right.</p></b>
|
||||
label.setText(catalog.i18nc("@label crash message", """<p><b>Oops, UltiMaker Cura has encountered something that doesn't seem right.</p></b>
|
||||
<p>We encountered an unrecoverable error during start up. It was possibly caused by some incorrect configuration files. We suggest to backup and reset your configuration.</p>
|
||||
<p>Backups can be found in the configuration folder.</p>
|
||||
<p>Please send us this Crash Report to fix the problem.</p>
|
||||
@ -119,7 +119,7 @@ class CrashHandler:
|
||||
layout.addWidget(label)
|
||||
|
||||
# "send report" check box and show details
|
||||
self._send_report_checkbox = QCheckBox(catalog.i18nc("@action:button", "Send crash report to Ultimaker"), dialog)
|
||||
self._send_report_checkbox = QCheckBox(catalog.i18nc("@action:button", "Send crash report to UltiMaker"), dialog)
|
||||
self._send_report_checkbox.setChecked(True)
|
||||
|
||||
show_details_button = QPushButton(catalog.i18nc("@action:button", "Show detailed crash report"), dialog)
|
||||
|
@ -5,7 +5,7 @@
|
||||
# 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 typing import Optional
|
||||
from typing import Optional, List, cast
|
||||
|
||||
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
@ -14,7 +14,6 @@ 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
|
||||
@ -29,11 +28,13 @@ class MachineListModel(ListModel):
|
||||
MachineCountRole = Qt.ItemDataRole.UserRole + 6
|
||||
IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7
|
||||
ComponentTypeRole = Qt.ItemDataRole.UserRole + 8
|
||||
IsNetworkedMachineRole = Qt.ItemDataRole.UserRole + 9
|
||||
|
||||
def __init__(self, parent: Optional[QObject] = None) -> None:
|
||||
def __init__(self, parent: Optional[QObject] = None, machines_filter: List[GlobalStack] = None, listenToChanges: bool = True) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._show_cloud_printers = False
|
||||
self._machines_filter = machines_filter
|
||||
|
||||
self._catalog = i18nCatalog("cura")
|
||||
|
||||
@ -45,17 +46,18 @@ class MachineListModel(ListModel):
|
||||
self.addRoleName(self.MachineCountRole, "machineCount")
|
||||
self.addRoleName(self.IsAbstractMachineRole, "isAbstractMachine")
|
||||
self.addRoleName(self.ComponentTypeRole, "componentType")
|
||||
self.addRoleName(self.IsNetworkedMachineRole, "isNetworked")
|
||||
|
||||
self._change_timer = QTimer()
|
||||
self._change_timer.setInterval(200)
|
||||
self._change_timer.setSingleShot(True)
|
||||
self._change_timer.timeout.connect(self._update)
|
||||
|
||||
# Listen to changes
|
||||
CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
|
||||
CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
|
||||
CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
|
||||
self._updateDelayed()
|
||||
if listenToChanges:
|
||||
CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged)
|
||||
CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged)
|
||||
CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged)
|
||||
self._updateDelayed()
|
||||
|
||||
showCloudPrintersChanged = pyqtSignal(bool)
|
||||
|
||||
@ -79,17 +81,33 @@ class MachineListModel(ListModel):
|
||||
def _updateDelayed(self) -> None:
|
||||
self._change_timer.start()
|
||||
|
||||
def _getMachineStacks(self) -> List[ContainerStack]:
|
||||
return CuraContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||
|
||||
def _getAbstractMachineStacks(self) -> List[ContainerStack]:
|
||||
return CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True")
|
||||
|
||||
def set_machines_filter(self, machines_filter: Optional[List[GlobalStack]]) -> None:
|
||||
self._machines_filter = machines_filter
|
||||
self._update()
|
||||
|
||||
def _update(self) -> None:
|
||||
self.clear()
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
machines_manager = CuraApplication.getInstance().getMachineManager()
|
||||
|
||||
other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine")
|
||||
other_machine_stacks = self._getMachineStacks()
|
||||
other_machine_stacks.sort(key = lambda machine: machine.getName().upper())
|
||||
|
||||
abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True")
|
||||
abstract_machine_stacks.sort(key = lambda machine: machine.getName().upper())
|
||||
abstract_machine_stacks = self._getAbstractMachineStacks()
|
||||
abstract_machine_stacks.sort(key = lambda machine: machine.getName().upper(), reverse = True)
|
||||
|
||||
if self._machines_filter is not None:
|
||||
filter_ids = [machine_filter.id for machine_filter in self._machines_filter]
|
||||
other_machine_stacks = [machine for machine in other_machine_stacks if machine.id in filter_ids]
|
||||
abstract_machine_stacks = [machine for machine in abstract_machine_stacks if machine.id in filter_ids]
|
||||
|
||||
for abstract_machine in abstract_machine_stacks:
|
||||
definition_id = abstract_machine.definition.getId()
|
||||
online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True)
|
||||
@ -113,18 +131,13 @@ class MachineListModel(ListModel):
|
||||
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
|
||||
})
|
||||
self.appendItem({
|
||||
"componentType": "HIDE_BUTTON" if self._show_cloud_printers else "SHOW_BUTTON",
|
||||
"isOnline": True,
|
||||
"isAbstractMachine": False,
|
||||
"machineCount": 0,
|
||||
"catergory": "connected",
|
||||
})
|
||||
|
||||
for stack in other_machine_stacks:
|
||||
self.addItem(stack, False)
|
||||
@ -134,11 +147,13 @@ class MachineListModel(ListModel):
|
||||
return
|
||||
|
||||
self.appendItem({
|
||||
"componentType": "MACHINE",
|
||||
"name": container_stack.getName(),
|
||||
"id": container_stack.getId(),
|
||||
"metadata": container_stack.getMetaData().copy(),
|
||||
"isOnline": is_online,
|
||||
"isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)),
|
||||
"machineCount": machine_count,
|
||||
})
|
||||
"componentType": "MACHINE",
|
||||
"name": container_stack.getName(),
|
||||
"id": container_stack.getId(),
|
||||
"metadata": container_stack.getMetaData().copy(),
|
||||
"isOnline": is_online,
|
||||
"isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)),
|
||||
"isNetworked": cast(GlobalStack, container_stack).hasNetworkedConnection() if isinstance(container_stack, GlobalStack) else False,
|
||||
"machineCount": machine_count,
|
||||
"catergory": "connected" if is_online else "other",
|
||||
})
|
||||
|
@ -274,7 +274,7 @@ class AuthorizationService:
|
||||
self._unable_to_get_data_message.show()
|
||||
else:
|
||||
self._unable_to_get_data_message = Message(i18n_catalog.i18nc("@info",
|
||||
"Unable to reach the Ultimaker account server."),
|
||||
"Unable to reach the UltiMaker account server."),
|
||||
title = i18n_catalog.i18nc("@info:title", "Log-in failed"),
|
||||
message_type = Message.MessageType.ERROR)
|
||||
Logger.warning("Unable to get user profile using auth data from preferences.")
|
||||
|
@ -252,7 +252,7 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||
# 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 "")
|
||||
new_configurations = sorted(all_configurations, key = lambda config: config.printerType or "", reverse = True)
|
||||
if new_configurations != self._unique_configurations:
|
||||
self._unique_configurations = new_configurations
|
||||
self.uniqueConfigurationsChanged.emit()
|
||||
|
48
cura/Settings/ActiveQuality.py
Normal file
48
cura/Settings/ActiveQuality.py
Normal file
@ -0,0 +1,48 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from UM import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
@dataclass
|
||||
class ActiveQuality:
|
||||
""" Represents the active intent+profile combination, contains all information needed to display active quality. """
|
||||
intent_category: str = "" # Name of the base intent. For example "visual" or "engineering".
|
||||
intent_name: str = "" # Name of the base intent formatted for display. For Example "Visual" or "Engineering"
|
||||
profile: str = "" # Name of the base profile. For example "Fine" or "Fast"
|
||||
custom_profile: str = "" # Name of the custom profile, this is based on profile. For example "MyCoolCustomProfile"
|
||||
layer_height: float = None # Layer height of quality in mm. For example 0.4
|
||||
is_experimental: bool = False # If the quality experimental.
|
||||
|
||||
def getMainStringParts(self) -> List[str]:
|
||||
string_parts = []
|
||||
|
||||
if self.custom_profile is not None:
|
||||
string_parts.append(self.custom_profile)
|
||||
else:
|
||||
string_parts.append(self.profile)
|
||||
if self.intent_category is not "default":
|
||||
string_parts.append(self.intent_name)
|
||||
|
||||
return string_parts
|
||||
|
||||
def getTailStringParts(self) -> List[str]:
|
||||
string_parts = []
|
||||
|
||||
if self.custom_profile is not None:
|
||||
string_parts.append(self.profile)
|
||||
if self.intent_category is not "default":
|
||||
string_parts.append(self.intent_name)
|
||||
|
||||
if self.layer_height:
|
||||
string_parts.append(f"{self.layer_height}mm")
|
||||
|
||||
if self.is_experimental:
|
||||
string_parts.append(catalog.i18nc("@label", "Experimental"))
|
||||
|
||||
return string_parts
|
||||
|
||||
def getStringParts(self) -> List[str]:
|
||||
return self.getMainStringParts() + self.getTailStringParts()
|
@ -275,7 +275,7 @@ class ExtruderManager(QObject):
|
||||
for extruder_setting in used_adhesion_extruders:
|
||||
extruder_str_nr = str(global_stack.getProperty(extruder_setting, "value"))
|
||||
if extruder_str_nr == "-1":
|
||||
extruder_str_nr = self._application.getMachineManager().defaultExtruderPosition
|
||||
continue # An optional extruder doesn't force any extruder to be used if it isn't used already
|
||||
if extruder_str_nr in self.extruderIds:
|
||||
used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr])
|
||||
|
||||
@ -298,7 +298,7 @@ class ExtruderManager(QObject):
|
||||
# Starts with the adhesion extruder.
|
||||
adhesion_type = global_stack.getProperty("adhesion_type", "value")
|
||||
if adhesion_type in {"skirt", "brim"}:
|
||||
return global_stack.getProperty("skirt_brim_extruder_nr", "value")
|
||||
return max(0, int(global_stack.getProperty("skirt_brim_extruder_nr", "value"))) # optional skirt/brim extruder defaults to zero
|
||||
if adhesion_type == "raft":
|
||||
return global_stack.getProperty("raft_base_extruder_nr", "value")
|
||||
|
||||
|
@ -142,8 +142,6 @@ class ExtruderStack(CuraContainerStack):
|
||||
|
||||
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
|
||||
if limit_to_extruder is not None:
|
||||
if limit_to_extruder == -1:
|
||||
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
limit_to_extruder = str(limit_to_extruder)
|
||||
|
||||
if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
|
||||
|
@ -226,8 +226,6 @@ class GlobalStack(CuraContainerStack):
|
||||
# Handle the "limit_to_extruder" property.
|
||||
limit_to_extruder = super().getProperty(key, "limit_to_extruder", context)
|
||||
if limit_to_extruder is not None:
|
||||
if limit_to_extruder == -1:
|
||||
limit_to_extruder = int(cura.CuraApplication.CuraApplication.getInstance().getMachineManager().defaultExtruderPosition)
|
||||
limit_to_extruder = str(limit_to_extruder)
|
||||
if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in self._extruders:
|
||||
if super().getProperty(key, "settable_per_extruder", context):
|
||||
|
@ -40,6 +40,7 @@ from cura.Settings.cura_empty_instance_containers import (empty_definition_chang
|
||||
empty_material_container, empty_quality_container,
|
||||
empty_quality_changes_container, empty_intent_container)
|
||||
from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT
|
||||
from .ActiveQuality import ActiveQuality
|
||||
|
||||
from .CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
@ -1631,33 +1632,31 @@ class MachineManager(QObject):
|
||||
# Examples:
|
||||
# - "my_profile - Fine" (only based on a default quality, no intent involved)
|
||||
# - "my_profile - Engineering - Fine" (based on an intent)
|
||||
@pyqtProperty("QVariantMap", notify = activeQualityDisplayNameChanged)
|
||||
def activeQualityDisplayNameMap(self) -> Dict[str, str]:
|
||||
@pyqtProperty("QList<QString>", notify = activeQualityDisplayNameChanged)
|
||||
def activeQualityDisplayNameStringParts(self) -> List[str]:
|
||||
return self.activeQualityDisplayNameMap().getStringParts()
|
||||
|
||||
@pyqtProperty("QList<QString>", notify = activeQualityDisplayNameChanged)
|
||||
def activeQualityDisplayNameMainStringParts(self) -> List[str]:
|
||||
return self.activeQualityDisplayNameMap().getMainStringParts()
|
||||
|
||||
@pyqtProperty("QList<QString>", notify = activeQualityDisplayNameChanged)
|
||||
def activeQualityDisplayNameTailStringParts(self) -> List[str]:
|
||||
return self.activeQualityDisplayNameMap().getTailStringParts()
|
||||
|
||||
def activeQualityDisplayNameMap(self) -> ActiveQuality:
|
||||
global_stack = self._application.getGlobalContainerStack()
|
||||
if global_stack is None:
|
||||
return {"main": "",
|
||||
"suffix": ""}
|
||||
return ActiveQuality()
|
||||
|
||||
display_name = global_stack.quality.getName()
|
||||
|
||||
intent_category = self.activeIntentCategory
|
||||
if intent_category != "default":
|
||||
intent_display_name = IntentCategoryModel.translation(intent_category,
|
||||
"name",
|
||||
intent_category.title())
|
||||
display_name = "{intent_name} - {the_rest}".format(intent_name = intent_display_name,
|
||||
the_rest = display_name)
|
||||
|
||||
main_part = display_name
|
||||
suffix_part = ""
|
||||
|
||||
# Not a custom quality
|
||||
if global_stack.qualityChanges != empty_quality_changes_container:
|
||||
main_part = self.activeQualityOrQualityChangesName
|
||||
suffix_part = display_name
|
||||
|
||||
return {"main": main_part,
|
||||
"suffix": suffix_part}
|
||||
return ActiveQuality(
|
||||
profile = global_stack.quality.getName(),
|
||||
intent_category = self.activeIntentCategory,
|
||||
intent_name = IntentCategoryModel.translation(self.activeIntentCategory, "name", self.activeIntentCategory.title()),
|
||||
custom_profile = self.activeQualityOrQualityChangesName if global_stack.qualityChanges is not empty_quality_changes_container else None,
|
||||
layer_height = self.activeQualityLayerHeight if self.isActiveQualitySupported else None,
|
||||
is_experimental = self.isActiveQualityExperimental and self.isActiveQualitySupported
|
||||
)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setIntentByCategory(self, intent_category: str) -> None:
|
||||
@ -1776,7 +1775,9 @@ class MachineManager(QObject):
|
||||
@pyqtProperty(bool, notify = activeQualityGroupChanged)
|
||||
def hasNotSupportedQuality(self) -> bool:
|
||||
global_container_stack = self._application.getGlobalContainerStack()
|
||||
return (not global_container_stack is None) and global_container_stack.quality == empty_quality_container and global_container_stack.qualityChanges == empty_quality_changes_container
|
||||
return global_container_stack is not None\
|
||||
and global_container_stack.quality == empty_quality_container \
|
||||
and global_container_stack.qualityChanges == empty_quality_changes_container
|
||||
|
||||
@pyqtProperty(bool, notify = activeQualityGroupChanged)
|
||||
def isActiveQualityCustom(self) -> bool:
|
||||
|
@ -12,7 +12,7 @@ class AddPrinterPagesModel(WelcomePagesModel):
|
||||
|
||||
def initialize(self, cancellable: bool = True) -> None:
|
||||
self._pages.append({"id": "add_network_or_local_printer",
|
||||
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
|
||||
"page_url": self._getBuiltinWelcomePagePath("AddUltimakerOrThirdPartyPrinterStack.qml"),
|
||||
"next_page_id": "machine_actions",
|
||||
"next_page_button_text": self._catalog.i18nc("@action:button", "Add"),
|
||||
})
|
||||
|
@ -265,7 +265,7 @@ class WelcomePagesModel(ListModel):
|
||||
"should_show_function": self.shouldShowCloudPage,
|
||||
},
|
||||
{"id": "add_network_or_local_printer",
|
||||
"page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"),
|
||||
"page_url": self._getBuiltinWelcomePagePath("AddUltimakerOrThirdPartyPrinterStack.qml"),
|
||||
"next_page_id": "machine_actions",
|
||||
},
|
||||
{"id": "add_printer_by_ip",
|
||||
|
@ -17,4 +17,4 @@ export OPENSSL_CONF="$scriptdir/openssl.cnf"
|
||||
# unset `QT_STYLE_OVERRIDE` as a precaution
|
||||
unset QT_STYLE_OVERRIDE
|
||||
|
||||
$scriptdir/Ultimaker-Cura "$@"
|
||||
$scriptdir/UltiMaker-Cura "$@"
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
|
||||
import argparse # Command line arguments parsing and help.
|
||||
from jinja2 import Template
|
||||
import os # Finding installation directory.
|
||||
@ -71,6 +72,6 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description = "Create AppImages of Cura.")
|
||||
parser.add_argument("dist_path", type=str, help="Path to where PyInstaller installed the distribution of Cura.")
|
||||
parser.add_argument("version", type=str, help="Full version number of Cura (e.g. '5.1.0-beta')")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the AppImage (e.g. 'Ultimaker-Cura-5.1.0-beta-Linux-X64.AppImage')")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the AppImage (e.g. 'UltiMaker-Cura-5.1.0-beta-Linux-X64.AppImage')")
|
||||
args = parser.parse_args()
|
||||
build_appimage(args.dist_path, args.version, args.filename)
|
||||
|
@ -3,16 +3,16 @@
|
||||
<id>com.ultimaker.cura</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>LGPL-3.0</project_license>
|
||||
<name>Ultimaker Cura</name>
|
||||
<name>UltiMaker Cura</name>
|
||||
<summary>Slicer to prepare your 3D printing projects</summary>
|
||||
<description>
|
||||
<p>Ultimaker Cura is a slicer, an application that prepares your model for 3D printing. Optimized, expert-tested profiles for 3D printers and materials mean you can start printing reliably in no time. And with industry-standard software integration, you can streamline your workflow for maximum efficiency.</p>
|
||||
<p>UltiMaker Cura is a slicer, an application that prepares your model for 3D printing. Optimized, expert-tested profiles for 3D printers and materials mean you can start printing reliably in no time. And with industry-standard software integration, you can streamline your workflow for maximum efficiency.</p>
|
||||
</description>
|
||||
<url type="homepage">https://ultimaker.com/en/software/ultimaker-cura</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Print preparation screen</caption>
|
||||
<image>https://raw.githubusercontent.com/Ultimaker/Cura/master/screenshot.png</image>
|
||||
<image>https://raw.githubusercontent.com/Ultimaker/Cura/main/cura-logo.PNG</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
|
@ -1,11 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Name=Ultimaker Cura
|
||||
Name[de]=Ultimaker Cura
|
||||
Name=UltiMaker Cura
|
||||
Name[de]=UltiMaker Cura
|
||||
GenericName=3D Printing Software
|
||||
GenericName[de]=3D-Druck-Software
|
||||
GenericName[nl]=3D-Print Software
|
||||
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.
|
||||
Exec=Ultimaker-Cura %F
|
||||
Exec=UltiMaker-Cura %F
|
||||
Icon=cura-icon
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Copyright (c) 2022 UltiMaker B.V.
|
||||
# Cura's build system is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
!define APP_NAME "{{ app_name }}"
|
||||
@ -64,7 +64,7 @@ InstallDir "$PROGRAMFILES64\${APP_NAME}"
|
||||
|
||||
!ifdef REG_START_MENU
|
||||
!define MUI_STARTMENUPAGE_NODISABLE
|
||||
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Ultimaker Cura"
|
||||
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "UltiMaker Cura"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}"
|
||||
@ -113,8 +113,8 @@ CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
!ifdef WEB_SITE
|
||||
WriteIniStr "$INSTDIR\Ultimaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\Ultimaker Cura website.lnk" "$INSTDIR\Ultimaker Cura website.url"
|
||||
WriteIniStr "$INSTDIR\UltiMaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\UltiMaker Cura website.lnk" "$INSTDIR\UltiMaker Cura website.url"
|
||||
!endif
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
!endif
|
||||
@ -125,8 +125,8 @@ CreateShortCut "$SMPROGRAMS\{{ app_name }}\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
!ifdef WEB_SITE
|
||||
WriteIniStr "$INSTDIR\Ultimaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\Ultimaker Cura website.lnk" "$INSTDIR\Ultimaker Cura website.url"
|
||||
WriteIniStr "$INSTDIR\UltiMaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\UltiMaker Cura website.lnk" "$INSTDIR\UltiMaker Cura website.url"
|
||||
!endif
|
||||
!endif
|
||||
|
||||
@ -170,7 +170,7 @@ RmDir /r /REBOOTOK "$INSTDIR"
|
||||
Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk"
|
||||
!ifdef WEB_SITE
|
||||
Delete "$SMPROGRAMS\$SM_Folder\Ultimaker Cura website.lnk"
|
||||
Delete "$SMPROGRAMS\$SM_Folder\UltiMaker Cura website.lnk"
|
||||
!endif
|
||||
RmDir "$SMPROGRAMS\$SM_Folder"
|
||||
!endif
|
||||
@ -179,7 +179,7 @@ RmDir "$SMPROGRAMS\$SM_Folder"
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\Uninstall ${APP_NAME}.lnk"
|
||||
!ifdef WEB_SITE
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\Ultimaker Cura website.lnk"
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\UltiMaker Cura website.lnk"
|
||||
!endif
|
||||
RmDir "$SMPROGRAMS\{{ app_name }}"
|
||||
!endif
|
||||
|
@ -1,5 +1,7 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
|
||||
import os
|
||||
import argparse # Command line arguments parsing and help.
|
||||
import subprocess
|
||||
@ -16,15 +18,15 @@ def generate_nsi(source_path: str, dist_path: str, filename: str):
|
||||
dist_loc = Path(os.getcwd(), dist_path)
|
||||
source_loc = Path(os.getcwd(), source_path)
|
||||
instdir = Path("$INSTDIR")
|
||||
dist_paths = [p.relative_to(dist_loc.joinpath("Ultimaker-Cura")) for p in sorted(dist_loc.joinpath("Ultimaker-Cura").rglob("*")) if p.is_file()]
|
||||
dist_paths = [p.relative_to(dist_loc.joinpath("UltiMaker-Cura")) for p in sorted(dist_loc.joinpath("UltiMaker-Cura").rglob("*")) if p.is_file()]
|
||||
mapped_out_paths = {}
|
||||
for dist_path in dist_paths:
|
||||
if "__pycache__" not in dist_path.parts:
|
||||
out_path = instdir.joinpath(dist_path).parent
|
||||
if out_path not in mapped_out_paths:
|
||||
mapped_out_paths[out_path] = [(dist_loc.joinpath("Ultimaker-Cura", dist_path), instdir.joinpath(dist_path))]
|
||||
mapped_out_paths[out_path] = [(dist_loc.joinpath("UltiMaker-Cura", dist_path), instdir.joinpath(dist_path))]
|
||||
else:
|
||||
mapped_out_paths[out_path].append((dist_loc.joinpath("Ultimaker-Cura", dist_path), instdir.joinpath(dist_path)))
|
||||
mapped_out_paths[out_path].append((dist_loc.joinpath("UltiMaker-Cura", dist_path), instdir.joinpath(dist_path)))
|
||||
|
||||
rmdir_paths = set()
|
||||
for rmdir_f in mapped_out_paths.values():
|
||||
@ -40,13 +42,13 @@ def generate_nsi(source_path: str, dist_path: str, filename: str):
|
||||
|
||||
|
||||
nsis_content = template.render(
|
||||
app_name = f"Ultimaker Cura {os.getenv('CURA_VERSION_FULL')}",
|
||||
main_app = "Ultimaker-Cura.exe",
|
||||
app_name = f"UltiMaker Cura {os.getenv('CURA_VERSION_FULL')}",
|
||||
main_app = "UltiMaker-Cura.exe",
|
||||
version = os.getenv('CURA_VERSION_FULL'),
|
||||
version_major = os.environ.get("CURA_VERSION_MAJOR"),
|
||||
version_minor = os.environ.get("CURA_VERSION_MINOR"),
|
||||
version_patch = os.environ.get("CURA_VERSION_PATCH"),
|
||||
company = "Ultimaker B.V.",
|
||||
company = "UltiMaker",
|
||||
web_site = "https://ultimaker.com",
|
||||
year = datetime.now().year,
|
||||
cura_license_file = str(source_loc.joinpath("packaging", "cura_license.txt")),
|
||||
@ -58,7 +60,7 @@ def generate_nsi(source_path: str, dist_path: str, filename: str):
|
||||
destination = filename
|
||||
)
|
||||
|
||||
with open(dist_loc.joinpath("Ultimaker-Cura.nsi"), "w") as f:
|
||||
with open(dist_loc.joinpath("UltiMaker-Cura.nsi"), "w") as f:
|
||||
f.write(nsis_content)
|
||||
|
||||
shutil.copy(source_loc.joinpath("packaging", "NSIS", "fileassoc.nsh"), dist_loc.joinpath("fileassoc.nsh"))
|
||||
@ -66,7 +68,7 @@ def generate_nsi(source_path: str, dist_path: str, filename: str):
|
||||
|
||||
def build(dist_path: str):
|
||||
dist_loc = Path(os.getcwd(), dist_path)
|
||||
command = ["makensis", "/V2", "/P4", str(dist_loc.joinpath("Ultimaker-Cura.nsi"))]
|
||||
command = ["makensis", "/V2", "/P4", str(dist_loc.joinpath("UltiMaker-Cura.nsi"))]
|
||||
subprocess.run(command)
|
||||
|
||||
|
||||
@ -74,7 +76,7 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description = "Create Windows exe installer of Cura.")
|
||||
parser.add_argument("source_path", type=str, help="Path to Conan install Cura folder.")
|
||||
parser.add_argument("dist_path", type=str, help="Path to Pyinstaller dist folder")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the exe (e.g. 'Ultimaker-Cura-5.1.0-beta-Windows-X64.exe')")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the exe (e.g. 'UltiMaker-Cura-5.1.0-beta-Windows-X64.exe')")
|
||||
args = parser.parse_args()
|
||||
generate_nsi(args.source_path, args.dist_path, args.filename)
|
||||
build(args.dist_path)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 604 KiB |
Binary file not shown.
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 381 KiB |
@ -1,3 +1,7 @@
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
|
||||
import os
|
||||
import argparse # Command line arguments parsing and help.
|
||||
import subprocess
|
||||
@ -14,11 +18,11 @@ def build_dmg(source_path: str, dist_path: str, filename: str) -> None:
|
||||
"--app-drop-link", "520", "272",
|
||||
"--volicon", f"{source_path}/packaging/icons/VolumeIcons_Cura.icns",
|
||||
"--icon-size", "90",
|
||||
"--icon", "Ultimaker-Cura.app", "169", "272",
|
||||
"--icon", "UltiMaker-Cura.app", "169", "272",
|
||||
"--eula", f"{source_path}/packaging/cura_license.txt",
|
||||
"--background", f"{source_path}/packaging/dmg/cura_background_dmg.png",
|
||||
f"{dist_path}/{filename}",
|
||||
f"{dist_path}/Ultimaker-Cura.app"]
|
||||
f"{dist_path}/UltiMaker-Cura.app"]
|
||||
|
||||
subprocess.run(arguments)
|
||||
|
||||
@ -57,7 +61,7 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description = "Create dmg of Cura.")
|
||||
parser.add_argument("source_path", type=str, help="Path to Conan install Cura folder.")
|
||||
parser.add_argument("dist_path", type=str, help="Path to Pyinstaller dist folder")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the dmg (e.g. 'Ultimaker-Cura-5.1.0-beta-Linux-X64.dmg')")
|
||||
parser.add_argument("filename", type = str, help = "Filename of the dmg (e.g. 'UltiMaker-Cura-5.1.0-beta-Linux-X64.dmg')")
|
||||
args = parser.parse_args()
|
||||
build_dmg(args.source_path, args.dist_path, args.filename)
|
||||
sign(args.dist_path, args.filename)
|
||||
|
@ -9,6 +9,7 @@ from typing import cast, Dict, List, Optional, Tuple, Any, Set
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from UM.Util import parseBool
|
||||
from UM.Workspace.WorkspaceReader import WorkspaceReader
|
||||
from UM.Application import Application
|
||||
|
||||
@ -599,7 +600,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
self._dialog.setNumUserSettings(num_user_settings)
|
||||
self._dialog.setActiveMode(active_mode)
|
||||
self._dialog.setUpdatableMachines(updatable_machines)
|
||||
self._dialog.setMachineName(machine_name)
|
||||
self._dialog.setMaterialLabels(material_labels)
|
||||
self._dialog.setMachineType(machine_type)
|
||||
self._dialog.setExtruders(extruders)
|
||||
@ -608,6 +608,36 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
self._dialog.setMissingPackagesMetadata(missing_package_metadata)
|
||||
self._dialog.show()
|
||||
|
||||
# Choosing the initially selected printer in MachineSelector
|
||||
is_networked_machine = False
|
||||
is_abstract_machine = False
|
||||
if global_stack and isinstance(global_stack, GlobalStack):
|
||||
# The machine included in the project file exists locally already, no need to change selected printers.
|
||||
is_networked_machine = global_stack.hasNetworkedConnection()
|
||||
is_abstract_machine = parseBool(existing_global_stack.getMetaDataEntry("is_abstract_machine", False))
|
||||
self._dialog.setMachineToOverride(global_stack.getId())
|
||||
self._dialog.setResolveStrategy("machine", "override")
|
||||
elif self._dialog.updatableMachinesModel.count > 0:
|
||||
# The machine included in the project file does not exist. There is another machine of the same type.
|
||||
# This will always default to an abstract machine first.
|
||||
machine = self._dialog.updatableMachinesModel.getItem(0)
|
||||
machine_name = machine["name"]
|
||||
is_networked_machine = machine["isNetworked"]
|
||||
is_abstract_machine = machine["isAbstractMachine"]
|
||||
self._dialog.setMachineToOverride(machine["id"])
|
||||
self._dialog.setResolveStrategy("machine", "override")
|
||||
else:
|
||||
# The machine included in the project file does not exist. There are no other printers of the same type. Default to "Create New".
|
||||
machine_name = i18n_catalog.i18nc("@button", "Create new")
|
||||
is_networked_machine = False
|
||||
is_abstract_machine = False
|
||||
self._dialog.setMachineToOverride(None)
|
||||
self._dialog.setResolveStrategy("machine", "new")
|
||||
|
||||
self._dialog.setIsNetworkedMachine(is_networked_machine)
|
||||
self._dialog.setIsAbstractMachine(is_abstract_machine)
|
||||
self._dialog.setMachineName(machine_name)
|
||||
|
||||
# Block until the dialog is closed.
|
||||
self._dialog.waitForClose()
|
||||
|
||||
@ -670,7 +700,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
temp_preferences = Preferences()
|
||||
try:
|
||||
serialized = archive.open("Cura/preferences.cfg").read().decode("utf-8")
|
||||
except KeyError:
|
||||
except KeyError as e:
|
||||
# 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>!",
|
||||
@ -702,7 +732,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
application.expandedCategoriesChanged.emit() # Notify the GUI of the change
|
||||
|
||||
# If there are no machines of the same type, create a new machine.
|
||||
if self._resolve_strategies["machine"] != "override" or self._dialog.updatableMachinesModel.count <= 1:
|
||||
if self._resolve_strategies["machine"] != "override" or self._dialog.updatableMachinesModel.count == 0:
|
||||
# We need to create a new machine
|
||||
machine_name = self._container_registry.uniqueName(self._machine_info.name)
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
create_new_list_item = {
|
||||
"id": "new",
|
||||
"name": "Create new",
|
||||
"displayName": "Create new",
|
||||
"type": "default_option" # to make sure we are not mixing the "Create new" option with a printer with id "new"
|
||||
} # type: Dict[str, str]
|
||||
|
||||
|
||||
class UpdatableMachinesModel(ListModel):
|
||||
"""Model that holds cura packages.
|
||||
|
||||
By setting the filter property the instances held by this model can be changed.
|
||||
"""
|
||||
|
||||
def __init__(self, parent = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.addRoleName(Qt.ItemDataRole.UserRole + 1, "id")
|
||||
self.addRoleName(Qt.ItemDataRole.UserRole + 2, "name")
|
||||
self.addRoleName(Qt.ItemDataRole.UserRole + 3, "displayName")
|
||||
self.addRoleName(Qt.ItemDataRole.UserRole + 4, "type") # Either "default_option" or "machine"
|
||||
|
||||
def update(self, machines: List[GlobalStack]) -> None:
|
||||
items = [create_new_list_item] # type: List[Dict[str, str]]
|
||||
|
||||
for machine in sorted(machines, key = lambda printer: printer.name):
|
||||
items.append({
|
||||
"id": machine.id,
|
||||
"name": machine.name,
|
||||
"displayName": "Update " + machine.name,
|
||||
"type": "machine"
|
||||
})
|
||||
self.setItems(items)
|
@ -5,6 +5,7 @@ from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication, QU
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
from typing import List, Optional, Dict, cast
|
||||
|
||||
from cura.Machines.Models.MachineListModel import MachineListModel
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from UM.Application import Application
|
||||
from UM.FlameProfiler import pyqtSlot
|
||||
@ -14,8 +15,6 @@ from UM.Message import Message
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
|
||||
from .UpdatableMachinesModel import UpdatableMachinesModel
|
||||
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
@ -63,10 +62,12 @@ class WorkspaceDialog(QObject):
|
||||
self._extruders = []
|
||||
self._objects_on_plate = False
|
||||
self._is_printer_group = False
|
||||
self._updatable_machines_model = UpdatableMachinesModel(self)
|
||||
self._updatable_machines_model = MachineListModel(self, listenToChanges=False)
|
||||
self._missing_package_metadata: List[Dict[str, str]] = []
|
||||
self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry()
|
||||
self._install_missing_package_dialog: Optional[QObject] = None
|
||||
self._is_abstract_machine = False
|
||||
self._is_networked_machine = False
|
||||
|
||||
machineConflictChanged = pyqtSignal()
|
||||
qualityChangesConflictChanged = pyqtSignal()
|
||||
@ -80,6 +81,8 @@ class WorkspaceDialog(QObject):
|
||||
intentNameChanged = pyqtSignal()
|
||||
machineNameChanged = pyqtSignal()
|
||||
updatableMachinesChanged = pyqtSignal()
|
||||
isAbstractMachineChanged = pyqtSignal()
|
||||
isNetworkedChanged = pyqtSignal()
|
||||
materialLabelsChanged = pyqtSignal()
|
||||
objectsOnPlateChanged = pyqtSignal()
|
||||
numUserSettingsChanged = pyqtSignal()
|
||||
@ -161,13 +164,31 @@ class WorkspaceDialog(QObject):
|
||||
self.machineNameChanged.emit()
|
||||
|
||||
@pyqtProperty(QObject, notify = updatableMachinesChanged)
|
||||
def updatableMachinesModel(self) -> UpdatableMachinesModel:
|
||||
return cast(UpdatableMachinesModel, self._updatable_machines_model)
|
||||
def updatableMachinesModel(self) -> MachineListModel:
|
||||
return cast(MachineListModel, self._updatable_machines_model)
|
||||
|
||||
def setUpdatableMachines(self, updatable_machines: List[GlobalStack]) -> None:
|
||||
self._updatable_machines_model.update(updatable_machines)
|
||||
self._updatable_machines_model.set_machines_filter(updatable_machines)
|
||||
self.updatableMachinesChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = isAbstractMachineChanged)
|
||||
def isAbstractMachine(self) -> bool:
|
||||
return self._is_abstract_machine
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def setIsAbstractMachine(self, is_abstract_machine: bool) -> None:
|
||||
self._is_abstract_machine = is_abstract_machine
|
||||
self.isAbstractMachineChanged.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = isNetworkedChanged)
|
||||
def isNetworked(self) -> bool:
|
||||
return self._is_networked_machine
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def setIsNetworkedMachine(self, is_networked_machine: bool) -> None:
|
||||
self._is_networked_machine = is_networked_machine
|
||||
self.isNetworkedChanged.emit()
|
||||
|
||||
@pyqtProperty(str, notify=qualityTypeChanged)
|
||||
def qualityType(self) -> str:
|
||||
return self._quality_type
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 Ultimaker B.V.
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -11,47 +11,48 @@ import Cura 1.1 as Cura
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
id: base
|
||||
id: workspaceDialog
|
||||
title: catalog.i18nc("@title:window", "Open Project")
|
||||
|
||||
minimumWidth: UM.Theme.getSize("popup_dialog").width
|
||||
minimumHeight: UM.Theme.getSize("popup_dialog").height
|
||||
width: minimumWidth
|
||||
backgroundColor: UM.Theme.getColor("main_background")
|
||||
margin: UM.Theme.getSize("default_margin").width
|
||||
property int comboboxHeight: UM.Theme.getSize("default_margin").height
|
||||
minimumWidth: UM.Theme.getSize("modal_window_minimum").width
|
||||
minimumHeight: UM.Theme.getSize("modal_window_minimum").height
|
||||
|
||||
onClosing: manager.notifyClosed()
|
||||
onVisibleChanged:
|
||||
backgroundColor: UM.Theme.getColor("detail_background")
|
||||
|
||||
headerComponent: Rectangle
|
||||
{
|
||||
if (visible)
|
||||
height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height
|
||||
color: UM.Theme.getColor("main_background")
|
||||
|
||||
UM.Label
|
||||
{
|
||||
machineResolveComboBox.currentIndex = 0
|
||||
qualityChangesResolveComboBox.currentIndex = 0
|
||||
materialResolveComboBox.currentIndex = 0
|
||||
id: titleLabel
|
||||
text: catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
}
|
||||
|
||||
Flickable
|
||||
Rectangle
|
||||
{
|
||||
clip: true
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
contentHeight: dialogSummaryItem.height
|
||||
ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar }
|
||||
anchors.fill: parent
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
color: UM.Theme.getColor("main_background")
|
||||
|
||||
Item
|
||||
Flickable
|
||||
{
|
||||
id: dialogSummaryItem
|
||||
width: verticalScrollBar.visible ? parent.width - verticalScrollBar.width - UM.Theme.getSize("default_margin").width : parent.width
|
||||
height: childrenRect.height
|
||||
anchors.margins: 10 * screenScaleFactor
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
UM.I18nCatalog
|
||||
{
|
||||
id: catalog
|
||||
name: "cura"
|
||||
}
|
||||
clip: true
|
||||
|
||||
contentHeight: contentColumn.height
|
||||
ScrollBar.vertical: UM.ScrollBar { id: scrollbar }
|
||||
|
||||
ListModel
|
||||
{
|
||||
@ -68,373 +69,245 @@ UM.Dialog
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
id: contentColumn
|
||||
width: parent.width - scrollbar.width - UM.Theme.getSize("default_margin").width
|
||||
height: childrenRect.height
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
leftPadding: UM.Theme.getSize("default_margin").width
|
||||
rightPadding: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Column
|
||||
WorkspaceSection
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
id: printerSection
|
||||
title: catalog.i18nc("@action:label", "Printer settings")
|
||||
iconSource: UM.Theme.getIcon("Printer")
|
||||
content: Column
|
||||
{
|
||||
id: titleLabel
|
||||
text: catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
}
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: separator
|
||||
color: UM.Theme.getColor("text")
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("default_lining").height
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
{
|
||||
id: machineResolveStrategyTooltip
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: base.visible && machineResolveComboBox.model.count > 1
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?")
|
||||
Cura.ComboBox
|
||||
WorkspaceRow
|
||||
{
|
||||
id: machineResolveComboBox
|
||||
model: manager.updatableMachinesModel
|
||||
visible: machineResolveStrategyTooltip.visible
|
||||
textRole: "displayName"
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onCurrentIndexChanged:
|
||||
leftLabelText: catalog.i18nc("@action:label", "Type")
|
||||
rightLabelText: manager.machineType
|
||||
}
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
leftLabelText: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
|
||||
rightLabelText: manager.machineName == catalog.i18nc("@button", "Create new") ? "" : manager.machineName
|
||||
}
|
||||
}
|
||||
|
||||
comboboxTitle: catalog.i18nc("@action:label", "Open With")
|
||||
comboboxTooltipText: catalog.i18nc("@info:tooltip", "Printer settings will be updated to match the settings saved with the project.")
|
||||
comboboxVisible: workspaceDialog.visible && manager.updatableMachinesModel.count > 1
|
||||
combobox: Cura.MachineSelector
|
||||
{
|
||||
id: machineSelector
|
||||
headerCornerSide: Cura.RoundedRectangle.Direction.All
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
machineListModel: manager.updatableMachinesModel
|
||||
machineName: manager.machineName
|
||||
|
||||
isConnectedCloudPrinter: false
|
||||
isCloudRegistered: false
|
||||
isNetworkPrinter: manager.isNetworked
|
||||
isGroup: manager.isAbstractMachine
|
||||
connectionStatus: ""
|
||||
|
||||
minDropDownWidth: machineSelector.width
|
||||
|
||||
buttons: [
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
if (model.getItem(currentIndex).id == "new"
|
||||
&& model.getItem(currentIndex).type == "default_option")
|
||||
id: createNewPrinter
|
||||
text: catalog.i18nc("@button", "Create new")
|
||||
fixedWidthMode: true
|
||||
width: parent.width - leftPadding * 1.5
|
||||
onClicked:
|
||||
{
|
||||
toggleContent()
|
||||
manager.setResolveStrategy("machine", "new")
|
||||
}
|
||||
else
|
||||
{
|
||||
manager.setResolveStrategy("machine", "override")
|
||||
manager.setMachineToOverride(model.getItem(currentIndex).id)
|
||||
machineSelector.machineName = catalog.i18nc("@button", "Create new")
|
||||
manager.setIsAbstractMachine(false)
|
||||
manager.setIsNetworkedMachine(false)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (!visible) {return}
|
||||
|
||||
currentIndex = 0
|
||||
// If the project printer exists in Cura, set it as the default dropdown menu option.
|
||||
// No need to check object 0, which is the "Create new" option
|
||||
for (var i = 1; i < model.count; i++)
|
||||
{
|
||||
if (model.getItem(i).name == manager.machineName)
|
||||
{
|
||||
currentIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
// The project printer does not exist in Cura. If there is at least one printer of the same
|
||||
// type, select the first one, else set the index to "Create new"
|
||||
if (currentIndex == 0 && model.count > 1)
|
||||
{
|
||||
currentIndex = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
onSelectPrinter: function(machine)
|
||||
{
|
||||
id: printer_settings_label
|
||||
text: catalog.i18nc("@action:label", "Printer settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Type")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineType
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.machineName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
toggleContent();
|
||||
machineSelector.machineName = machine.name
|
||||
manager.setResolveStrategy("machine", "override")
|
||||
manager.setMachineToOverride(machine.id)
|
||||
manager.setIsAbstractMachine(machine.isAbstractMachine)
|
||||
manager.setIsNetworkedMachine(machine.isNetworked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
WorkspaceSection
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
id: profileSection
|
||||
title: catalog.i18nc("@action:label", "Profile settings")
|
||||
iconSource: UM.Theme.getIcon("Sliders")
|
||||
content: Column
|
||||
{
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
id: profileSettingsValuesTable
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
leftLabelText: catalog.i18nc("@action:label", "Name")
|
||||
rightLabelText: manager.qualityName
|
||||
}
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
leftLabelText: catalog.i18nc("@action:label", "Intent")
|
||||
rightLabelText: manager.intentName
|
||||
}
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
leftLabelText: catalog.i18nc("@action:label", "Not in profile")
|
||||
rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings)
|
||||
visible: manager.numUserSettings != 0
|
||||
}
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
leftLabelText: catalog.i18nc("@action:label", "Derivative from")
|
||||
rightLabelText: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges)
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
}
|
||||
}
|
||||
|
||||
comboboxVisible: manager.qualityChangesConflict
|
||||
combobox: Cura.ComboBox
|
||||
{
|
||||
id: qualityChangesResolveComboBox
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
visible: manager.qualityChangesConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: qualityChangesResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
contentLeftPadding: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("narrow_margin").width
|
||||
textFont: UM.Theme.getFont("medium")
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
background: Cura.RoundedRectangle
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Profile settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
color: qualityChangesResolveComboBox.hovered ? UM.Theme.getColor("expandable_hover") : UM.Theme.getColor("action_button")
|
||||
cornerSide: Cura.RoundedRectangle.Direction.All
|
||||
radius: UM.Theme.getSize("default_radius").width
|
||||
}
|
||||
|
||||
Row
|
||||
// This is a hack. This will trigger onCurrentIndexChanged and set the index when this component in loaded
|
||||
currentIndex:
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.qualityName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
currentIndex = 0
|
||||
}
|
||||
|
||||
Row
|
||||
onCurrentIndexChanged:
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Intent")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: manager.intentName
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Not in profile")
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings)
|
||||
visible: manager.numUserSettings != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Derivative from")
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges)
|
||||
width: (parent.width / 3) | 0
|
||||
visible: manager.numSettingsOverridenByQualityChanges != 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(currentIndex).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
WorkspaceSection
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
UM.TooltipArea
|
||||
id: materialSection
|
||||
title: catalog.i18nc("@action:label", "Material settings")
|
||||
iconSource: UM.Theme.getIcon("Spool")
|
||||
content: Column
|
||||
{
|
||||
id: materialResolveTooltip
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
width: (parent.width / 3) | 0
|
||||
height: visible ? comboboxHeight : 0
|
||||
visible: manager.materialConflict
|
||||
text: catalog.i18nc("@info:tooltip", "How should the conflict in the material be resolved?")
|
||||
Cura.ComboBox
|
||||
{
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
id: materialResolveComboBox
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
onActivated:
|
||||
{
|
||||
manager.setResolveStrategy("material", resolveStrategiesModel.get(index).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
Row
|
||||
{
|
||||
height: childrenRect.height
|
||||
width: parent.width
|
||||
spacing: UM.Theme.getSize("narrow_margin").width
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Material settings")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: manager.materialLabels
|
||||
delegate: Row
|
||||
delegate: WorkspaceRow
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Name")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: modelData
|
||||
width: (parent.width / 3) | 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
leftLabelText: catalog.i18nc("@action:label", "Name")
|
||||
rightLabelText: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comboboxVisible: manager.materialConflict
|
||||
|
||||
combobox: Cura.ComboBox
|
||||
{
|
||||
id: materialResolveComboBox
|
||||
model: resolveStrategiesModel
|
||||
textRole: "label"
|
||||
visible: manager.materialConflict
|
||||
contentLeftPadding: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("narrow_margin").width
|
||||
textFont: UM.Theme.getFont("medium")
|
||||
|
||||
background: Cura.RoundedRectangle
|
||||
{
|
||||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: UM.Theme.getColor("lining")
|
||||
color: materialResolveComboBox.hovered ? UM.Theme.getColor("expandable_hover") : UM.Theme.getColor("action_button")
|
||||
cornerSide: Cura.RoundedRectangle.Direction.All
|
||||
radius: UM.Theme.getSize("default_radius").width
|
||||
}
|
||||
|
||||
// This is a hack. This will trigger onCurrentIndexChanged and set the index when this component in loaded
|
||||
currentIndex:
|
||||
{
|
||||
currentIndex = 0
|
||||
}
|
||||
|
||||
onCurrentIndexChanged:
|
||||
{
|
||||
manager.setResolveStrategy("material", resolveStrategiesModel.get(currentIndex).key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column
|
||||
WorkspaceSection
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
id: visibilitySection
|
||||
title: catalog.i18nc("@action:label", "Setting visibility")
|
||||
iconSource: UM.Theme.getIcon("Eye")
|
||||
content: Column
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
|
||||
bottomPadding: UM.Theme.getSize("narrow_margin").height
|
||||
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Setting visibility")
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
UM.Label
|
||||
WorkspaceRow
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Mode")
|
||||
width: (parent.width / 3) | 0
|
||||
leftLabelText: catalog.i18nc("@action:label", "Mode")
|
||||
rightLabelText: manager.activeMode
|
||||
}
|
||||
UM.Label
|
||||
|
||||
WorkspaceRow
|
||||
{
|
||||
text: manager.activeMode
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
}
|
||||
Row
|
||||
{
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
visible: manager.hasVisibleSettingsField
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "Visible settings:")
|
||||
width: (parent.width / 3) | 0
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
text: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings)
|
||||
width: (parent.width / 3) | 0
|
||||
leftLabelText: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings)
|
||||
rightLabelText: manager.activeMode
|
||||
visible: manager.hasVisibleSettingsField
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: clearBuildPlateWarning
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
visible: manager.hasObjectsOnPlate
|
||||
|
||||
UM.ColorImage
|
||||
{
|
||||
width: warningLabel.height
|
||||
@ -459,14 +332,18 @@ UM.Dialog
|
||||
color: warning ? UM.Theme.getColor("warning") : "transparent"
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: childrenRect.height + 2 * base.margin
|
||||
height: childrenRect.height + (warning ? 2 * workspaceDialog.margin : workspaceDialog.margin)
|
||||
|
||||
Column
|
||||
{
|
||||
height: childrenRect.height
|
||||
spacing: base.margin
|
||||
spacing: workspaceDialog.margin
|
||||
|
||||
anchors.leftMargin: workspaceDialog.margin
|
||||
anchors.rightMargin: workspaceDialog.margin
|
||||
anchors.bottomMargin: workspaceDialog.margin
|
||||
anchors.topMargin: warning ? workspaceDialog.margin : 0
|
||||
|
||||
anchors.margins: base.margin
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
@ -476,7 +353,7 @@ UM.Dialog
|
||||
id: warningRow
|
||||
height: childrenRect.height
|
||||
visible: warning
|
||||
spacing: base.margin
|
||||
spacing: workspaceDialog.margin
|
||||
UM.ColorImage
|
||||
{
|
||||
width: UM.Theme.getSize("extruder_icon").width
|
||||
@ -500,7 +377,7 @@ UM.Dialog
|
||||
}
|
||||
}
|
||||
|
||||
buttonSpacing: UM.Theme.getSize("default_margin").width
|
||||
buttonSpacing: UM.Theme.getSize("wide_margin").width
|
||||
|
||||
rightButtons: [
|
||||
Cura.TertiaryButton
|
||||
@ -532,6 +409,19 @@ UM.Dialog
|
||||
}
|
||||
]
|
||||
|
||||
onClosing: manager.notifyClosed()
|
||||
onRejected: manager.onCancelButtonClicked()
|
||||
onAccepted: manager.onOkButtonClicked()
|
||||
onVisibleChanged:
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
// Force relead the comboboxes
|
||||
// Since this dialog is only created once the first time you open it, these comboxes need to be reloaded
|
||||
// each time it is shown after the first time so that the indexes will update correctly.
|
||||
materialSection.reloadValues()
|
||||
profileSection.reloadValues()
|
||||
printerSection.reloadValues()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
plugins/3MFReader/WorkspaceRow.qml
Normal file
34
plugins/3MFReader/WorkspaceRow.qml
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.3
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
import UM 1.5 as UM
|
||||
import Cura 1.1 as Cura
|
||||
|
||||
Row
|
||||
{
|
||||
property alias leftLabelText: leftLabel.text
|
||||
property alias rightLabelText: rightLabel.text
|
||||
|
||||
width: parent.width
|
||||
height: visible ? childrenRect.height : 0
|
||||
|
||||
UM.Label
|
||||
{
|
||||
id: leftLabel
|
||||
text: catalog.i18nc("@action:label", "Type")
|
||||
width: Math.round(parent.width / 4)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
id: rightLabel
|
||||
text: manager.machineType
|
||||
width: Math.round(parent.width / 3)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
128
plugins/3MFReader/WorkspaceSection.qml
Normal file
128
plugins/3MFReader/WorkspaceSection.qml
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.3
|
||||
|
||||
|
||||
import UM 1.5 as UM
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
property alias title: sectionTitle.text
|
||||
property alias iconSource: sectionTitleIcon.source
|
||||
property Component content: Item { visible: false }
|
||||
|
||||
property alias comboboxTitle: comboboxLabel.text
|
||||
property Component combobox: Item { visible: false }
|
||||
property string comboboxTooltipText: ""
|
||||
property bool comboboxVisible: false
|
||||
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
|
||||
Row
|
||||
{
|
||||
id: sectionTitleRow
|
||||
anchors.top: parent.top
|
||||
bottomPadding: UM.Theme.getSize("default_margin").height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
|
||||
UM.ColorImage
|
||||
{
|
||||
id: sectionTitleIcon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: ""
|
||||
height: UM.Theme.getSize("medium_button_icon").height
|
||||
color: UM.Theme.getColor("text")
|
||||
width: height
|
||||
}
|
||||
UM.Label
|
||||
{
|
||||
id: sectionTitle
|
||||
text: ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: comboboxTooltip
|
||||
width: Math.round(parent.width / 2.5)
|
||||
height: visible ? UM.Theme.getSize("default_margin").height : 0
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
visible: comboboxVisible
|
||||
|
||||
UM.Label
|
||||
{
|
||||
id: comboboxLabel
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: comboboxVisible && text != ""
|
||||
text: ""
|
||||
font: UM.Theme.getFont("default_bold")
|
||||
}
|
||||
|
||||
Loader
|
||||
{
|
||||
id: comboboxLoader
|
||||
width: parent.width
|
||||
height: UM.Theme.getSize("button").height
|
||||
anchors.top: comboboxLabel.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
sourceComponent: combobox
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
id: helpIconMouseArea
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: comboboxLabel.verticalCenter
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
hoverEnabled: true
|
||||
|
||||
UM.ColorImage
|
||||
{
|
||||
width: UM.Theme.getSize("section_icon").width
|
||||
height: width
|
||||
|
||||
visible: comboboxTooltipText != ""
|
||||
source: UM.Theme.getIcon("Help")
|
||||
color: UM.Theme.getColor("text")
|
||||
|
||||
UM.ToolTip
|
||||
{
|
||||
text: comboboxTooltipText
|
||||
visible: helpIconMouseArea.containsMouse
|
||||
targetPoint: Qt.point(parent.x + Math.round(parent.width / 2), parent.y)
|
||||
x: 0
|
||||
y: parent.y + parent.height + UM.Theme.getSize("default_margin").height
|
||||
width: UM.Theme.getSize("tooltip").width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loader
|
||||
{
|
||||
width: parent.width
|
||||
height: content.height
|
||||
anchors.top: sectionTitleRow.bottom
|
||||
sourceComponent: content
|
||||
}
|
||||
|
||||
function reloadValues()
|
||||
{
|
||||
comboboxLoader.sourceComponent = null
|
||||
comboboxLoader.sourceComponent = combobox
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2021-2022 Ultimaker B.V.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import argparse #To run the engine in debug mode if the front-end is in debug mode.
|
||||
@ -166,7 +166,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||
self._slicing_error_message.addAction(
|
||||
action_id = "report_bug",
|
||||
name = catalog.i18nc("@message:button", "Report a bug"),
|
||||
description = catalog.i18nc("@message:description", "Report a bug on Ultimaker Cura's issue tracker."),
|
||||
description = catalog.i18nc("@message:description", "Report a bug on UltiMaker Cura's issue tracker."),
|
||||
icon = "[no_icon]"
|
||||
)
|
||||
self._slicing_error_message.actionTriggered.connect(self._reportBackendError)
|
||||
|
@ -487,6 +487,10 @@ class StartSliceJob(Job):
|
||||
settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr)
|
||||
settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr)
|
||||
|
||||
# Manually add 'nozzle offsetting', since that is a metadata-entry instead for some reason.
|
||||
# NOTE: This probably needs to be an actual setting at some point.
|
||||
settings["nozzle_offsetting_for_disallowed_areas"] = CuraApplication.getInstance().getGlobalContainerStack().getMetaDataEntry("nozzle_offsetting_for_disallowed_areas", True)
|
||||
|
||||
# Add all sub-messages for each individual setting.
|
||||
for key, value in settings.items():
|
||||
setting_message = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings")
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
from typing import List, Dict, Any, Set
|
||||
@ -142,7 +142,7 @@ class CloudPackageChecker(QObject):
|
||||
sync_message = Message(self._i18n_catalog.i18nc(
|
||||
"@info:generic",
|
||||
"Do you want to sync material and software packages with your account?"),
|
||||
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account", ))
|
||||
title = self._i18n_catalog.i18nc("@info:title", "Changes detected from your UltiMaker account", ))
|
||||
sync_message.addAction("sync",
|
||||
name = self._i18n_catalog.i18nc("@action:button", "Sync"),
|
||||
icon = "",
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import tempfile
|
||||
from typing import Dict, List, Any
|
||||
@ -92,7 +92,7 @@ class DownloadPresenter:
|
||||
lifetime = 0,
|
||||
use_inactivity_timer = False,
|
||||
progress = 0.0,
|
||||
title = i18n_catalog.i18nc("@info:title", "Changes detected from your Ultimaker account"))
|
||||
title = i18n_catalog.i18nc("@info:title", "Changes detected from your UltiMaker account"))
|
||||
|
||||
def _onFinished(self, package_id: str, reply: QNetworkReply) -> None:
|
||||
self._progress[package_id]["received"] = self._progress[package_id]["total"]
|
||||
|
@ -44,7 +44,7 @@ class LocalPackageList(PackageList):
|
||||
|
||||
def _sortSectionsOnUpdate(self) -> None:
|
||||
section_order = dict(zip([i for k, v in self.PACKAGE_CATEGORIES.items() for i in self.PACKAGE_CATEGORIES[k].values()], ["a", "b", "c", "d"]))
|
||||
self.sort(lambda model: (section_order[model.sectionTitle], model.canUpdate, model.displayName.lower()), key = "package")
|
||||
self.sort(lambda model: (section_order[model.sectionTitle], not model.canUpdate, model.displayName.lower()), key = "package")
|
||||
|
||||
def _removePackageModel(self, package_id: str) -> None:
|
||||
"""
|
||||
|
@ -92,7 +92,7 @@ class PackageModel(QObject):
|
||||
"display_name": display_name,
|
||||
"package_version": package_version,
|
||||
"package_type": package_type,
|
||||
"description": "The material package associated with the Cura project could not be found on the Ultimaker marketplace. Use the partial material profile definition stored in the Cura project file at your own risk."
|
||||
"description": catalog.i18nc("@label:label Ultimaker Marketplace is a brand name, don't translate", "The material package associated with the Cura project could not be found on the Ultimaker Marketplace. Use the partial material profile definition stored in the Cura project file at your own risk.")
|
||||
}
|
||||
package_model = cls(package_data)
|
||||
package_model.setIsMissingPackageInformation(True)
|
||||
|
@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
@ -12,7 +13,7 @@ Packages
|
||||
|
||||
bannerVisible: UM.Preferences.getValue("cura/market_place_show_manage_packages_banner");
|
||||
bannerIcon: UM.Theme.getIcon("ArrowDoubleCircleRight")
|
||||
bannerText: catalog.i18nc("@text", "Manage your Ultimaker Cura plugins and material profiles here. Make sure to keep your plugins up to date and backup your setup regularly.")
|
||||
bannerText: catalog.i18nc("@text", "Manage your UltiMaker Cura plugins and material profiles here. Make sure to keep your plugins up to date and backup your setup regularly.")
|
||||
bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/4411125921426/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-manage"
|
||||
onRemoveBanner: function() {
|
||||
UM.Preferences.setValue("cura/market_place_show_manage_packages_banner", false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import UM 1.4 as UM
|
||||
@ -9,7 +9,7 @@ Packages
|
||||
|
||||
bannerVisible: UM.Preferences.getValue("cura/market_place_show_material_banner")
|
||||
bannerIcon: UM.Theme.getIcon("Spool")
|
||||
bannerText: catalog.i18nc("@text", "Select and install material profiles optimised for your Ultimaker 3D printers.")
|
||||
bannerText: catalog.i18nc("@text", "Select and install material profiles optimised for your UltiMaker 3D printers.")
|
||||
bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/360011968360/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-materials"
|
||||
onRemoveBanner: function() {
|
||||
UM.Preferences.setValue("cura/market_place_show_material_banner", false);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import UM 1.4 as UM
|
||||
@ -9,7 +9,7 @@ Packages
|
||||
|
||||
bannerVisible: UM.Preferences.getValue("cura/market_place_show_plugin_banner")
|
||||
bannerIcon: UM.Theme.getIcon("Shop")
|
||||
bannerText: catalog.i18nc("@text", "Streamline your workflow and customize your Ultimaker Cura experience with plugins contributed by our amazing community of users.")
|
||||
bannerText: catalog.i18nc("@text", "Streamline your workflow and customize your UltiMaker Cura experience with plugins contributed by our amazing community of users.")
|
||||
bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/360011968360/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-plugins"
|
||||
onRemoveBanner: function() {
|
||||
UM.Preferences.setValue("cura/market_place_show_plugin_banner", false)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.15
|
||||
@ -18,9 +18,9 @@ Control
|
||||
{
|
||||
switch(packageData.packageType)
|
||||
{
|
||||
case "plugin": return catalog.i18nc("@info", "Ultimaker Verified Plug-in");
|
||||
case "material": return catalog.i18nc("@info", "Ultimaker Certified Material");
|
||||
default: return catalog.i18nc("@info", "Ultimaker Verified Package");
|
||||
case "plugin": return catalog.i18nc("@info", "UltiMaker Verified Plug-in");
|
||||
case "material": return catalog.i18nc("@info", "UltiMaker Certified Material");
|
||||
default: return catalog.i18nc("@info", "UltiMaker Verified Package");
|
||||
}
|
||||
}
|
||||
visible: parent.hovered
|
||||
|
@ -19,5 +19,7 @@ Item
|
||||
width: UM.Theme.getSize("machine_selector_widget").width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
|
||||
machineListModel: Cura.MachineListModel {}
|
||||
}
|
||||
}
|
@ -150,6 +150,7 @@ Item
|
||||
width: parent.width / 2 - UM.Theme.getSize("default_margin").width
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
textRole: "text"
|
||||
forceHighlight: base.hovered
|
||||
|
||||
model: ListModel
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ class PauseAtHeight(Script):
|
||||
"description": "Whether to pause at a certain height or at a certain layer.",
|
||||
"type": "enum",
|
||||
"options": {"height": "Height", "layer_no": "Layer Number"},
|
||||
"default_value": "height"
|
||||
"default_value": "layer_no"
|
||||
},
|
||||
"pause_height":
|
||||
{
|
||||
@ -58,16 +58,25 @@ class PauseAtHeight(Script):
|
||||
"default_value": "marlin",
|
||||
"value": "\\\"griffin\\\" if machine_gcode_flavor==\\\"Griffin\\\" else \\\"reprap\\\" if machine_gcode_flavor==\\\"RepRap (RepRap)\\\" else \\\"repetier\\\" if machine_gcode_flavor==\\\"Repetier\\\" else \\\"bq\\\" if \\\"BQ\\\" in machine_name or \\\"Flying Bear Ghost 4S\\\" in machine_name else \\\"marlin\\\""
|
||||
},
|
||||
"hold_steppers_on":
|
||||
{
|
||||
"label": "Keep motors engaged",
|
||||
"description": "Keep the steppers engaged to allow change of filament without moving the head. Applying too much force will move the head/bed anyway",
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "pause_method != \\\"griffin\\\""
|
||||
},
|
||||
"disarm_timeout":
|
||||
{
|
||||
"label": "Disarm timeout",
|
||||
"description": "After this time steppers are going to disarm (meaning that they can easily lose their positions). Set this to 0 if you don't want to set any duration.",
|
||||
"description": "After this time steppers are going to disarm (meaning that they can easily lose their positions). Set this to 0 if you don't want to set any duration and disarm immediately.",
|
||||
"type": "int",
|
||||
"value": "0",
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "1800",
|
||||
"unit": "s"
|
||||
"unit": "s",
|
||||
"enabled": "not hold_steppers_on"
|
||||
},
|
||||
"head_park_enabled":
|
||||
{
|
||||
@ -192,6 +201,22 @@ class PauseAtHeight(Script):
|
||||
"default_value": "RepRap (Marlin/Sprinter)",
|
||||
"enabled": false
|
||||
},
|
||||
"beep_at_pause":
|
||||
{
|
||||
"label": "Beep at pause",
|
||||
"description": "Make a beep when pausing",
|
||||
"type": "bool",
|
||||
"default_value": true
|
||||
},
|
||||
"beep_length":
|
||||
{
|
||||
"label": "Beep length",
|
||||
"description": "How much should the beep last",
|
||||
"type": "int",
|
||||
"default_value": "1000",
|
||||
"unit": "ms",
|
||||
"enabled": "beep_at_pause"
|
||||
},
|
||||
"custom_gcode_before_pause":
|
||||
{
|
||||
"label": "G-code Before Pause",
|
||||
@ -242,6 +267,7 @@ class PauseAtHeight(Script):
|
||||
pause_at = self.getSettingValueByKey("pause_at")
|
||||
pause_height = self.getSettingValueByKey("pause_height")
|
||||
pause_layer = self.getSettingValueByKey("pause_layer")
|
||||
hold_steppers_on = self.getSettingValueByKey("hold_steppers_on")
|
||||
disarm_timeout = self.getSettingValueByKey("disarm_timeout")
|
||||
retraction_amount = self.getSettingValueByKey("retraction_amount")
|
||||
retraction_speed = self.getSettingValueByKey("retraction_speed")
|
||||
@ -260,6 +286,8 @@ class PauseAtHeight(Script):
|
||||
display_text = self.getSettingValueByKey("display_text")
|
||||
gcode_before = self.getSettingValueByKey("custom_gcode_before_pause")
|
||||
gcode_after = self.getSettingValueByKey("custom_gcode_after_pause")
|
||||
beep_at_pause = self.getSettingValueByKey("beep_at_pause")
|
||||
beep_length = self.getSettingValueByKey("beep_length")
|
||||
|
||||
pause_method = self.getSettingValueByKey("pause_method")
|
||||
pause_command = {
|
||||
@ -437,19 +465,26 @@ class PauseAtHeight(Script):
|
||||
prepend_gcode += "M117 " + display_text + "\n"
|
||||
|
||||
# Set the disarm timeout
|
||||
if disarm_timeout > 0:
|
||||
prepend_gcode += self.putValue(M = 18, S = disarm_timeout) + " ; Set the disarm timeout\n"
|
||||
if hold_steppers_on:
|
||||
prepend_gcode += self.putValue(M = 84, S = 3600) + " ; Keep steppers engaged for 1h\n"
|
||||
elif disarm_timeout > 0:
|
||||
prepend_gcode += self.putValue(M = 84, S = disarm_timeout) + " ; Set the disarm timeout\n"
|
||||
|
||||
# Beep at pause
|
||||
if beep_at_pause:
|
||||
prepend_gcode += self.putValue(M = 300, S = 440, P = beep_length) + " ; Beep\n"
|
||||
|
||||
|
||||
# Set a custom GCODE section before pause
|
||||
if gcode_before:
|
||||
prepend_gcode += gcode_before + "\n"
|
||||
prepend_gcode += gcode_before.replace(";","\n") + "\n"
|
||||
|
||||
# Wait till the user continues printing
|
||||
prepend_gcode += pause_command + " ; Do the actual pause\n"
|
||||
|
||||
# Set a custom GCODE section before pause
|
||||
# Set a custom GCODE section after pause
|
||||
if gcode_after:
|
||||
prepend_gcode += gcode_after + "\n"
|
||||
prepend_gcode += gcode_after.replace(";","\n") + "\n"
|
||||
|
||||
if pause_method == "repetier":
|
||||
#Push the filament back,
|
||||
|
@ -55,6 +55,50 @@ Item
|
||||
Layout.preferredWidth: parent.machineSelectorWidth
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
machineManager: Cura.MachineManager
|
||||
onSelectPrinter: function(machine)
|
||||
{
|
||||
toggleContent();
|
||||
Cura.MachineManager.setActiveMachine(machine.id);
|
||||
}
|
||||
|
||||
machineListModel: Cura.MachineListModel {}
|
||||
|
||||
buttons: [
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: addPrinterButton
|
||||
leftPadding: UM.Theme.getSize("default_margin").width
|
||||
rightPadding: UM.Theme.getSize("default_margin").width
|
||||
text: catalog.i18nc("@button", "Add printer")
|
||||
// The maximum width of the button is half of the total space, minus the padding of the parent, the left
|
||||
// padding of the component and half the spacing because of the space between buttons.
|
||||
fixedWidthMode: true
|
||||
width: Math.round(parent.width / 2 - leftPadding * 1.5)
|
||||
onClicked:
|
||||
{
|
||||
machineSelection.toggleContent()
|
||||
Cura.Actions.addMachine.trigger()
|
||||
}
|
||||
},
|
||||
Cura.SecondaryButton
|
||||
{
|
||||
id: managePrinterButton
|
||||
leftPadding: UM.Theme.getSize("default_margin").width
|
||||
rightPadding: UM.Theme.getSize("default_margin").width
|
||||
text: catalog.i18nc("@button", "Manage printers")
|
||||
fixedWidthMode: true
|
||||
// The maximum width of the button is half of the total space, minus the padding of the parent, the right
|
||||
// padding of the component and half the spacing because of the space between buttons.
|
||||
width: Math.round(parent.width / 2 - rightPadding * 1.5)
|
||||
onClicked:
|
||||
{
|
||||
machineSelection.toggleContent()
|
||||
Cura.Actions.configureMachines.trigger()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Cura.ConfigurationMenu
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -70,7 +70,7 @@ Window
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
text: catalog.i18nc("@text:window", "Ultimaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is shared:")
|
||||
text: catalog.i18nc("@text:window", "UltiMaker Cura collects anonymous data in order to improve the print quality and user experience. Below is an example of all the data that is shared:")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class UFPReader(MeshReader):
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/x-ufp",
|
||||
comment = "Ultimaker Format Package",
|
||||
comment = "UltiMaker Format Package",
|
||||
suffixes = ["ufp"]
|
||||
)
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Copyright (c) 2019 Ultimaker B.V.
|
||||
#Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import sys
|
||||
|
||||
@ -19,7 +19,7 @@ def getMetaData():
|
||||
{
|
||||
"mime_type": "application/x-ufp",
|
||||
"extension": "ufp",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Ultimaker Format Package")
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "UltiMaker Format Package")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import json
|
||||
from dataclasses import asdict
|
||||
from typing import cast, List, Dict
|
||||
|
||||
from Charon.VirtualFile import VirtualFile # To open UFP files.
|
||||
@ -40,7 +41,7 @@ class UFPWriter(MeshWriter):
|
||||
MimeTypeDatabase.addMimeType(
|
||||
MimeType(
|
||||
name = "application/x-ufp",
|
||||
comment = "Ultimaker Format Package",
|
||||
comment = "UltiMaker Format Package",
|
||||
suffixes = ["ufp"]
|
||||
)
|
||||
)
|
||||
@ -225,8 +226,7 @@ class UFPWriter(MeshWriter):
|
||||
"changes": {},
|
||||
"all_settings": {},
|
||||
},
|
||||
"intent": machine_manager.activeIntentCategory,
|
||||
"quality": machine_manager.activeQualityOrQualityChangesName,
|
||||
"quality": asdict(machine_manager.activeQualityDisplayNameMap()),
|
||||
}
|
||||
|
||||
global_stack = cast(GlobalStack, Application.getInstance().getGlobalContainerStack())
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Copyright (c) 2018 Ultimaker B.V.
|
||||
#Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import sys
|
||||
|
||||
@ -25,7 +25,7 @@ def getMetaData():
|
||||
"mime_type": "application/x-ufp",
|
||||
"mode": MeshWriter.OutputMode.BinaryMode,
|
||||
"extension": "ufp",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Ultimaker Format Package")
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "UltiMaker Format Package")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.3
|
||||
@ -284,7 +284,7 @@ Item
|
||||
MonitorInfoBlurb
|
||||
{
|
||||
id: cameraDisabledInfo
|
||||
text: catalog.i18nc("@info", "Webcam feeds for cloud printers cannot be viewed from Ultimaker Cura." +
|
||||
text: catalog.i18nc("@info", "Webcam feeds for cloud printers cannot be viewed from UltiMaker Cura." +
|
||||
" Click \"Manage printer\" to visit Ultimaker Digital Factory and view this webcam.")
|
||||
target: cameraButton
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.15
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from time import time
|
||||
import os
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import os
|
||||
from typing import Dict, List, Optional, Set
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Copyright (c) 2022 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
# Copyright (c) 2022 UltiMaker
|
||||
# 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Ultimaker B.V.
|
||||
// Copyright (c) 2022 UltiMaker
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.10
|
||||
@ -27,7 +27,7 @@ Cura.MachineAction
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Please select any upgrades made to this Ultimaker Original")
|
||||
text: catalog.i18nc("@label","Please select any upgrades made to this UltiMaker Original")
|
||||
font: UM.Theme.getFont("medium")
|
||||
}
|
||||
|
||||
|
@ -1125,21 +1125,28 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
id_list = list(id_list)
|
||||
return id_list
|
||||
|
||||
__product_to_id_map: Optional[Dict[str, List[str]]] = None
|
||||
|
||||
@classmethod
|
||||
def getProductIdMap(cls) -> Dict[str, List[str]]:
|
||||
"""Gets a mapping from product names in the XML files to their definition IDs.
|
||||
|
||||
This loads the mapping from a file.
|
||||
"""
|
||||
if cls.__product_to_id_map is not None:
|
||||
return cls.__product_to_id_map
|
||||
|
||||
plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath("XmlMaterialProfile"))
|
||||
product_to_id_file = os.path.join(plugin_path, "product_to_id.json")
|
||||
with open(product_to_id_file, encoding = "utf-8") as f:
|
||||
product_to_id_map = json.load(f)
|
||||
product_to_id_map = {key: [value] for key, value in product_to_id_map.items()}
|
||||
contents = ""
|
||||
for line in f:
|
||||
contents += line if "#" not in line else "".join([line.replace("#", str(n)) for n in range(1, 12)])
|
||||
cls.__product_to_id_map = json.loads(contents)
|
||||
cls.__product_to_id_map = {key: [value] for key, value in cls.__product_to_id_map.items()}
|
||||
#This also loads "Ultimaker S5" -> "ultimaker_s5" even though that is not strictly necessary with the default to change spaces into underscores.
|
||||
#However it is not always loaded with that default; this mapping is also used in serialize() without that default.
|
||||
return product_to_id_map
|
||||
return cls.__product_to_id_map
|
||||
|
||||
@staticmethod
|
||||
def _parseCompatibleValue(value: str):
|
||||
|
@ -1,14 +1,11 @@
|
||||
{
|
||||
"Ultimaker 2": "ultimaker2",
|
||||
"Ultimaker 2 Extended": "ultimaker2_extended",
|
||||
"Ultimaker 2 Extended+": "ultimaker2_extended_plus",
|
||||
"Ultimaker 2 Go": "ultimaker2_go",
|
||||
"Ultimaker 2+": "ultimaker2_plus",
|
||||
"Ultimaker 2+ Connect": "ultimaker2_plus_connect",
|
||||
"Ultimaker 3": "ultimaker3",
|
||||
"Ultimaker 3 Extended": "ultimaker3_extended",
|
||||
"Ultimaker S3": "ultimaker_s3",
|
||||
"Ultimaker S5": "ultimaker_s5",
|
||||
"Ultimaker #": "ultimaker#",
|
||||
"Ultimaker # Extended": "ultimaker#_extended",
|
||||
"Ultimaker # Extended+": "ultimaker#_extended_plus",
|
||||
"Ultimaker # Go": "ultimaker#_go",
|
||||
"Ultimaker #+": "ultimaker#_plus",
|
||||
"Ultimaker #+ Connect": "ultimaker#_plus_connect",
|
||||
"Ultimaker S#": "ultimaker_s#",
|
||||
"Ultimaker Original": "ultimaker_original",
|
||||
"Ultimaker Original+": "ultimaker_original_plus",
|
||||
"Ultimaker Original Dual Extrusion": "ultimaker_original_dual",
|
||||
|
33
printer-linter/README.md
Normal file
33
printer-linter/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Printer Linter
|
||||
Printer linter is a python package that does linting on Cura definitions files.
|
||||
Running this on your definition files will get them ready for a pull request.
|
||||
|
||||
## Running Locally
|
||||
From the Cura root folder.
|
||||
|
||||
```python3 printer-linter/src/terminal.py "flashforge_dreamer_nx.def.json" "flashforge_base.def.json" --fix --format```
|
||||
|
||||
## Developing
|
||||
### Printer Linter Rules
|
||||
Inside ```.printer-linter``` you can find a list of rules. These are seperated into roughly three categories.
|
||||
|
||||
1. Checks
|
||||
1. These rules are about checking if a file meets some requirements that can't be fixed by replacing its content.
|
||||
2. An example of a check is ```diagnostic-mesh-file-extension``` this checks if a mesh file extension is acceptable.
|
||||
2. Format
|
||||
1. These rules are purely about how a file is structured, not content.
|
||||
2. An example of a format rule is ```format-definition-bracket-newline``` This rule says that when assigning a dict value the bracket should go on a new line.
|
||||
3. Fixes
|
||||
1. These are about the content of the file.
|
||||
2. An example of a fix is ```diagnostic-definition-redundant-override``` This removes settings that have already been defined by a parent definition
|
||||
|
||||
### Linters
|
||||
Linters find issues within a file. There are separate linters for each type of file. The linter that is used is decided by the create function in factory.py. All linters implement the abstract class Linter.
|
||||
|
||||
A Linter class returns an iterator of Diagnostics, each diagnostic is an issue with the file. The diagnostics can also contain suggested fixes.
|
||||
|
||||
### Formatters
|
||||
Formatters load a file reformat it and write it to disk. There are separate formatters for each file type. All formatters implement the abstract class Formatter.
|
||||
|
||||
Formatters should format based on the Format rules in .printer-linter
|
||||
|
17
printer-linter/pyproject.toml
Normal file
17
printer-linter/pyproject.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "printerlinter"
|
||||
description = "Cura UltiMaker printer linting tool"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
{ name = "UltiMaker", email = "cura@ultimaker.com" }
|
||||
]
|
||||
dependencies = [
|
||||
"pyyaml"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
printer-linter = "terminal:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
10
printer-linter/setup.cfg
Normal file
10
printer-linter/setup.cfg
Normal file
@ -0,0 +1,10 @@
|
||||
[metadata]
|
||||
name = printerlinter
|
||||
|
||||
[options]
|
||||
package_dir=
|
||||
=src
|
||||
packages=find:
|
||||
|
||||
[options.packages.find]
|
||||
where=src
|
6
printer-linter/setup.py
Normal file
6
printer-linter/setup.py
Normal file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
4
printer-linter/src/printerlinter/__init__.py
Normal file
4
printer-linter/src/printerlinter/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .diagnostic import Diagnostic
|
||||
from .factory import getLinter
|
||||
|
||||
__all__ = ["Diagnostic", "getLinter"]
|
34
printer-linter/src/printerlinter/diagnostic.py
Normal file
34
printer-linter/src/printerlinter/diagnostic.py
Normal file
@ -0,0 +1,34 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from .replacement import Replacement
|
||||
|
||||
|
||||
class Diagnostic:
|
||||
def __init__(self, file: Path, diagnostic_name: str, message: str, level: str, offset: int, replacements: Optional[List[Replacement]] = None) -> None:
|
||||
""" A diagnosis of an issue in "file" at "offset" in that file. May include suggested replacements.
|
||||
|
||||
@param file: The path to the file this diagnostic is for.
|
||||
@param diagnostic_name: The name of the diagnostic rule that spawned this result. A list can be found in .printer-linter.
|
||||
@param message: A message explaining the issue with this file.
|
||||
@param level: How important this diagnostic is, ranges from Warning -> Error.
|
||||
@param offset: The offset in file where the issue is.
|
||||
@param replacements: A list of Replacement that contain replacement text.
|
||||
"""
|
||||
self.file = file
|
||||
self.diagnostic_name = diagnostic_name
|
||||
self.message = message
|
||||
self.offset = offset
|
||||
self.level = level
|
||||
self.replacements = replacements
|
||||
|
||||
def toDict(self) -> Dict[str, Any]:
|
||||
return {"DiagnosticName": self.diagnostic_name,
|
||||
"DiagnosticMessage": {
|
||||
"Message": self.message,
|
||||
"FilePath": self.file.as_posix(),
|
||||
"FileOffset": self.offset,
|
||||
"Replacements": [] if self.replacements is None else [r.toDict() for r in self.replacements],
|
||||
},
|
||||
"Level": self.level
|
||||
}
|
26
printer-linter/src/printerlinter/factory.py
Normal file
26
printer-linter/src/printerlinter/factory.py
Normal file
@ -0,0 +1,26 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from .linters.profile import Profile
|
||||
from .linters.defintion import Definition
|
||||
from .linters.linter import Linter
|
||||
from .linters.meshes import Meshes
|
||||
|
||||
|
||||
def getLinter(file: Path, settings: dict) -> Optional[Linter]:
|
||||
""" Returns a Linter depending on the file format """
|
||||
if not file.exists():
|
||||
return None
|
||||
|
||||
if ".inst" in file.suffixes and ".cfg" in file.suffixes:
|
||||
return Profile(file, settings)
|
||||
|
||||
if ".def" in file.suffixes and ".json" in file.suffixes:
|
||||
if file.stem in ("fdmprinter.def", "fdmextruder.def"):
|
||||
return None
|
||||
return Definition(file, settings)
|
||||
|
||||
if file.parent.stem == "meshes":
|
||||
return Meshes(file, settings)
|
||||
|
||||
return None
|
4
printer-linter/src/printerlinter/formatters/__init__.py
Normal file
4
printer-linter/src/printerlinter/formatters/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .def_json_formatter import DefJsonFormatter
|
||||
from .inst_cfg_formatter import InstCfgFormatter
|
||||
|
||||
__all__ = ["DefJsonFormatter", "InstCfgFormatter"]
|
@ -0,0 +1,84 @@
|
||||
import json
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from .formatter import FileFormatter
|
||||
|
||||
|
||||
# Dictionary items with matching keys will be sorted as if they were the value
|
||||
# Example: "version" will be sorted as if it was "0"
|
||||
TOP_LEVEL_SORT_PRIORITY = {
|
||||
"version": "0",
|
||||
"name": "1",
|
||||
"inherits": "3",
|
||||
}
|
||||
|
||||
METADATA_SORT_PRIORITY = {
|
||||
"visible": "0",
|
||||
"author": "1",
|
||||
"manufacturer": "2",
|
||||
"file_formats": "3",
|
||||
"platform": "4",
|
||||
}
|
||||
|
||||
|
||||
class DefJsonFormatter(FileFormatter):
|
||||
def formatFile(self, file: Path):
|
||||
""" Format .def.json files according to the rules in settings.
|
||||
|
||||
You can assume that you will be running regex on standard formatted json files, because we load the json first and then
|
||||
dump it to a string. This means you only have to write regex that works on the output of json.dump()
|
||||
"""
|
||||
|
||||
definition = json.loads(file.read_text(), object_pairs_hook=OrderedDict)
|
||||
|
||||
if self._settings["format"].get("format-definition-sort-keys", True):
|
||||
definition = self.order_keys(definition)
|
||||
|
||||
content = json.dumps(definition, indent=self._settings["format"].get("format-definition-indent", 4))
|
||||
|
||||
if self._settings["format"].get("format-definition-bracket-newline", True):
|
||||
newline = re.compile(r"(\B\s+)(\"[\w\"]+)(\:\s\{)")
|
||||
content = newline.sub(r"\1\2:\1{", content)
|
||||
|
||||
if self._settings["format"].get("format-definition-single-value-single-line", True):
|
||||
single_value_dict = re.compile(r"(:)(\s*\n?.*\{\s+)(\".*)(\d*\s*\})(.*\n,?)")
|
||||
content = single_value_dict.sub(r"\1 { \3 }\5", content)
|
||||
|
||||
single_value_list = re.compile(r"(:)(\s*\n?.*\[\s+)(\".*)(\d*\s*\])(.*\n,?)")
|
||||
content = single_value_list.sub(r"\1 [ \3 ]\5", content)
|
||||
|
||||
if self._settings["format"].get("format-definition-paired-coordinate-array", True):
|
||||
paired_coordinates = re.compile(r"(\s*\[)\s*([-\d\.]+),\s*([-\d\.]+)[\s]*(\])")
|
||||
content = paired_coordinates.sub(r"\1\2, \3\4", content)
|
||||
|
||||
file.write_text(content)
|
||||
|
||||
def order_keys(self, json_content: OrderedDict) -> OrderedDict:
|
||||
""" Orders json keys lexicographically """
|
||||
# First order all keys (Recursive) lexicographically
|
||||
json_content_text = json.dumps(json_content, sort_keys=True)
|
||||
json_content = json.loads(json_content_text, object_pairs_hook=OrderedDict)
|
||||
|
||||
# Do a custom ordered sort on the top level items in the json. This is so that keys like "version" appear at the top.
|
||||
json_content = self.custom_sort_keys(json_content, TOP_LEVEL_SORT_PRIORITY)
|
||||
|
||||
# Do a custom ordered sort on collections that are one level deep into the json
|
||||
if "metadata" in json_content.keys():
|
||||
json_content["metadata"] = self.custom_sort_keys(json_content["metadata"], METADATA_SORT_PRIORITY)
|
||||
|
||||
return json_content
|
||||
|
||||
|
||||
def custom_sort_keys(self, ordered_dictionary: OrderedDict, sort_priority: Dict[str, str]) -> OrderedDict:
|
||||
""" Orders keys in dictionary lexicographically, except for keys with matching strings in sort_priority.
|
||||
|
||||
Keys in ordered_dictionary that match keys in sort_priority will sort based on the value in sort_priority.
|
||||
|
||||
@param ordered_dictionary: A dictionary that will have it's top level keys sorted
|
||||
@param sort_priority: A mapping from string keys to alternative strings to be used instead when sorting.
|
||||
@return: A dictionary sorted by it's top level keys
|
||||
"""
|
||||
return OrderedDict(sorted(ordered_dictionary.items(), key=lambda x: sort_priority[x[0]] if str(x[0]) in sort_priority.keys() else str(x[0])))
|
16
printer-linter/src/printerlinter/formatters/formatter.py
Normal file
16
printer-linter/src/printerlinter/formatters/formatter.py
Normal file
@ -0,0 +1,16 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class FileFormatter(ABC):
|
||||
def __init__(self, settings: dict) -> None:
|
||||
""" Yields Diagnostics for file, these are issues with the file such as bad text format or too large file size.
|
||||
|
||||
@param file: A file to generate diagnostics for
|
||||
@param settings: A list of settings containing rules for creating diagnostics
|
||||
"""
|
||||
self._settings = settings
|
||||
|
||||
@abstractmethod
|
||||
def formatFile(self, file: Path) -> None:
|
||||
pass
|
@ -0,0 +1,21 @@
|
||||
import configparser
|
||||
import json
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
|
||||
from .formatter import FileFormatter
|
||||
|
||||
class InstCfgFormatter(FileFormatter):
|
||||
def formatFile(self, file: Path):
|
||||
""" Format .inst.cfg files according to the rules in settings """
|
||||
config = configparser.ConfigParser()
|
||||
config.read(file)
|
||||
|
||||
if self._settings["format"].get("format-profile-sort-keys", True):
|
||||
for section in config._sections:
|
||||
config._sections[section] = OrderedDict(sorted(config._sections[section].items(), key=lambda t: t[0]))
|
||||
config._sections = OrderedDict(sorted(config._sections.items(), key=lambda t: t[0]))
|
||||
|
||||
with open(file, "w") as f:
|
||||
config.write(f, space_around_delimiters=self._settings["format"].get("format-profile-space-around-delimiters", True))
|
6
printer-linter/src/printerlinter/linters/__init__.py
Normal file
6
printer-linter/src/printerlinter/linters/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .profile import Profile
|
||||
from .meshes import Meshes
|
||||
from .linter import Linter
|
||||
from .defintion import Definition
|
||||
|
||||
__all__ = ["Profile", "Meshes", "Linter", "Definition"]
|
122
printer-linter/src/printerlinter/linters/defintion.py
Normal file
122
printer-linter/src/printerlinter/linters/defintion.py
Normal file
@ -0,0 +1,122 @@
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
|
||||
from ..diagnostic import Diagnostic
|
||||
from .linter import Linter
|
||||
from ..replacement import Replacement
|
||||
|
||||
|
||||
class Definition(Linter):
|
||||
""" Finds issues in definition files, such as overriding default parameters """
|
||||
def __init__(self, file: Path, settings: dict) -> None:
|
||||
super().__init__(file, settings)
|
||||
self._definitions = {}
|
||||
self._loadDefinitionFiles(file)
|
||||
self._content = self._file.read_text()
|
||||
self._loadBasePrinterSettings()
|
||||
|
||||
@property
|
||||
def base_def(self):
|
||||
if "fdmextruder" in self._definitions:
|
||||
return "fdmextruder"
|
||||
return "fdmprinter"
|
||||
|
||||
def check(self) -> Iterator[Diagnostic]:
|
||||
if self._settings["checks"].get("diagnostic-definition-redundant-override", False):
|
||||
for check in self.checkRedefineOverride():
|
||||
yield check
|
||||
|
||||
# Add other which will yield Diagnostic's
|
||||
# TODO: A check to determine if the user set value is with the min and max value defined in the parent and doesn't trigger a warning
|
||||
# TODO: A check if the key exist in the first place
|
||||
# TODO: Check if the model platform exist
|
||||
|
||||
yield
|
||||
|
||||
def checkRedefineOverride(self) -> Iterator[Diagnostic]:
|
||||
""" Checks if definition file overrides its parents settings with the same value. """
|
||||
definition_name = list(self._definitions.keys())[0]
|
||||
definition = self._definitions[definition_name]
|
||||
if "overrides" in definition and definition_name not in ("fdmprinter", "fdmextruder"):
|
||||
for key, value_dict in definition["overrides"].items():
|
||||
is_redefined, value, parent = self._isDefinedInParent(key, value_dict, definition['inherits'])
|
||||
if is_redefined:
|
||||
redefined = re.compile(r'.*(\"' + key + r'\"[\s\S]*?\{)[\s\S]*?(\}[,\"]?)')
|
||||
found = redefined.search(self._content)
|
||||
yield Diagnostic(
|
||||
file = self._file,
|
||||
diagnostic_name = "diagnostic-definition-redundant-override",
|
||||
message = f"Overriding {key} with the same value ({value}) as defined in parent definition: {definition['inherits']}",
|
||||
level = "Warning",
|
||||
offset = found.span(0)[0],
|
||||
replacements = [Replacement(
|
||||
file = self._file,
|
||||
offset = found.span(1)[0],
|
||||
length = found.span(2)[1] - found.span(1)[0],
|
||||
replacement_text = "")]
|
||||
)
|
||||
|
||||
def _loadDefinitionFiles(self, definition_file) -> None:
|
||||
""" Loads definition file contents into self._definitions. Also load parent definition if it exists. """
|
||||
definition_name = Path(definition_file.stem).stem
|
||||
|
||||
if not definition_file.exists() or definition_name in self._definitions:
|
||||
return
|
||||
|
||||
# Load definition file into dictionary
|
||||
self._definitions[definition_name] = json.loads(definition_file.read_text())
|
||||
|
||||
# Load parent definition if it exists
|
||||
if "inherits" in self._definitions[definition_name]:
|
||||
if self._definitions[definition_name]['inherits'] in ("fdmextruder", "fdmprinter"):
|
||||
parent_file = definition_file.parent.parent.joinpath("definitions", f"{self._definitions[definition_name]['inherits']}.def.json")
|
||||
else:
|
||||
parent_file = definition_file.parent.joinpath(f"{self._definitions[definition_name]['inherits']}.def.json")
|
||||
self._loadDefinitionFiles(parent_file)
|
||||
|
||||
def _isDefinedInParent(self, key, value_dict, inherits_from):
|
||||
if "overrides" not in self._definitions[inherits_from]:
|
||||
return self._isDefinedInParent(key, value_dict, self._definitions[inherits_from]["inherits"])
|
||||
|
||||
parent = self._definitions[inherits_from]["overrides"]
|
||||
if key not in self._definitions[self.base_def]["overrides"]:
|
||||
is_number = False
|
||||
else:
|
||||
is_number = self._definitions[self.base_def]["overrides"][key]["type"] in ("float", "int")
|
||||
for value in value_dict.values():
|
||||
if key in parent:
|
||||
check_values = [cv for cv in [parent[key].get("default_value", None), parent[key].get("value", None)] if cv is not None]
|
||||
for check_value in check_values:
|
||||
if is_number:
|
||||
try:
|
||||
v = str(float(value))
|
||||
except:
|
||||
v = value
|
||||
try:
|
||||
cv = str(float(check_value))
|
||||
except:
|
||||
cv = check_value
|
||||
else:
|
||||
v = value
|
||||
cv = check_value
|
||||
if v == cv:
|
||||
return True, value, parent
|
||||
|
||||
if "inherits" in parent:
|
||||
return self._isDefinedInParent(key, value_dict, parent["inherits"])
|
||||
return False, None, None
|
||||
|
||||
def _loadBasePrinterSettings(self):
|
||||
""" TODO @Jelle please explain why this """
|
||||
settings = {}
|
||||
for k, v in self._definitions[self.base_def]["settings"].items():
|
||||
self._getSetting(k, v, settings)
|
||||
self._definitions[self.base_def] = {"overrides": settings}
|
||||
|
||||
def _getSetting(self, name, setting, settings) -> None:
|
||||
if "children" in setting:
|
||||
for childname, child in setting["children"].items():
|
||||
self._getSetting(childname, child, settings)
|
||||
settings |= {name: setting}
|
20
printer-linter/src/printerlinter/linters/linter.py
Normal file
20
printer-linter/src/printerlinter/linters/linter.py
Normal file
@ -0,0 +1,20 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
|
||||
from ..diagnostic import Diagnostic
|
||||
|
||||
|
||||
class Linter(ABC):
|
||||
def __init__(self, file: Path, settings: dict) -> None:
|
||||
""" Yields Diagnostics for file, these are issues with the file such as bad text format or too large file size.
|
||||
|
||||
@param file: A file to generate diagnostics for
|
||||
@param settings: A list of settings containing rules for creating diagnostics
|
||||
"""
|
||||
self._settings = settings
|
||||
self._file = file
|
||||
|
||||
@abstractmethod
|
||||
def check(self) -> Iterator[Diagnostic]:
|
||||
pass
|
47
printer-linter/src/printerlinter/linters/meshes.py
Normal file
47
printer-linter/src/printerlinter/linters/meshes.py
Normal file
@ -0,0 +1,47 @@
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
|
||||
from ..diagnostic import Diagnostic
|
||||
from .linter import Linter
|
||||
|
||||
|
||||
class Meshes(Linter):
|
||||
def __init__(self, file: Path, settings: dict) -> None:
|
||||
""" Finds issues in model files, such as incorrect file format or too large size """
|
||||
super().__init__(file, settings)
|
||||
self._max_file_size = self._settings.get("diagnostic-mesh-file-size", 1e6)
|
||||
|
||||
def check(self) -> Iterator[Diagnostic]:
|
||||
if self._settings["checks"].get("diagnostic-mesh-file-extension", False):
|
||||
for check in self.checkFileFormat():
|
||||
yield check
|
||||
|
||||
if self._settings["checks"].get("diagnostic-mesh-file-size", False):
|
||||
for check in self.checkFileSize():
|
||||
yield check
|
||||
|
||||
yield
|
||||
|
||||
def checkFileFormat(self) -> Iterator[Diagnostic]:
|
||||
""" Check if mesh is in supported format """
|
||||
if self._file.suffix.lower() not in (".3mf", ".obj", ".stl"):
|
||||
yield Diagnostic(
|
||||
file = self._file,
|
||||
diagnostic_name = "diagnostic-mesh-file-extension",
|
||||
message = f"Extension {self._file.suffix} not supported, use 3mf, obj or stl",
|
||||
level = "Error",
|
||||
offset = 1
|
||||
)
|
||||
yield
|
||||
|
||||
def checkFileSize(self) -> Iterator[Diagnostic]:
|
||||
""" Check if file is within size limits for Cura """
|
||||
if self._file.stat().st_size > self._max_file_size:
|
||||
yield Diagnostic(
|
||||
file = self._file,
|
||||
diagnostic_name = "diagnostic-mesh-file-size",
|
||||
message = f"Mesh file with a size {self._file.stat().st_size} is bigger then allowed maximum of {self._max_file_size}",
|
||||
level = "Error",
|
||||
offset = 1
|
||||
)
|
||||
yield
|
9
printer-linter/src/printerlinter/linters/profile.py
Normal file
9
printer-linter/src/printerlinter/linters/profile.py
Normal file
@ -0,0 +1,9 @@
|
||||
from typing import Iterator
|
||||
|
||||
from ..diagnostic import Diagnostic
|
||||
from .linter import Linter
|
||||
|
||||
|
||||
class Profile(Linter):
|
||||
def check(self) -> Iterator[Diagnostic]:
|
||||
yield
|
21
printer-linter/src/printerlinter/replacement.py
Normal file
21
printer-linter/src/printerlinter/replacement.py
Normal file
@ -0,0 +1,21 @@
|
||||
from pathlib import Path
|
||||
|
||||
class Replacement:
|
||||
def __init__(self, file: Path, offset: int, length: int, replacement_text: str):
|
||||
""" Replacement text for file between offset and offset+length.
|
||||
|
||||
@param file: File to replace text in
|
||||
@param offset: Offset in file to start text replace
|
||||
@param length: Length of text that will be replaced. offset -> offset+length is the section of text to replace.
|
||||
@param replacement_text: Text to insert of offset in file.
|
||||
"""
|
||||
self.file = file
|
||||
self.offset = offset
|
||||
self.length = length
|
||||
self.replacement_text = replacement_text
|
||||
|
||||
def toDict(self) -> dict:
|
||||
return {"FilePath": self.file.as_posix(),
|
||||
"Offset": self.offset,
|
||||
"Length": self.length,
|
||||
"ReplacementText": self.replacement_text}
|
117
printer-linter/src/terminal.py
Normal file
117
printer-linter/src/terminal.py
Normal file
@ -0,0 +1,117 @@
|
||||
from argparse import ArgumentParser
|
||||
from os import getcwd
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
import yaml
|
||||
|
||||
from printerlinter import factory
|
||||
from printerlinter.diagnostic import Diagnostic
|
||||
from printerlinter.formatters.def_json_formatter import DefJsonFormatter
|
||||
from printerlinter.formatters.inst_cfg_formatter import InstCfgFormatter
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = ArgumentParser(
|
||||
description="UltiMaker Cura printer linting, static analysis and formatting of Cura printer definitions and other resources")
|
||||
parser.add_argument("--setting", required=False, type=Path, help="Path to the `.printer-linter` setting file")
|
||||
parser.add_argument("--report", required=False, type=Path, help="Path where the diagnostic report should be stored")
|
||||
parser.add_argument("--format", action="store_true", help="Format the files")
|
||||
parser.add_argument("--diagnose", action="store_true", help="Diagnose the files")
|
||||
parser.add_argument("--fix", action="store_true", help="Attempt to apply the suggested fixes on the files")
|
||||
parser.add_argument("Files", metavar="F", type=Path, nargs="+", help="Files or directories to format")
|
||||
|
||||
args = parser.parse_args()
|
||||
files = extractFilePaths(args.Files)
|
||||
setting_path = args.setting
|
||||
to_format = args.format
|
||||
to_fix = args.fix
|
||||
to_diagnose = args.diagnose
|
||||
report = args.report
|
||||
|
||||
if not setting_path:
|
||||
setting_path = Path(getcwd(), ".printer-linter")
|
||||
|
||||
if not setting_path.exists():
|
||||
print(f"Can't find the settings: {setting_path}")
|
||||
return
|
||||
|
||||
with open(setting_path, "r") as f:
|
||||
settings = yaml.load(f, yaml.FullLoader)
|
||||
|
||||
full_body_check = {"Diagnostics": []}
|
||||
|
||||
if to_fix or to_diagnose:
|
||||
for file in files:
|
||||
diagnostics = diagnoseIssuesWithFile(file, settings)
|
||||
full_body_check["Diagnostics"].extend([d.toDict() for d in diagnostics])
|
||||
|
||||
results = yaml.dump(full_body_check, default_flow_style=False, indent=4, width=240)
|
||||
|
||||
if report:
|
||||
report.write_text(results)
|
||||
else:
|
||||
print(results)
|
||||
|
||||
if to_fix:
|
||||
for file in files:
|
||||
if f"{file.as_posix()}" in full_body_check:
|
||||
applyFixesToFile(file, settings, full_body_check)
|
||||
|
||||
if to_format:
|
||||
for file in files:
|
||||
applyFormattingToFile(file, settings)
|
||||
|
||||
|
||||
def diagnoseIssuesWithFile(file: Path, settings: dict) -> List[Diagnostic]:
|
||||
""" For file, runs all diagnostic checks in settings and returns a list of diagnostics """
|
||||
linter = factory.getLinter(file, settings)
|
||||
|
||||
if not linter:
|
||||
return []
|
||||
|
||||
return list(filter(lambda d: d is not None, linter.check()))
|
||||
|
||||
|
||||
def applyFixesToFile(file, settings, full_body_check) -> None:
|
||||
if not file.exists():
|
||||
return
|
||||
ext = ".".join(file.name.split(".")[-2:])
|
||||
|
||||
if ext == "def.json":
|
||||
issues = full_body_check[f"{file.as_posix()}"]
|
||||
for issue in issues:
|
||||
if issue["diagnostic"] == "diagnostic-definition-redundant-override" and settings["fixes"].get(
|
||||
"diagnostic-definition-redundant-override", True):
|
||||
pass
|
||||
|
||||
|
||||
def applyFormattingToFile(file: Path, settings) -> None:
|
||||
if not file.exists():
|
||||
return
|
||||
|
||||
ext = ".".join(file.name.split(".")[-2:])
|
||||
|
||||
if ext == "def.json":
|
||||
formatter = DefJsonFormatter(settings)
|
||||
formatter.formatFile(file)
|
||||
|
||||
if ext == "inst.cfg":
|
||||
formatter = InstCfgFormatter(settings)
|
||||
formatter.formatFile(file)
|
||||
|
||||
|
||||
def extractFilePaths(paths: List[Path]) -> List[Path]:
|
||||
""" Takes list of files and directories, returns the files as well as all files within directories as a List """
|
||||
file_paths = []
|
||||
for path in paths:
|
||||
if path.is_dir():
|
||||
file_paths.extend(path.rglob("**/*"))
|
||||
else:
|
||||
file_paths.append(path)
|
||||
|
||||
return file_paths
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,5 +1,6 @@
|
||||
pytest
|
||||
pyinstaller
|
||||
pyinstaller-hooks-contrib
|
||||
pyyaml
|
||||
sip==6.5.1
|
||||
jinja2
|
||||
|
@ -1 +1 @@
|
||||
git+https://github.com/ultimaker/libcharon@master#egg=charon
|
||||
git+https://github.com/ultimaker/libcharon@master/s-line#egg=charon
|
||||
|
@ -10,7 +10,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -27,7 +27,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -61,7 +61,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -78,7 +78,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -95,7 +95,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -112,7 +112,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -129,7 +129,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -146,7 +146,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -163,7 +163,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -180,7 +180,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -197,7 +197,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -214,7 +214,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -248,7 +248,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -265,7 +265,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -282,7 +282,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -316,7 +316,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -333,7 +333,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -350,7 +350,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -367,7 +367,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -384,7 +384,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -401,7 +401,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -418,7 +418,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -435,7 +435,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -452,7 +452,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -469,7 +469,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -486,7 +486,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -503,7 +503,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -520,7 +520,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -537,7 +537,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -554,7 +554,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -571,7 +571,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -588,7 +588,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -605,7 +605,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -622,7 +622,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -639,7 +639,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -656,7 +656,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -673,7 +673,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -690,7 +690,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -707,7 +707,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -724,7 +724,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -741,7 +741,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -758,7 +758,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -775,7 +775,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -792,7 +792,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -809,7 +809,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -826,7 +826,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -843,7 +843,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -860,7 +860,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -877,7 +877,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -894,7 +894,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -911,7 +911,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -928,7 +928,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -945,7 +945,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -962,7 +962,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -979,7 +979,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -997,7 +997,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -1031,7 +1031,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -1048,7 +1048,7 @@
|
||||
"website": "https://ultimaker.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://ultimaker.com"
|
||||
}
|
||||
@ -1609,7 +1609,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1628,7 +1628,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/breakaway",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1647,7 +1647,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1666,7 +1666,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/cpe",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1685,7 +1685,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1704,7 +1704,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/pc",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1723,7 +1723,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1742,7 +1742,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/pp",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1761,7 +1761,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/abs",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1780,7 +1780,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/tpu-95a",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
@ -1799,7 +1799,7 @@
|
||||
"website": "https://ultimaker.com/products/materials/tough-pla",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "Ultimaker B.V.",
|
||||
"display_name": "UltiMaker",
|
||||
"email": "materials@ultimaker.com",
|
||||
"website": "https://ultimaker.com",
|
||||
"description": "Professional 3D printing made accessible.",
|
||||
|
@ -29,7 +29,7 @@
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"gantry_height": { "value": "0" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_start_gcode": { "default_value": ";Profil Homepage: https://github.com/NilsRo/Cura_Anycubic_MegaS_Profile\n\n;Slicer Information - (Support for OctoPrint Slicer Estimator)\n;Slicer info:material_guid;{material_guid}\n;Slicer info:material_id;{material_id}\n;Slicer info:material_brand;{material_brand}\n;Slicer info:material_name;{material_name}\n;Slicer info:filament_cost;{filament_cost}\n;Slicer info:material_bed_temperature;{material_bed_temperature}\n;Slicer info:material_bed_temperature_layer_0;{material_bed_temperature_layer_0}\n;Slicer info:material_print_temperature;{material_print_temperature}\n;Slicer info:material_print_temperature_layer_0;{material_print_temperature_layer_0}\n;Slicer info:material_flow;{material_flow}\n;Slicer info:layer_height;{layer_height}\n;Slicer info:machine_nozzle_size;{machine_nozzle_size}\n;Slicer info:wall_thickness;{wall_thickness}\n;Slicer info:speed_print;{speed_print}\n;Slicer info:speed_topbottom;{speed_topbottom}\n;Slicer info:travel_speed;{travel_speed}\n;Slicer info:support;{support}\n;Slicer info:retraction_speed;{retraction_speed}\n;Slicer info:retraction_amount;{retraction_amount}\n;Slicer info:layer_height;{layer_height}\n;Slicer info:infill_pattern;{infill_pattern}\n;Slicer info:infill_sparse_density;{infill_sparse_density}\n;Slicer info:cool_fan_enabled;{cool_fan_enabled}\n;Slicer info:cool_fan_speed;{cool_fan_speed}\n;Slicer info:sliced_at;{day} {date} {time}\nG21 ; metric values \nG90 ; absolute positioning \nM82 ; set extruder to absolute mode \nM107 ; start with the fan off \nM140 S{material_bed_temperature_layer_0} ; Start heating the bed \nG4 S60 ; wait 1 minute \nM104 S{material_print_temperature_layer_0} ; start heating the hot end \nM190 S{material_bed_temperature_layer_0} ; wait for bed \nM109 S{material_print_temperature_layer_0} ; wait for hotend \nM300 S1000 P500 ; BEEP heating done \nG28 X0 Y10 Z0 ; move X/Y to min endstops \nM420 S1 ; Enable leveling \nM420 Z2.0 ; Set leveling fading height to 2 mm \nG0 Z0.15 ; lift nozzle a bit \nG92 E0 ; zero the extruded length \nG1 X50 E20 F500 ; Extrude 20mm of filament in a 5cm line. \nG92 E0 ; zero the extruded length again \nG1 E-2 F500 ; Retract a little \nG1 X50 F500 ; wipe away from the filament line\nG1 X100 F9000 ; Quickly wipe away from the filament line" },
|
||||
"machine_start_gcode": { "default_value": ";Profil Homepage: https://github.com/NilsRo/Cura_Anycubic_MegaS_Profile\n\n;Slicer Information - (Support for OctoPrint Slicer Estimator)\n;Slicer info:material_guid;{material_guid}\n;Slicer info:material_id;{material_id}\n;Slicer info:material_brand;{material_brand}\n;Slicer info:material_name;{material_name}\n;Slicer info:filament_cost;{filament_cost}\n;Slicer info:material_bed_temperature;{material_bed_temperature}\n;Slicer info:material_bed_temperature_layer_0;{material_bed_temperature_layer_0}\n;Slicer info:material_print_temperature;{material_print_temperature}\n;Slicer info:material_print_temperature_layer_0;{material_print_temperature_layer_0}\n;Slicer info:material_flow;{material_flow}\n;Slicer info:layer_height;{layer_height}\n;Slicer info:machine_nozzle_size;{machine_nozzle_size}\n;Slicer info:wall_thickness;{wall_thickness}\n;Slicer info:speed_print;{speed_print}\n;Slicer info:speed_topbottom;{speed_topbottom}\n;Slicer info:travel_speed;{travel_speed}\n;Slicer info:support;{support}\n;Slicer info:retraction_speed;{retraction_speed}\n;Slicer info:retraction_amount;{retraction_amount}\n;Slicer info:layer_height;{layer_height}\n;Slicer info:infill_pattern;{infill_pattern}\n;Slicer info:infill_sparse_density;{infill_sparse_density}\n;Slicer info:cool_fan_enabled;{cool_fan_enabled}\n;Slicer info:cool_fan_speed;{cool_fan_speed}\n;Slicer info:sliced_at;{day} {date} {time}\nG21 ; metric values \nG90 ; absolute positioning \nM82 ; set extruder to absolute mode \nM900 K0 ; disable lin. adv. if not set in GCODE\nM107 ; start with the fan off \nM140 S{material_bed_temperature_layer_0} ; Start heating the bed \nG4 S60 ; wait 1 minute \nM104 S{material_print_temperature_layer_0} ; start heating the hot end \nM190 S{material_bed_temperature_layer_0} ; wait for bed \nM109 S{material_print_temperature_layer_0} ; wait for hotend \nM300 S1000 P500 ; BEEP heating done \nG28 X0 Y10 Z0 ; move X/Y to min endstops \nM420 S1 ; Enable leveling \nM420 Z2.0 ; Set leveling fading height to 2 mm \nG0 Z0.15 ; lift nozzle a bit \nG92 E0 ; zero the extruded length \nG1 X50 E20 F500 ; Extrude 20mm of filament in a 5cm line. \nG92 E0 ; zero the extruded length again \nG1 E-2 F500 ; Retract a little \nG1 X50 F500 ; wipe away from the filament line\nG1 X100 F9000 ; Quickly wipe away from the filament line" },
|
||||
"machine_end_gcode": { "default_value": "M104 S0 ; Extruder off \nM140 S0 ; Heatbed off \nM107 ; Fan off \nG91 ; relative positioning \nG1 E-5 F300 ; retract a little \nG1 Z+10 E-5 ; X-20 Y-20 F{travel_xy_speed} ; lift print head \nG28 X0 Y0 ; homing \nG1 Y180 F2000 ; reset feedrate \nM84 ; disable stepper motors \nG90 ; absolute positioning \nM300 S440 P200 ; Make Print Completed Tones \nM300 S660 P250 ; beep \nM300 S880 P300 ; beep" },
|
||||
|
||||
"machine_max_acceleration_x": { "value": 3000 },
|
||||
|
27
resources/definitions/dxu_umo.def.json
Normal file
27
resources/definitions/dxu_umo.def.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "UMO+ DXU",
|
||||
"inherits": "dxu",
|
||||
"overrides": {
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": [
|
||||
[[100, -102.5], [ 110, -102.5], [ 110, -62.5], [100, -62.5]]
|
||||
]
|
||||
},
|
||||
"machine_width": { "default_value": 220 },
|
||||
"machine_depth": { "default_value": 205 },
|
||||
"machine_height": { "default_value": 200 },
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 1.95
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 0.8
|
||||
},
|
||||
"machine_start_gcode" : {
|
||||
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"; Script based on an original created by tjjfvi (https://github.com/tjjfvi)\\n; An up-to-date version of the tjjfvi's original script can be found\\n; here: https://csi.t6.fyi/\\n; Note - This script will only work in Cura V4.2 and above!\\n; --- Global Settings\\n; layer_height = {layer_height}\\n; smooth_spiralized_contours = {smooth_spiralized_contours}\\n; magic_mesh_surface_mode = {magic_mesh_surface_mode}\\n; machine_extruder_count = {machine_extruder_count}\\n; --- Single Extruder Settings\\n; speed_z_hop = {speed_z_hop}\\n; retraction_amount = {retraction_amount}\\n; retraction_hop = {retraction_hop}\\n; retraction_hop_enabled = {retraction_hop_enabled}\\n; retraction_enable = {retraction_enable}\\n; retraction_speed = {retraction_speed}\\n; retraction_retract_speed = {retraction_retract_speed}\\n; retraction_prime_speed = {retraction_prime_speed}\\n; speed_travel = {speed_travel}\\n\\nM355 S1 P25;turn on case light\\n\\n;material_bed_temperature={material_bed_temperature} material_print_temperature={material_print_temperature} material_print_temperature_layer_0={material_print_temperature_layer_0}\\nM190 S{material_bed_temperature_layer_0}\\nG21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nM200 D0 T{initial_extruder_nr} ;reset filament diameter\\nG28 ;home all\\nT{initial_extruder_nr} ;switch to the first nozzle used for print\\nM104 T{initial_extruder_nr} S{material_standby_temperature, initial_extruder_nr}\\nG0 X25 Y20 F7200 ;change Y20 to Y0 ansonl\\nG0 Z20 F2400\\nM109 T{initial_extruder_nr} S{material_print_temperature_layer_0, initial_extruder_nr}\\nG0 X210 Y0 F7200\\nG92 E-12.0 ; increase purge 6.5 to 12\\nG1 E0 F200 ;purge nozzle ;change F45 to F200 like ultimaker code ansonl\\nG1 E-6.5 F1500\\nG1 E0 F1500\\nG1 Y50 F9000 ;add quick movement to Y50 like ultimaker code ansonl\\nM400 ;finish all moves\\nT{initial_extruder_nr}\\n;end of startup sequence\\n\\nM355 S1 P50;turn on case light\""
|
||||
},
|
||||
"machine_end_gcode" : {
|
||||
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \";end code from UM3\\nG91 ;Relative movement\\nG0 F15000 X8.0 Y8.0 Z3.5 E-4.5 ;Wiping+material retraction ;increase bed lower 0.5>5.0 and add Y movement\\nG0 F10000 Z1.5 E4.5 ;Compensation for the retraction\\nG90 ;Disable relative movement\\n\\nG90 ;absolute positioning\\nM104 S0 T0 ;extruder heater off\\nM104 S0 T1\\nM140 S0 ;turn off bed\\nT0 ; move to the first head\\nM107 ;fan off\\nM355 S0 ;turn off case light\""
|
||||
}
|
||||
}
|
||||
}
|
33
resources/definitions/dxu_umo_dual.def.json
Normal file
33
resources/definitions/dxu_umo_dual.def.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "UMO+ DXU Dual",
|
||||
"inherits": "dxu_dual",
|
||||
"overrides": {
|
||||
"machine_disallowed_areas": {
|
||||
"default_value": [
|
||||
[[100, -102.5], [ 110, -102.5], [ 110, -62.5], [100, -62.5]]
|
||||
]
|
||||
},
|
||||
"machine_width": { "default_value": 220 },
|
||||
"machine_depth": { "default_value": 205 },
|
||||
"machine_height": { "default_value": 200 },
|
||||
"machine_nozzle_heat_up_speed": {
|
||||
"default_value": 1.95
|
||||
},
|
||||
"machine_nozzle_cool_down_speed": {
|
||||
"default_value": 0.8
|
||||
},
|
||||
"machine_nozzle_size": {
|
||||
"default_value": 0.4
|
||||
},
|
||||
"material_diameter": {
|
||||
"default_value": 1.75
|
||||
},
|
||||
"machine_start_gcode" : {
|
||||
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"; Script based on an original created by tjjfvi (https://github.com/tjjfvi)\\n; An up-to-date version of the tjjfvi's original script can be found\\n; here: https://csi.t6.fyi/\\n; Note - This script will only work in Cura V4.2 and above!\\n; --- Global Settings\\n; layer_height = {layer_height}\\n; smooth_spiralized_contours = {smooth_spiralized_contours}\\n; magic_mesh_surface_mode = {magic_mesh_surface_mode}\\n; machine_extruder_count = {machine_extruder_count}\\n; --- Single Extruder Settings\\n; speed_z_hop = {speed_z_hop}\\n; retraction_amount = {retraction_amount}\\n; retraction_hop = {retraction_hop}\\n; retraction_hop_enabled = {retraction_hop_enabled}\\n; retraction_enable = {retraction_enable}\\n; retraction_speed = {retraction_speed}\\n; retraction_retract_speed = {retraction_retract_speed}\\n; retraction_prime_speed = {retraction_prime_speed}\\n; speed_travel = {speed_travel}\\n; --- Multi-Extruder Settings\\n; speed_z_hop_0 = {speed_z_hop, 0}\\n; speed_z_hop_1 = {speed_z_hop, 1}\\n; retraction_amount_0 = {retraction_amount, 0}\\n; retraction_amount_1 = {retraction_amount, 1}\\n; retraction_hop_0 = {retraction_hop, 0}\\n; retraction_hop_1 = {retraction_hop, 1}\\n; retraction_hop_enabled_0 = {retraction_hop_enabled, 0}\\n; retraction_hop_enabled_1 = {retraction_hop_enabled, 1}\\n; retraction_prime_speed_0 = {retraction_prime_speed, 0}\\n; retraction_prime_speed_1 = {retraction_prime_speed, 1}\\n; retraction_retract_speed_0 = {retraction_retract_speed, 0}\\n; retraction_retract_speed_1 = {retraction_retract_speed, 1}\\n; retraction_speed_0 = {retraction_speed, 0}\\n; retraction_speed_1 = {retraction_speed, 1}\\n; retraction_enable_0 = {retraction_enable, 0}\\n; retraction_enable_1 = {retraction_enable, 1}\\n; speed_travel_0 = {speed_travel, 0}\\n; speed_travel_1 = {speed_travel, 1}\\n\\nM355 S1 P25;turn on case light\\n\\n;material_bed_temperature={material_bed_temperature} material_print_temperature={material_print_temperature} material_print_temperature_layer_0={material_print_temperature_layer_0}\\nM190 S{material_bed_temperature_layer_0}\\nM104 T0 S{material_standby_temperature, 0}\\nM104 T0 S{material_print_temperature_layer_0, 0}\\nG21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nM200 D0 T0 ;reset filament diameter\\nM200 D0 T1\\nG28 ;home all\\nT1 ; move to the nozzle 2\\nG0 Z20 F2400 ;move the platform to 30mm\\nM109 T1 S{material_print_temperature_layer_0, 1}\\nG0 X210 Y0 F7200 ;change Y20 to Y0 ansonl\\nG92 E0\\nG92 E-12.0 ;prime distance ;increase purge 6.5 to 12\\nG1 E0 F200 ;purge nozzle ;change F45 to F200 like ultimaker code ansonl\\nG1 E-6.5 F1500 ; retract\\nT0 ; move to the nozzle 1\\nM104 T1 S{material_standby_temperature, 1}\\nG0 Z20 F2400\\nM109 T0 S{material_print_temperature_layer_0, 0}\\nG0 X210 Y0 F7200 ;change Y20 to Y0 ansonl\\nG92 E0\\nG92 E-12.0\\nG1 E0 F200 ;purge nozzle\\nG1 E-6.5 F1500\\nM104 T0 S{material_standby_temperature, 0}\\nT{initial_extruder_nr} ;switch to the first nozzle used for print\\nM109 T{initial_extruder_nr} S{material_print_temperature_layer_0, initial_extruder_nr}\\nM400 ;finish all moves\\nG1 E0 F1500\\nG92 E0\\nG1 Y100 F9000 ;add quick movement to Y50 like ultimaker code ansonl\\n;end of startup sequence\\n\\nM355 S1 P50;turn on case light\""
|
||||
},
|
||||
"machine_end_gcode" : {
|
||||
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \";end code from UM3\\nG91 ;Relative movement\\nG0 F15000 X8.0 Y8.0 Z3.5 E-4.5 ;Wiping+material retraction ;increase bed lower 0.5>5.0 and add Y movement\\nG0 F10000 Z1.5 E4.5 ;Compensation for the retraction\\nG90 ;Disable relative movement\\n\\nG90 ;absolute positioning\\nM104 S0 T0 ;extruder heater off\\nM104 S0 T1\\nM140 S0 ;turn off bed\\nT0 ; move to the first head\\nM107 ;fan off\\nM355 S0 ;turn off case light\""
|
||||
}
|
||||
}
|
||||
}
|
@ -4015,7 +4015,7 @@
|
||||
"maximum_value": "machine_max_feedrate_e if retraction_enable else float('inf')",
|
||||
"maximum_value_warning": "70",
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"children":
|
||||
{
|
||||
@ -4032,7 +4032,7 @@
|
||||
"maximum_value_warning": "70",
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"",
|
||||
"value": "retraction_speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_prime_speed":
|
||||
@ -4048,7 +4048,7 @@
|
||||
"maximum_value_warning": "70",
|
||||
"enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\"",
|
||||
"value": "retraction_speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
@ -4063,7 +4063,7 @@
|
||||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "5.0",
|
||||
"enabled": "retraction_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_min_travel":
|
||||
@ -4077,7 +4077,7 @@
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "line_width * 1.5",
|
||||
"maximum_value_warning": "10",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_count_max":
|
||||
@ -4232,7 +4232,7 @@
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_hop": {
|
||||
@ -4244,7 +4244,7 @@
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "retraction_enable and retraction_hop_enabled",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"retraction_hop_after_extruder_switch": {
|
||||
@ -4435,6 +4435,7 @@
|
||||
"description": "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"resolve": "any(extruderValues('support_enable'))",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -4681,6 +4682,54 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_interface_wall_count":
|
||||
{
|
||||
"label": "Support Interface Wall Line Count",
|
||||
"description": "The number of walls with which to surround support interface. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used.",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "0 if (support_skip_some_zags and support_interface_pattern == 'zigzag') else 3",
|
||||
"maximum_value": "999999",
|
||||
"type": "int",
|
||||
"value": "1 if (support_interface_pattern == 'zigzag') else 0",
|
||||
"enabled": "support_interface_enable or support_meshes_present",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"children": {
|
||||
"support_roof_wall_count": {
|
||||
"label": "Support Roof Wall Line Count",
|
||||
"description": "The number of walls with which to surround support interface roof. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used.",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "0 if (support_skip_some_zags and support_interface_pattern == 'zigzag') else 3",
|
||||
"maximum_value": "999999",
|
||||
"type": "int",
|
||||
"value": "support_interface_wall_count",
|
||||
"enabled": "support_interface_enable or support_meshes_present",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"support_bottom_wall_count": {
|
||||
"label": "Support Bottom Wall Line Count",
|
||||
"description": "The number of walls with which to surround support interface floor. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used.",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "0 if (support_skip_some_zags and support_interface_pattern == 'zigzag') else 3",
|
||||
"maximum_value": "999999",
|
||||
"type": "int",
|
||||
"value": "support_interface_wall_count",
|
||||
"enabled": "support_interface_enable or support_meshes_present",
|
||||
"limit_to_extruder": "support_interface_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"zig_zaggify_support":
|
||||
{
|
||||
"label": "Connect Support Lines",
|
||||
@ -4784,7 +4833,6 @@
|
||||
"default_value": 8.0,
|
||||
"minimum_value": "0.0",
|
||||
"maximum_value_warning": "50.0",
|
||||
"maximum_value": "0.5 * min(machine_width, machine_depth)",
|
||||
"enabled": "(support_enable or support_meshes_present) and support_brim_enable",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
@ -4799,7 +4847,6 @@
|
||||
"default_value": 20,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "50 / skirt_brim_line_width",
|
||||
"maximum_value": "0.5 * min(machine_width, machine_depth) / skirt_brim_line_width",
|
||||
"value": "math.ceil(support_brim_width / (skirt_brim_line_width * initial_layer_line_width_factor / 100.0))",
|
||||
"enabled": "(support_enable or support_meshes_present) and support_brim_enable",
|
||||
"settable_per_mesh": false,
|
||||
@ -5558,10 +5605,11 @@
|
||||
{
|
||||
"label": "Skirt/Brim Extruder",
|
||||
"description": "The extruder train to use for printing the skirt or brim. This is used in multi-extrusion.",
|
||||
"type": "extruder",
|
||||
"type": "optional_extruder",
|
||||
"default_value": "0",
|
||||
"value": "adhesion_extruder_nr",
|
||||
"enabled": "extruders_enabled_count > 1 and (resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable'))",
|
||||
"resolve": "'-1' if '-1' in extruderValues('skirt_brim_extruder_nr') else adhesion_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
@ -5613,9 +5661,9 @@
|
||||
"maximum_value_warning": "10",
|
||||
"maximum_value": "0.5 * min(machine_width, machine_depth) / skirt_brim_line_width",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'skirt'",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"skirt_gap":
|
||||
{
|
||||
@ -5627,9 +5675,9 @@
|
||||
"minimum_value_warning": "max(extruderValues('machine_nozzle_size'))",
|
||||
"maximum_value_warning": "10",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'skirt'",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"skirt_brim_minimal_length":
|
||||
{
|
||||
@ -5642,6 +5690,7 @@
|
||||
"minimum_value_warning": "25",
|
||||
"maximum_value_warning": "2500",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable')",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
@ -5654,11 +5703,10 @@
|
||||
"default_value": 8.0,
|
||||
"minimum_value": "0.0",
|
||||
"maximum_value_warning": "50.0",
|
||||
"maximum_value": "0.5 * min(machine_width, machine_depth)",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable')",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"children":
|
||||
{
|
||||
"brim_line_count":
|
||||
@ -5669,12 +5717,11 @@
|
||||
"default_value": 20,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "50 / skirt_brim_line_width",
|
||||
"maximum_value": "0.5 * min(machine_width, machine_depth) / skirt_brim_line_width",
|
||||
"value": "math.ceil(brim_width / (skirt_brim_line_width * initial_layer_line_width_factor / 100.0))",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable')",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5688,9 +5735,9 @@
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "skirt_brim_line_width",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim'",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"brim_replaces_support":
|
||||
{
|
||||
@ -5699,9 +5746,9 @@
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim' and (support_enable or support_meshes_present)",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "support_infill_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"brim_outside_only":
|
||||
{
|
||||
@ -5710,9 +5757,22 @@
|
||||
"type": "bool",
|
||||
"default_value": true,
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim'",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr"
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"brim_inside_margin":
|
||||
{
|
||||
"label": "Brim Inside Avoid Margin",
|
||||
"description": "If brim is only on outside then parts fully enclosed inside another part will get a brim which might overlap with the internal holes of the outer part. This setting controls how far to stay away from those internal holes. Set to a high value to prevent any brim from being generated for parts enclosed within the holes of other parts.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"minimum_value": "0",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'brim' and any(extruderValues('brim_outside_only'))",
|
||||
"limit_to_extruder": "skirt_brim_extruder_nr",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"raft_margin":
|
||||
{
|
||||
@ -6334,6 +6394,81 @@
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"interlocking_enable":
|
||||
{
|
||||
"label": "Generate Interlocking Structure",
|
||||
"description": "Whether to generate a structure at the interface between two models of a different material in order to improve the adhesion. The structure consists of cells of horizontal beams with alternating direction which connect to each other over the Z direction in order to interlock with the beams of the other material.",
|
||||
"type": "bool",
|
||||
"enabled": "extruders_enabled_count > 1",
|
||||
"default_value": false,
|
||||
"resolve": "(extruders_enabled_count > 1) and any(extruderValues('interlocking_enable'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"interlocking_beam_width":
|
||||
{
|
||||
"label": "Interlocking Beam Width",
|
||||
"description": "The width of the beams of this material in the interlocking structure.",
|
||||
"type": "float",
|
||||
"unit": "mm",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('interlocking_enable')",
|
||||
"default_value": 0.8,
|
||||
"value": "2 * wall_line_width_0",
|
||||
"minimum_value": "0.001",
|
||||
"maximum_value": "min(0.5 * machine_width, 0.5 * machine_depth)",
|
||||
"maximum_value_warning": "max(extruderValues('wall_line_width_0')) * 6",
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"interlocking_orientation":
|
||||
{
|
||||
"label": "Interlocking Structure Orientation",
|
||||
"description": "The direction of the beams of the interlocking structure in the XY plane as a rotation about the Z axis.",
|
||||
"unit": "°",
|
||||
"type": "float",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('interlocking_enable')",
|
||||
"default_value": 22.5,
|
||||
"resolve": "min(extruderValues('interlocking_orientation'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"interlocking_beam_layer_count":
|
||||
{
|
||||
"label": "Interlocking Beam Layer Count",
|
||||
"description": "The height of the beams of the interlocking structure as measured in number of layers. Less layers is stronger, but more prone to manufacturing defects.",
|
||||
"type": "int",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('interlocking_enable')",
|
||||
"default_value": 2,
|
||||
"minimum_value": "1",
|
||||
"resolve": "max(extruderValues('interlocking_beam_layer_count'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"interlocking_depth":
|
||||
{
|
||||
"label": "Interlocking Depth",
|
||||
"description": "The number of cells along the depth of the interface between two models where an interlocking structure is to be generated. Less cells is better, but too little cells can cause the not to be connected properly, which reduces the adhesion performance of the interlocking structure.",
|
||||
"type": "int",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('interlocking_enable')",
|
||||
"default_value": 2,
|
||||
"minimum_value": "1",
|
||||
"resolve": "max(extruderValues('interlocking_depth'))",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"interlocking_boundary_avoidance":
|
||||
{
|
||||
"label": "Interlocking Boundary Avoidance",
|
||||
"description": "The distance close to the boundary of the print where not to generate an interlocking structure as measued in number of cells times 2. If set to a value lower than the Inerlocking Depth then the interlocking structure can become visible on the outside of the print near the interfaces where two models meet.",
|
||||
"type": "int",
|
||||
"enabled": "extruders_enabled_count > 1 and resolveOrValue('interlocking_enable')",
|
||||
"default_value": 3,
|
||||
"minimum_value": "0",
|
||||
"resolve": "max(extruderValues('interlocking_boundary_avoidance'))",
|
||||
"minimum_value_warning": "resolveOrValue('interlocking_depth')",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false
|
||||
},
|
||||
"switch_extruder_retraction_amount":
|
||||
{
|
||||
"label": "Nozzle Switch Retraction Distance",
|
||||
@ -7849,7 +7984,7 @@
|
||||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "10.0",
|
||||
"enabled": "wipe_retraction_enable and clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
@ -7864,7 +7999,7 @@
|
||||
"minimum_value_warning": "-0.0001",
|
||||
"maximum_value_warning": "10.0",
|
||||
"enabled": "wipe_retraction_enable and clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"wipe_retraction_speed":
|
||||
@ -7897,7 +8032,7 @@
|
||||
"maximum_value_warning": "70",
|
||||
"enabled": "wipe_retraction_enable and clean_between_layers",
|
||||
"value": "wipe_retraction_speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"wipe_retraction_prime_speed":
|
||||
@ -7913,7 +8048,7 @@
|
||||
"maximum_value_warning": "70",
|
||||
"enabled": "wipe_retraction_enable and clean_between_layers",
|
||||
"value": "wipe_retraction_speed",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true
|
||||
}
|
||||
}
|
||||
@ -7927,7 +8062,7 @@
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
"enabled": "clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
@ -7952,7 +8087,7 @@
|
||||
"default_value": 1,
|
||||
"value": "retraction_hop",
|
||||
"enabled": "wipe_hop_enable and clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
@ -7967,7 +8102,7 @@
|
||||
"minimum_value": "0",
|
||||
"minimum_value_warning": "1",
|
||||
"enabled": "wipe_hop_enable and clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
@ -7992,7 +8127,7 @@
|
||||
"minimum_value": "0",
|
||||
"default_value": 5,
|
||||
"enabled": "clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
@ -8004,7 +8139,7 @@
|
||||
"type": "float",
|
||||
"default_value": 20,
|
||||
"enabled": "clean_between_layers",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_mesh": true,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
|
@ -9,289 +9,117 @@
|
||||
"exclude_materials": [ "generic_hips", "structur3d_dap100silicone" ]
|
||||
},
|
||||
"overrides": {
|
||||
"machine_max_feedrate_e": {
|
||||
"default_value": 45
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_travel_enabled": { "value": false },
|
||||
"bottom_layers": { "value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))" },
|
||||
"bridge_enable_more_layers": { "value": false },
|
||||
"bridge_fan_speed": { "value": "cool_fan_speed_max" },
|
||||
"bridge_fan_speed_2": { "value": "cool_fan_speed_min" },
|
||||
"bridge_fan_speed_3": { "value": "cool_fan_speed_min" },
|
||||
"bridge_settings_enabled": { "value": true },
|
||||
"bridge_skin_density": { "value": "80" },
|
||||
"bridge_skin_density_2": { "value": 100 },
|
||||
"bridge_skin_density_3": { "value": 100 },
|
||||
"bridge_skin_material_flow": { "value": "skin_material_flow" },
|
||||
"bridge_skin_material_flow_2": { "value": "skin_material_flow" },
|
||||
"bridge_skin_material_flow_3": { "value": "skin_material_flow" },
|
||||
"bridge_skin_speed": { "value": "speed_topbottom" },
|
||||
"bridge_skin_speed_2": { "value": "speed_topbottom" },
|
||||
"bridge_skin_speed_3": { "value": "speed_topbottom" },
|
||||
"bridge_skin_support_threshold": { "value": 50 },
|
||||
"bridge_sparse_infill_max_density": { "value": 0 },
|
||||
"bridge_wall_coast": { "value": 0 },
|
||||
"bridge_wall_material_flow": { "value": "wall_material_flow" },
|
||||
"bridge_wall_speed": { "value": "bridge_skin_speed" },
|
||||
"cool_fan_speed_0": { "value": "cool_fan_speed_min" },
|
||||
"gradual_support_infill_steps": { "value": "2 if support_interface_enable else 0" },
|
||||
"gradual_support_infill_step_height": { "value": "4 * layer_height" },
|
||||
"infill_material_flow": { "value": "(1.95-infill_sparse_density / 100 if infill_sparse_density > 95 else 1) * material_flow" },
|
||||
"inset_direction": { "value": "'outside_in'" },
|
||||
"jerk_infill": { "minimum_value_warning": 20 },
|
||||
"jerk_wall": { "minimum_value_warning": 20 },
|
||||
"jerk_wall_0": { "minimum_value_warning": 20 },
|
||||
"jerk_prime_tower": { "minimum_value_warning": 20 },
|
||||
"jerk_print":
|
||||
{
|
||||
"value": "20",
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"material_print_temperature": {
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"material_bed_temperature": {
|
||||
"jerk_print_layer_0": { "value": "max(20, jerk_wall_0)" },
|
||||
"jerk_roofing": { "minimum_value_warning": 20 },
|
||||
"jerk_support": { "minimum_value_warning": 20 },
|
||||
"jerk_support_infill": { "minimum_value_warning": 20 },
|
||||
"jerk_support_interface": { "minimum_value_warning": 20 },
|
||||
"jerk_topbottom": { "minimum_value_warning": 20 },
|
||||
"jerk_travel": { "value": "jerk_print" },
|
||||
"jerk_travel_enabled": { "value": false },
|
||||
"layer_height_0": { "value": "max(0.2, layer_height)" },
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
"machine_max_feedrate_e": { "default_value": 45 },
|
||||
"material_bed_temperature":
|
||||
{
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "125"
|
||||
},
|
||||
"material_bed_temperature_layer_0":
|
||||
"material_bed_temperature_layer_0": { "maximum_value_warning": "125" },
|
||||
"material_print_temperature": { "minimum_value": "0" },
|
||||
"material_standby_temperature":
|
||||
{
|
||||
"maximum_value_warning": "125"
|
||||
},
|
||||
"material_standby_temperature": {
|
||||
"value": "material_print_temperature - 100",
|
||||
"minimum_value": "0"
|
||||
},
|
||||
"extruder_prime_pos_y":
|
||||
{
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "machine_depth"
|
||||
},
|
||||
"extruder_prime_pos_x":
|
||||
{
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "machine_width"
|
||||
},
|
||||
"meshfix_maximum_deviation": { "value": "machine_nozzle_size / 10" },
|
||||
"meshfix_maximum_resolution": { "value": "max(speed_wall_0 / 75, 0.5)" },
|
||||
"minimum_support_area": { "value": "4.0" },
|
||||
"raft_base_speed": { "value": "raft_speed" },
|
||||
"raft_base_thickness": { "value": "min(machine_nozzle_size * 0.75, 0.3)" },
|
||||
"raft_interface_fan_speed": { "value": "(raft_base_fan_speed + raft_surface_fan_speed) / 2" },
|
||||
"raft_interface_line_width": { "value": "(raft_base_line_width + raft_surface_line_width) / 2" },
|
||||
"raft_interface_speed": { "value": "(raft_surface_speed + raft_base_speed) / 2" },
|
||||
"raft_interface_thickness": { "value": "(raft_base_thickness + raft_surface_thickness) / 2" },
|
||||
"raft_speed": { "value": 15 },
|
||||
"raft_surface_fan_speed": { "value": "cool_fan_speed_min" },
|
||||
"raft_surface_speed": { "value": "speed_topbottom" },
|
||||
"relative_extrusion":
|
||||
{
|
||||
"value": false,
|
||||
"enabled": false
|
||||
},
|
||||
"skin_angles": {
|
||||
"value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]"
|
||||
},
|
||||
"line_width": {
|
||||
"value": "machine_nozzle_size"
|
||||
},
|
||||
"wall_thickness": {
|
||||
"value": "wall_line_width_0 + wall_line_width_x"
|
||||
},
|
||||
"infill_material_flow": {
|
||||
"value": "(1.95-infill_sparse_density / 100 if infill_sparse_density > 95 else 1) * material_flow"
|
||||
},
|
||||
"inset_direction": {
|
||||
"value": "'outside_in'"
|
||||
},
|
||||
"retraction_combing": {
|
||||
"value": "'no_outer_surfaces'"
|
||||
},
|
||||
"retraction_count_max": {
|
||||
"value": 25
|
||||
},
|
||||
"retraction_extrusion_window": {
|
||||
"value": 1
|
||||
},
|
||||
"roofing_layer_count": {
|
||||
"value": "1"
|
||||
},
|
||||
"roofing_material_flow": {
|
||||
"value": "material_flow"
|
||||
},
|
||||
"skin_material_flow": {
|
||||
"value": "0.95 * material_flow"
|
||||
},
|
||||
"support_interface_material_flow": {
|
||||
"value": "skin_material_flow"
|
||||
},
|
||||
"skin_monotonic" : {
|
||||
"value": "roofing_layer_count == 0"
|
||||
},
|
||||
"speed_equalize_flow_width_factor": {
|
||||
"value": "110.0"
|
||||
},
|
||||
"top_layers": {
|
||||
"value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))"
|
||||
},
|
||||
"bottom_layers": {
|
||||
"value": "math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))"
|
||||
},
|
||||
"xy_offset": {
|
||||
"value": "-layer_height * 0.1"
|
||||
},
|
||||
"meshfix_maximum_resolution": {
|
||||
"value": "max(speed_wall_0 / 75, 0.5)"
|
||||
},
|
||||
"meshfix_maximum_deviation": {
|
||||
"value": "machine_nozzle_size / 10"
|
||||
},
|
||||
"jerk_travel_enabled": {
|
||||
"value": false
|
||||
},
|
||||
"acceleration_travel_enabled": {
|
||||
"value": false
|
||||
},
|
||||
"acceleration_travel": {
|
||||
"value": "acceleration_wall"
|
||||
},
|
||||
"skin_edge_support_thickness": {
|
||||
"value": "4 * layer_height if infill_sparse_density < 30 else 0"
|
||||
},
|
||||
"bridge_settings_enabled": {
|
||||
"value": true
|
||||
},
|
||||
"bridge_skin_support_threshold": {
|
||||
"value": 50
|
||||
},
|
||||
"bridge_sparse_infill_max_density": {
|
||||
"value": 0
|
||||
},
|
||||
"bridge_wall_coast": {
|
||||
"value": 0
|
||||
},
|
||||
"bridge_wall_speed": {
|
||||
"value": "bridge_skin_speed"
|
||||
},
|
||||
"bridge_wall_material_flow": {
|
||||
"value": "wall_material_flow"
|
||||
},
|
||||
"bridge_skin_speed": {
|
||||
"value": "speed_topbottom"
|
||||
},
|
||||
"bridge_skin_material_flow": {
|
||||
"value": "skin_material_flow"
|
||||
},
|
||||
"bridge_skin_density": {
|
||||
"value": "80"
|
||||
},
|
||||
"bridge_fan_speed": {
|
||||
"value": "cool_fan_speed_max"
|
||||
},
|
||||
"bridge_enable_more_layers": {
|
||||
"value": false
|
||||
},
|
||||
"bridge_skin_speed_2": {
|
||||
"value": "speed_topbottom"
|
||||
},
|
||||
"bridge_skin_material_flow_2": {
|
||||
"value": "skin_material_flow"
|
||||
},
|
||||
"bridge_skin_density_2": {
|
||||
"value": 100
|
||||
},
|
||||
"bridge_fan_speed_2": {
|
||||
"value": "cool_fan_speed_min"
|
||||
},
|
||||
"bridge_skin_speed_3": {
|
||||
"value": "speed_topbottom"
|
||||
},
|
||||
"bridge_skin_material_flow_3": {
|
||||
"value": "skin_material_flow"
|
||||
},
|
||||
"bridge_skin_density_3": {
|
||||
"value": 100
|
||||
},
|
||||
"bridge_fan_speed_3": {
|
||||
"value": "cool_fan_speed_min"
|
||||
},
|
||||
"jerk_print": {
|
||||
"value": "20",
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_infill": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_0": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_roofing": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_topbottom": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_infill": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_interface": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_prime_tower": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_layer_0": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_print_layer_0": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_travel": {
|
||||
"value": "jerk_print",
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_travel_layer_0": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_skirt_brim": {
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"support_wall_count": {
|
||||
"value": "1 if support_structure == 'tree' else 0"
|
||||
},
|
||||
"zig_zaggify_support": {
|
||||
"value": true
|
||||
},
|
||||
"support_infill_rate": {
|
||||
"value": "0 if support_structure == 'tree' else 80 if gradual_support_infill_steps != 0 else 15"
|
||||
},
|
||||
"support_line_distance": {
|
||||
"minimum_value_warning": "0 if support_structure == 'tree' else support_line_width"
|
||||
},
|
||||
"support_initial_layer_line_distance": {
|
||||
"minimum_value_warning": "0 if support_structure == 'tree' else support_line_width"
|
||||
},
|
||||
"gradual_support_infill_steps": {
|
||||
"value": "2 if support_interface_enable else 0"
|
||||
},
|
||||
"gradual_support_infill_step_height": {
|
||||
"value": "4 * layer_height"
|
||||
},
|
||||
"raft_base_speed": {
|
||||
"value": "raft_speed"
|
||||
},
|
||||
"raft_base_thickness": {
|
||||
"value": "min(machine_nozzle_size * 0.75, 0.3)"
|
||||
},
|
||||
"raft_interface_fan_speed": {
|
||||
"value": "(raft_base_fan_speed + raft_surface_fan_speed) / 2"
|
||||
},
|
||||
"raft_interface_line_width": {
|
||||
"value": "(raft_base_line_width + raft_surface_line_width) / 2"
|
||||
},
|
||||
"raft_interface_speed": {
|
||||
"value": "(raft_surface_speed + raft_base_speed) / 2"
|
||||
},
|
||||
"raft_interface_thickness": {
|
||||
"value": "(raft_base_thickness + raft_surface_thickness) / 2"
|
||||
},
|
||||
"raft_speed": {
|
||||
"value": 15
|
||||
},
|
||||
"raft_surface_fan_speed": {
|
||||
"value": "cool_fan_speed"
|
||||
},
|
||||
"raft_surface_speed": {
|
||||
"value": "speed_topbottom"
|
||||
},
|
||||
"support_interface_height": {
|
||||
"value": "2 * layer_height"
|
||||
},
|
||||
"support_offset": {
|
||||
"value": "support_xy_distance if support_interface_enable else 0"
|
||||
},
|
||||
"support_xy_distance": {
|
||||
"value": "1"
|
||||
},
|
||||
"support_xy_distance_overhang": {
|
||||
"value": "0.2"
|
||||
},
|
||||
"minimum_support_area": {
|
||||
"value": "(2 + support_offset)**2"
|
||||
},
|
||||
"support_interface_skip_height": {
|
||||
"value": "layer_height"
|
||||
},
|
||||
"support_interface_pattern": {
|
||||
"value": "'concentric'"
|
||||
},
|
||||
"support_interface_offset": {
|
||||
"value": "support_offset"
|
||||
},
|
||||
"support_use_towers": {
|
||||
"value": false
|
||||
},
|
||||
"support_z_distance": {
|
||||
"value": "0"
|
||||
}
|
||||
"retraction_combing": { "value": "'no_outer_surfaces'" },
|
||||
"retraction_combing_max_distance": { "value": 15 },
|
||||
"retraction_count_max": { "value": 25 },
|
||||
"retraction_extrusion_window": { "value": 1 },
|
||||
"roofing_layer_count": { "value": "1" },
|
||||
"roofing_material_flow": { "value": "material_flow" },
|
||||
"skin_angles": { "value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]" },
|
||||
"skin_edge_support_thickness": { "value": "4 * layer_height if infill_sparse_density < 30 else 0" },
|
||||
"skin_material_flow": { "value": "0.95 * material_flow" },
|
||||
"skin_material_flow_layer_0": { "value": "0.85 * material_flow_layer_0" },
|
||||
"skin_monotonic" : { "value": "roofing_layer_count == 0" },
|
||||
"speed_equalize_flow_width_factor": { "value": "110.0" },
|
||||
"speed_layer_0": { "value": "min(30, layer_height / layer_height_0 * speed_wall_0)" },
|
||||
"speed_slowdown_layers": { "value": 1 },
|
||||
"speed_travel_layer_0": { "value": "speed_travel" },
|
||||
"support_infill_rate": { "value": "0 if support_structure == 'tree' else 80 if gradual_support_infill_steps != 0 else 15" },
|
||||
"support_initial_layer_line_distance": { "minimum_value_warning": "0 if support_structure == 'tree' else support_line_width" },
|
||||
"support_interface_height": { "value": "2 * layer_height" },
|
||||
"support_interface_material_flow": { "value": "skin_material_flow" },
|
||||
"support_interface_offset": { "value": "support_offset" },
|
||||
"support_interface_pattern": { "value": "'concentric'" },
|
||||
"support_interface_skip_height": { "value": "layer_height" },
|
||||
"support_line_distance": { "minimum_value_warning": "0 if support_structure == 'tree' else support_line_width" },
|
||||
"support_offset": { "value": "support_xy_distance if support_interface_enable else 0" },
|
||||
"support_use_towers": { "value": false },
|
||||
"support_wall_count": { "value": "1 if support_structure == 'tree' else 0" },
|
||||
"support_xy_distance": { "value": "1" },
|
||||
"support_xy_distance_overhang": { "value": "0.2" },
|
||||
"support_z_distance": { "value": "0" },
|
||||
"top_layers": { "value": "math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))" },
|
||||
"wall_0_material_flow_layer_0": { "value": "1.10 * material_flow_layer_0" },
|
||||
"wall_thickness": { "value": "wall_line_width_0 + wall_line_width_x" },
|
||||
"wall_x_material_flow_layer_0": { "value": "0.95 * material_flow_layer_0" },
|
||||
"xy_offset": { "value": "-layer_height * 0.1" },
|
||||
"xy_offset_layer_0": { "value": "-wall_line_width_0 / 5 + xy_offset" },
|
||||
"zig_zaggify_support": { "value": true }
|
||||
}
|
||||
}
|
||||
|
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