Make Conan/Python installs available for whole project and not just the AboutDialog

Generation of dependency list now happens in
Also cleaned up the AboutDialog.qml

CURA-10561
This commit is contained in:
c.lamboo 2023-10-19 15:48:28 +02:00
parent 5f998f0ab4
commit 6f1adaad43
10 changed files with 287 additions and 369 deletions

View File

@ -155,7 +155,7 @@ jobs:
run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache"
- name: Create the Packages (Bash)
run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING --json "cura_inst/conan_install_info.json"
run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING
- name: Upload the Package(s)
if: always()
@ -206,12 +206,7 @@ jobs:
import json
from pathlib import Path
conan_install_info_path = Path("cura_inst/conan_install_info.json")
conan_info = {"installed": []}
if os.path.exists(conan_install_info_path):
with open(conan_install_info_path, "r") as f:
conan_info = json.load(f)
sorted_deps = sorted([dep["recipe"]["id"].replace('#', r' rev: ') for dep in conan_info["installed"]])
from cura.CuraVersion import ConanInstalls
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
@ -223,14 +218,17 @@ jobs:
f.write(content)
f.writelines("# ${{ steps.filename.outputs.INSTALLER_FILENAME }}\n")
f.writelines("## Conan packages:\n")
for dep in sorted_deps:
f.writelines(f"`{dep}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]} {dep_info["revision"]}`\n")
- name: Summarize the used Python modules
shell: python
run: |
import os
import pkg_resources
from cura.CuraVersion import ConanDependencies
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
if os.path.exists(summary_env):
@ -240,8 +238,8 @@ jobs:
with open(summary_env, "w") as f:
f.write(content)
f.writelines("## Python modules:\n")
for package in pkg_resources.working_set:
f.writelines(f"`{package.key}/{package.version}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]}`\n")
- name: Create the Linux AppImage (Bash)
run: |

View File

@ -155,7 +155,7 @@ jobs:
run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache"
- name: Create the Packages (Bash)
run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING --json "cura_inst/conan_install_info.json"
run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING"
- name: Upload the Package(s)
if: ${{ inputs.operating_system != 'self-hosted' }}
@ -210,12 +210,7 @@ jobs:
import json
from pathlib import Path
conan_install_info_path = Path("cura_inst/conan_install_info.json")
conan_info = {"installed": []}
if os.path.exists(conan_install_info_path):
with open(conan_install_info_path, "r") as f:
conan_info = json.load(f)
sorted_deps = sorted([dep["recipe"]["id"].replace('#', r' rev: ') for dep in conan_info["installed"]])
from cura.CuraVersion import ConanInstalls
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
@ -227,14 +222,17 @@ jobs:
f.write(content)
f.writelines("# ${{ steps.filename.outputs.INSTALLER_FILENAME }}\n")
f.writelines("## Conan packages:\n")
for dep in sorted_deps:
f.writelines(f"`{dep}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]} {dep_info["revision"]}`\n")
- name: Summarize the used Python modules
shell: python
run: |
import os
import pkg_resources
from cura.CuraVersion import PythonInstalls
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
if os.path.exists(summary_env):
@ -244,8 +242,8 @@ jobs:
with open(summary_env, "w") as f:
f.write(content)
f.writelines("## Python modules:\n")
for package in pkg_resources.working_set:
f.writelines(f"`{package.key}/{package.version}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]}`\n")
- name: Create the Macos dmg (Bash)
run: python ../cura_inst/packaging/MacOS/build_macos.py --source_path ../cura_inst --dist_path . --cura_conan_version $CURA_CONAN_VERSION --filename "${{ steps.filename.outputs.INSTALLER_FILENAME }}" --build_dmg --build_pkg --app_name "$CURA_APP_NAME"

View File

@ -122,7 +122,7 @@ jobs:
run: conan config set storage.download_cache="C:\Users\runneradmin\.conan\conan_download_cache"
- name: Create the Packages (Powershell)
run: conan install $Env:CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$Env:ENTERPRISE -o cura:staging=$Env:STAGING --json "cura_inst/conan_install_info.json"
run: conan install $Env:CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$Env:ENTERPRISE -o cura:staging=$Env:STAGING
- name: Upload the Package(s)
if: always()
@ -169,12 +169,7 @@ jobs:
import json
from pathlib import Path
conan_install_info_path = Path("cura_inst/conan_install_info.json")
conan_info = {"installed": []}
if os.path.exists(conan_install_info_path):
with open(conan_install_info_path, "r") as f:
conan_info = json.load(f)
sorted_deps = sorted([dep["recipe"]["id"].replace('#', r' rev: ') for dep in conan_info["installed"]])
from cura.CuraVersion import ConanInstalls
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
@ -186,14 +181,17 @@ jobs:
f.write(content)
f.writelines("# ${{ steps.filename.outputs.INSTALLER_FILENAME }}\n")
f.writelines("## Conan packages:\n")
for dep in sorted_deps:
f.writelines(f"`{dep}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]} {dep_info["revision"]}`\n")
- name: Summarize the used Python modules
shell: python
run: |
import os
import pkg_resources
from cura.CuraVersion import PythonInstalls
summary_env = os.environ["GITHUB_STEP_SUMMARY"]
content = ""
if os.path.exists(summary_env):
@ -203,8 +201,8 @@ jobs:
with open(summary_env, "w") as f:
f.write(content)
f.writelines("## Python modules:\n")
for package in pkg_resources.working_set:
f.writelines(f"`{package.key}/{package.version}`\n")
for dep_name, dep_info in ConanDependencies.items():
f.writelines(f"`{dep_name} {dep_info["version"]}`\n")
- name: Create PFX certificate from BASE64_PFX_CONTENT secret
id: create-pfx

1
.gitignore vendored
View File

@ -101,7 +101,6 @@ graph_info.json
Ultimaker-Cura.spec
.run/
/printer-linter/src/printerlinter.egg-info/
/resources/qml/Dialogs/AboutDialogVersionsList.qml
/plugins/CuraEngineGradualFlow
/resources/bundled_packages/bundled_*.json
curaengine_plugin_gradual_flow

View File

@ -1,61 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls 2.9
import UM 1.6 as UM
import Cura 1.5 as Cura
ListView
{
id: projectBuildInfoList
visible: false
anchors.top: creditsNotes.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
height: base.height - y - (2 * UM.Theme.getSize("default_margin").height + closeButton.height)
ScrollBar.vertical: UM.ScrollBar
{
id: projectBuildInfoListScrollBar
}
delegate: Row
{
spacing: UM.Theme.getSize("narrow_margin").width
UM.Label
{
text: (model.name)
width: (projectBuildInfoList.width* 0.4) | 0
elide: Text.ElideRight
}
UM.Label
{
text: (model.version)
width: (projectBuildInfoList.width *0.6) | 0
elide: Text.ElideRight
}
}
model: ListModel
{
id: developerInfo
}
Component.onCompleted:
{
var conan_installs = {{ conan_installs }};
var python_installs = {{ python_installs }};
developerInfo.append({ name : "<H1>Conan Installs</H1>", version : '' });
for (var n in conan_installs)
{
developerInfo.append({ name : conan_installs[n][0], version : conan_installs[n][1] });
}
developerInfo.append({ name : '', version : '' });
developerInfo.append({ name : "<H1>Python Installs</H1>", version : '' });
for (var n in python_installs)
{
developerInfo.append({ name : python_installs[n][0], version : python_installs[n][1] });
}
}
}

View File

@ -1,4 +1,4 @@
# Copyright (c) 2022 UltiMaker
# Copyright (c) 2023 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
CuraAppName = "{{ cura_app_name }}"
@ -12,3 +12,6 @@ CuraCloudAccountAPIRoot = "{{ cura_cloud_account_api_root }}"
CuraMarketplaceRoot = "{{ cura_marketplace_root }}"
CuraDigitalFactoryURL = "{{ cura_digital_factory_url }}"
CuraLatestURL = "{{ cura_latest_url }}"
ConanInstalls = {{ conan_installs }}
PythonInstalls = {{ python_installs }}

View File

@ -137,18 +137,21 @@ class CuraConan(ConanFile):
return "'x86_64'"
return "None"
def _generate_about_versions(self, location):
with open(os.path.join(self.recipe_folder, "AboutDialogVersionsList.qml.jinja"), "r") as f:
cura_version_py = Template(f.read())
def _conan_installs(self):
conan_installs = {}
conan_installs = []
python_installs = []
# list of conan installs
for dependency in self.dependencies.host.values():
conan_installs[dependency.ref.name] = {
"version": dependency.ref.version,
"revision": dependency.ref.revision
}
return conan_installs
# list of conan installs
for _, dependency in self.dependencies.host.items():
conan_installs.append([dependency.ref.name,dependency.ref.version])
def _python_installs(self):
python_installs = {}
#list of python installs
# list of python installs
outer = '"' if self.settings.os == "Windows" else "'"
inner = "'" if self.settings.os == "Windows" else '"'
python_ins_cmd = f"python -c {outer}import pkg_resources; print({inner};{inner}.join([(s.key+{inner},{inner}+ s.version) for s in pkg_resources.working_set])){outer}"
@ -157,16 +160,12 @@ class CuraConan(ConanFile):
self.run(python_ins_cmd, run_environment= True, env = "conanrun", output=buffer)
packages = str(buffer.getvalue()).split("-----------------\n")
package = packages[1].strip('\r\n').split(";")
for pack in package:
python_installs.append(pack.split(","))
with open(os.path.join(location, "AboutDialogVersionsList.qml"), "w") as f:
f.write(cura_version_py.render(
conan_installs = conan_installs,
python_installs = python_installs
))
packages = packages[1].strip('\r\n').split(";")
for package in packages:
name, version = package.split(",")
python_installs[name] = {"version": version}
return python_installs
def _generate_cura_version(self, location):
with open(os.path.join(self.recipe_folder, "CuraVersion.py.jinja"), "r") as f:
@ -192,89 +191,9 @@ class CuraConan(ConanFile):
cura_cloud_account_api_root = self.conan_data["urls"][self._urls]["cloud_account_api_root"],
cura_marketplace_root = self.conan_data["urls"][self._urls]["marketplace_root"],
cura_digital_factory_url = self.conan_data["urls"][self._urls]["digital_factory_url"],
cura_latest_url = self.conan_data["urls"][self._urls]["cura_latest_url"]))
def _generate_pyinstaller_spec(self, location, entrypoint_location, icon_path, entitlements_file):
pyinstaller_metadata = self.conan_data["pyinstaller"]
datas = [(str(self._base_dir.joinpath("conan_install_info.json")), ".")]
for data in pyinstaller_metadata["datas"].values():
if not self.options.internal and data.get("internal", False):
continue
if "package" in data: # get the paths from conan package
if data["package"] == self.name:
if self.in_local_cache:
src_path = os.path.join(self.package_folder, data["src"])
else:
src_path = os.path.join(self.source_folder, data["src"])
else:
src_path = os.path.join(self.deps_cpp_info[data["package"]].rootpath, data["src"])
elif "root" in data: # get the paths relative from the install folder
src_path = os.path.join(self.install_folder, data["root"], data["src"])
else:
continue
if Path(src_path).exists():
datas.append((str(src_path), data["dst"]))
binaries = []
for binary in pyinstaller_metadata["binaries"].values():
if "package" in binary: # get the paths from conan package
src_path = os.path.join(self.deps_cpp_info[binary["package"]].rootpath, binary["src"])
elif "root" in binary: # get the paths relative from the sourcefolder
src_path = str(self.source_path.joinpath(binary["root"], binary["src"]))
if self.settings.os == "Windows":
src_path = src_path.replace("\\", "\\\\")
else:
continue
if not Path(src_path).exists():
self.output.warning(f"Source path for binary {binary['binary']} does not exist")
continue
for bin in Path(src_path).glob(binary["binary"] + "*[.exe|.dll|.so|.dylib|.so.]*"):
binaries.append((str(bin), binary["dst"]))
for bin in Path(src_path).glob(binary["binary"]):
binaries.append((str(bin), binary["dst"]))
# Make sure all Conan dependencies which are shared are added to the binary list for pyinstaller
for _, dependency in self.dependencies.host.items():
for bin_paths in dependency.cpp_info.bindirs:
binaries.extend([(f"{p}", ".") for p in Path(bin_paths).glob("**/*.dll")])
for lib_paths in dependency.cpp_info.libdirs:
binaries.extend([(f"{p}", ".") for p in Path(lib_paths).glob("**/*.so*")])
binaries.extend([(f"{p}", ".") for p in Path(lib_paths).glob("**/*.dylib*")])
# Copy dynamic libs from lib path
binaries.extend([(f"{p}", ".") for p in Path(self._base_dir.joinpath("lib")).glob("**/*.dylib*")])
binaries.extend([(f"{p}", ".") for p in Path(self._base_dir.joinpath("lib")).glob("**/*.so*")])
# 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(os.path.join(self.recipe_folder, "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(os.path.join(location, "UltiMaker-Cura.spec"), "w") as f:
f.write(pyinstaller.render(
name = str(self.options.display_name).replace(" ", "-"),
display_name = self._app_name,
entrypoint = entrypoint_location,
datas = datas,
binaries = binaries,
venv_script_path = str(self._script_dir),
hiddenimports = pyinstaller_metadata["hiddenimports"],
collect_all = pyinstaller_metadata["collect_all"],
icon = icon_path,
entitlements_file = entitlements_file,
osx_bundle_identifier = "'nl.ultimaker.cura'" if self.settings.os == "Macos" else "None",
upx = str(self.settings.os == "Windows"),
strip = False, # This should be possible on Linux and MacOS but, it can also cause issues on some distributions. Safest is to disable it for now
target_arch = self._pyinstaller_spec_arch,
macos = self.settings.os == "Macos",
version = f"'{version}'",
short_version = f"'{cura_version.major}.{cura_version.minor}.{cura_version.patch}'",
cura_latest_url=self.conan_data["urls"][self._urls]["cura_latest_url"],
conan_installs=self._conan_installs(),
python_installs=self._python_installs(),
))
def export_sources(self):
@ -346,7 +265,6 @@ class CuraConan(ConanFile):
vr.generate()
self._generate_cura_version(os.path.join(self.source_folder, "cura"))
self._generate_about_versions(os.path.join(self.source_folder, "resources","qml", "Dialogs"))
if not self.in_local_cache:
# Copy CuraEngine.exe to bindirs of Virtual Python Environment
@ -387,12 +305,6 @@ class CuraConan(ConanFile):
copy(self, "*", cura_private_data.resdirs[0], str(self._share_dir.joinpath("cura")))
if self.options.devtools:
entitlements_file = "'{}'".format(os.path.join(self.source_folder, "packaging", "MacOS", "cura.entitlements"))
self._generate_pyinstaller_spec(location = self.generators_folder,
entrypoint_location = "'{}'".format(os.path.join(self.source_folder, self.conan_data["pyinstaller"]["runinfo"]["entrypoint"])).replace("\\", "\\\\"),
icon_path = "'{}'".format(os.path.join(self.source_folder, "packaging", self.conan_data["pyinstaller"]["icon"][str(self.settings.os)])).replace("\\", "\\\\"),
entitlements_file = entitlements_file if self.settings.os == "Macos" else "None")
# Update the po and pot files
if self.settings.os != "Windows" or self.conf.get("tools.microsoft.bash:path", check_type=str):
vb = VirtualBuildEnv(self)
@ -451,13 +363,6 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
save(self, os.path.join(self._script_dir, f"activate_github_actions_version_env{ext}"), activate_github_actions_version_env)
self._generate_cura_version(os.path.join(self._site_packages, "cura"))
self._generate_about_versions(str(self._share_dir.joinpath("cura", "resources", "qml", "Dialogs")))
entitlements_file = "'{}'".format(Path(self.cpp_info.res_paths[2], "MacOS", "cura.entitlements"))
self._generate_pyinstaller_spec(location = self._base_dir,
entrypoint_location = "'{}'".format(os.path.join(self.package_folder, self.cpp_info.bindirs[0], self.conan_data["pyinstaller"]["runinfo"]["entrypoint"])).replace("\\", "\\\\"),
icon_path = "'{}'".format(os.path.join(self.package_folder, self.cpp_info.resdirs[2], self.conan_data["pyinstaller"]["icon"][str(self.settings.os)])).replace("\\", "\\\\"),
entitlements_file = entitlements_file if self.settings.os == "Macos" else "None")
def package(self):
copy(self, "cura_app.py", src = self.source_folder, dst = os.path.join(self.package_folder, self.cpp.package.bindirs[0]))

View File

@ -1,4 +1,4 @@
# Copyright (c) 2022 UltiMaker
# Copyright (c) 2023 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
# ---------
@ -69,13 +69,25 @@ try:
except ImportError:
CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME
DEPENDENCY_INFO = {}
try:
from pathlib import Path
conan_install_info = Path(__file__).parent.parent.joinpath("conan_install_info.json")
if conan_install_info.exists():
import json
with open(conan_install_info, "r") as f:
DEPENDENCY_INFO = json.loads(f.read())
except:
pass
from cura.CuraVersion import ConanInstalls
if type(ConanInstalls) == dict:
CONAN_INSTALLS = ConanInstalls
else:
CONAN_INSTALLS = {}
except ImportError:
CONAN_INSTALLS = {}
try:
from cura.CuraVersion import PythonInstalls
if type(PythonInstalls) == dict:
PYTHON_INSTALLS = PythonInstalls
else:
PYTHON_INSTALLS = {}
except ImportError:
PYTHON_INSTALLS = {}

View File

@ -269,6 +269,9 @@ class CuraApplication(QtApplication):
CentralFileStorage.setIsEnterprise(ApplicationMetadata.IsEnterpriseVersion)
Resources.setIsEnterprise(ApplicationMetadata.IsEnterpriseVersion)
self._conan_installs = ApplicationMetadata.CONAN_INSTALLS
self._python_installs = ApplicationMetadata.PYTHON_INSTALLS
@pyqtProperty(str, constant=True)
def ultimakerCloudApiRootUrl(self) -> str:
return UltimakerCloudConstants.CuraCloudAPIRoot
@ -851,11 +854,8 @@ class CuraApplication(QtApplication):
self._log_hardware_info()
if len(ApplicationMetadata.DEPENDENCY_INFO) > 0:
Logger.debug("Using Conan managed dependencies: " + ", ".join(
[dep["recipe"]["id"] for dep in ApplicationMetadata.DEPENDENCY_INFO["installed"] if dep["recipe"]["version"] != "latest"]))
else:
Logger.warning("Could not find conan_install_info.json")
Logger.debug("Using conan dependencies: {}", str(self.conanInstalls))
Logger.debug("Using python dependencies: {}", str(self.pythonInstalls))
Logger.log("i", "Initializing machine error checker")
self._machine_error_checker = MachineErrorChecker(self)
@ -2130,3 +2130,11 @@ class CuraApplication(QtApplication):
@pyqtProperty(bool, constant=True)
def isEnterprise(self) -> bool:
return ApplicationMetadata.IsEnterpriseVersion
@pyqtProperty("QVariant", constant=True)
def conanInstalls(self) -> Dict[str, Dict[str, str]]:
return self._conan_installs
@pyqtProperty("QVariant", constant=True)
def pythonInstalls(self) -> Dict[str, Dict[str, str]]:
return self._python_installs

View File

@ -1,19 +1,22 @@
// Copyright (c) 2022 UltiMaker
// Copyright (c) 2023 UltiMaker
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.4
import QtQuick.Controls 2.9
import QtQuick.Layouts 1.3
import UM 1.6 as UM
import Cura 1.5 as Cura
import Cura 1.6 as Cura
UM.Dialog
{
id: base
//: About dialog title
title: catalog.i18nc("@title:window The argument is the application name.", "About %1").arg(CuraApplication.applicationDisplayName)
// Flag to toggle between main dependencies information and extensive dependencies information
property bool showDefaultDependencies: true
minimumWidth: 500 * screenScaleFactor
minimumHeight: 700 * screenScaleFactor
width: minimumWidth
@ -21,186 +24,241 @@ UM.Dialog
backgroundColor: UM.Theme.getColor("main_background")
Rectangle
headerComponent: Rectangle
{
id: header
width: parent.width + 2 * margin // margin from Dialog.qml
height: childrenRect.height + topPadding
anchors.top: parent.top
anchors.topMargin: -margin
anchors.horizontalCenter: parent.horizontalCenter
property real topPadding: UM.Theme.getSize("wide_margin").height
width: parent.width
height: logo.height + 2 * UM.Theme.getSize("wide_margin").height
color: UM.Theme.getColor("main_window_header_background")
Image
{
id: logo
width: (base.minimumWidth * 0.85) | 0
height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0
width: Math.floor(base.width * 0.85)
height: Math.floor(width * UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)
source: UM.Theme.getImage("logo")
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
anchors.top: parent.top
anchors.topMargin: parent.topPadding
anchors.horizontalCenter: parent.horizontalCenter
anchors.centerIn: parent
UM.I18nCatalog{id: catalog; name: "cura"}
MouseArea
{
anchors.fill: parent
onClicked:
{
projectsList.visible = !projectsList.visible;
projectBuildInfoList.visible = !projectBuildInfoList.visible;
}
}
UM.I18nCatalog{ id: catalog; name: "cura" }
}
UM.Label
{
id: version
text: catalog.i18nc("@label","version: %1").arg(UM.Application.version)
font: UM.Theme.getFont("large_bold")
color: UM.Theme.getColor("button_text")
anchors.right : logo.right
anchors.top: logo.bottom
anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0
}
MouseArea
{
anchors.fill: parent
onDoubleClicked: showDefaultDependencies = !showDefaultDependencies
}
}
UM.Label
// Reusable component to display a dependency
readonly property Component dependency_row: RowLayout
{
id: description
width: parent.width
spacing: UM.Theme.getSize("narrow_margin").width
//: About dialog application description
text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: header.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
UM.Label
{
id: creditsNotes
width: parent.width
//: About dialog application author note
text: catalog.i18nc("@info:credit","Cura is developed by UltiMaker in cooperation with the community.\nCura proudly uses the following open source projects:")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
anchors.top: description.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
}
ListView
{
id: projectsList
anchors.top: creditsNotes.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
height: base.height - y - (2 * UM.Theme.getSize("default_margin").height + closeButton.height)
ScrollBar.vertical: UM.ScrollBar
UM.Label
{
id: projectsListScrollBar
text: {
if (typeof(url) !== "undefined" && url !== "") {
return "<a href=\"" + url + "\">" + name + "</a>";
} else {
return name;
}
}
visible: text !== ""
Layout.fillWidth: true
Layout.preferredWidth: 2
elide: Text.ElideRight
}
delegate: Row
UM.Label
{
text: description
visible: text !== ""
Layout.fillWidth: true
Layout.preferredWidth: 3
elide: Text.ElideRight
}
UM.Label
{
text: license
visible: text !== ""
Layout.fillWidth: true
Layout.preferredWidth: 2
elide: Text.ElideRight
}
UM.Label
{
text: version
visible: text !== ""
Layout.fillWidth: true
Layout.preferredWidth: 2
elide: Text.ElideRight
}
}
Flickable
{
anchors.fill: parent
ScrollBar.vertical: UM.ScrollBar {
visible: contentHeight > height
}
contentHeight: content.height
clip: true
Column
{
id: content
spacing: UM.Theme.getSize("narrow_margin").width
width: parent.width
UM.Label
{
text: "<a href='%1' title='%2'>%2</a>".arg(model.url).arg(model.name)
width: (projectsList.width * 0.25) | 0
elide: Text.ElideRight
onLinkActivated: Qt.openUrlExternally(link)
text: catalog.i18nc("@label", "End-to-end solution for fused filament 3D printing.")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
}
UM.Label
{
text: model.description
elide: Text.ElideRight
width: ((projectsList.width * 0.6) | 0) - parent.spacing * 2 - projectsListScrollBar.width
text: catalog.i18nc("@info:credit", "Cura is developed by UltiMaker in cooperation with the community.\nCura proudly uses the following open source projects:")
font: UM.Theme.getFont("system")
wrapMode: Text.WordWrap
}
Column
{
visible: showDefaultDependencies
width: parent.width
Repeater
{
width: parent.width
delegate: Loader {
sourceComponent: dependency_row
width: parent.width
property string name: model.name
property string description: model.description
property string license: model.license
property string url: model.url
}
model: ListModel
{
id: projectsModel
}
Component.onCompleted:
{
//Do NOT add dependencies of our dependencies here, nor CI-dependencies!
//UltiMaker's own projects and forks.
projectsModel.append({ name: "Cura", description: catalog.i18nc("@label Description for application component", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" });
projectsModel.append({ name: "Uranium", description: catalog.i18nc("@label Description for application component", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" });
projectsModel.append({ name: "CuraEngine", description: catalog.i18nc("@label Description for application component", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" });
projectsModel.append({ name: "libArcus", description: catalog.i18nc("@label Description for application component", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" });
projectsModel.append({ name: "pynest2d", description: catalog.i18nc("@label Description for application component", "Python bindings for libnest2d"), license: "LGPL", url: "https://github.com/Ultimaker/pynest2d" });
projectsModel.append({ name: "libnest2d", description: catalog.i18nc("@label Description for application component", "Polygon packing library, developed by Prusa Research"), license: "LGPL", url: "https://github.com/tamasmeszaros/libnest2d" });
projectsModel.append({ name: "libSavitar", description: catalog.i18nc("@label Description for application component", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" });
projectsModel.append({ name: "libCharon", description: catalog.i18nc("@label Description for application component", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" });
//Direct dependencies of the front-end.
projectsModel.append({ name: "Python", description: catalog.i18nc("@label Description for application dependency", "Programming language"), license: "Python", url: "http://python.org/" });
projectsModel.append({ name: "Qt6", description: catalog.i18nc("@label Description for application dependency", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" });
projectsModel.append({ name: "PyQt", description: catalog.i18nc("@label Description for application dependency", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" });
projectsModel.append({ name: "SIP", description: catalog.i18nc("@label Description for application dependency", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" });
projectsModel.append({ name: "Protobuf", description: catalog.i18nc("@label Description for application dependency", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" });
projectsModel.append({ name: "Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" });
//CuraEngine's dependencies.
projectsModel.append({ name: "Clipper", description: catalog.i18nc("@label Description for application dependency", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" });
projectsModel.append({ name: "RapidJSON", description: catalog.i18nc("@label Description for application dependency", "JSON parser"), license: "MIT", url: "https://rapidjson.org/" });
projectsModel.append({ name: "STB", description: catalog.i18nc("@label Description for application dependency", "Utility functions, including an image loader"), license: "Public Domain", url: "https://github.com/nothings/stb" });
projectsModel.append({ name: "Boost", description: catalog.i18nc("@label Description for application dependency", "Utility library, including Voronoi generation"), license: "Boost", url: "https://www.boost.org/" });
//Python modules.
projectsModel.append({ name: "Certifi", description: catalog.i18nc("@label Description for application dependency", "Root Certificates for validating SSL trustworthiness"), license: "MPL", url: "https://github.com/certifi/python-certifi" });
projectsModel.append({ name: "Cryptography", description: catalog.i18nc("@label Description for application dependency", "Root Certificates for validating SSL trustworthiness"), license: "APACHE and BSD", url: "https://cryptography.io/" });
projectsModel.append({ name: "Future", description: catalog.i18nc("@label Description for application dependency", "Compatibility between Python 2 and 3"), license: "MIT", url: "https://python-future.org/" });
projectsModel.append({ name: "keyring", description: catalog.i18nc("@label Description for application dependency", "Support library for system keyring access"), license: "MIT", url: "https://github.com/jaraco/keyring" });
projectsModel.append({ name: "NumPy", description: catalog.i18nc("@label Description for application dependency", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" });
projectsModel.append({ name: "NumPy-STL", description: catalog.i18nc("@label Description for application dependency", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" });
projectsModel.append({ name: "PyClipper", description: catalog.i18nc("@label Description for application dependency", "Python bindings for Clipper"), license: "MIT", url: "https://github.com/fonttools/pyclipper" });
projectsModel.append({ name: "PySerial", description: catalog.i18nc("@label Description for application dependency", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" });
projectsModel.append({ name: "SciPy", description: catalog.i18nc("@label Description for application dependency", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" });
projectsModel.append({ name: "Sentry", description: catalog.i18nc("@Label Description for application dependency", "Python Error tracking library"), license: "BSD 2-Clause 'Simplified'", url: "https://sentry.io/for/python/" });
projectsModel.append({ name: "Trimesh", description: catalog.i18nc("@label Description for application dependency", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" });
projectsModel.append({ name: "python-zeroconf", description: catalog.i18nc("@label Description for application dependency", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" });
//Building/packaging.
projectsModel.append({ name: "CMake", description: catalog.i18nc("@label Description for development tool", "Universal build system configuration"), license: "BSD 3-Clause", url: "https://cmake.org/" });
projectsModel.append({ name: "Conan", description: catalog.i18nc("@label Description for development tool", "Dependency and package manager"), license: "MIT", url: "https://conan.io/" });
projectsModel.append({ name: "Pyinstaller", description: catalog.i18nc("@label Description for development tool", "Packaging Python-applications"), license: "GPLv2", url: "https://pyinstaller.org/" });
projectsModel.append({ name: "AppImageKit", description: catalog.i18nc("@label Description for development tool", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" });
projectsModel.append({ name: "NSIS", description: catalog.i18nc("@label Description for development tool", "Generating Windows installers"), license: "Zlib", url: "https://nsis.sourceforge.io/" });
}
}
}
UM.Label
{
text: model.license
elide: Text.ElideRight
width: (projectsList.width * 0.15) | 0
visible: !showDefaultDependencies
text: "Conan Installs"
font: UM.Theme.getFont("large_bold")
}
Column
{
visible: !showDefaultDependencies
width: parent.width
Repeater
{
width: parent.width
model: Object.entries(CuraApplication.conanInstalls).map(function (item) { return { name: item[0], version: item[1].version } })
delegate: Loader {
sourceComponent: dependency_row
width: parent.width
property string name: modelData.name
property string version: modelData.version
}
}
}
UM.Label
{
visible: !showDefaultDependencies
text: "Python Installs"
font: UM.Theme.getFont("large_bold")
}
Column
{
width: parent.width
visible: !showDefaultDependencies
Repeater
{
delegate: Loader {
sourceComponent: dependency_row
width: parent.width
property string name: modelData.name
property string version: modelData.version
}
width: parent.width
model: Object.entries(CuraApplication.pythonInstalls).map(function (item) { return { name: item[0], version: item[1].version } })
}
}
}
model: ListModel
{
id: projectsModel
}
Component.onCompleted:
{
//Do NOT add dependencies of our dependencies here, nor CI-dependencies!
//UltiMaker's own projects and forks.
projectsModel.append({ name: "Cura", description: catalog.i18nc("@label Description for application component", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" });
projectsModel.append({ name: "Uranium", description: catalog.i18nc("@label Description for application component", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" });
projectsModel.append({ name: "CuraEngine", description: catalog.i18nc("@label Description for application component", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" });
projectsModel.append({ name: "libArcus", description: catalog.i18nc("@label Description for application component", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" });
projectsModel.append({ name: "pynest2d", description: catalog.i18nc("@label Description for application component", "Python bindings for libnest2d"), license: "LGPL", url: "https://github.com/Ultimaker/pynest2d" });
projectsModel.append({ name: "libnest2d", description: catalog.i18nc("@label Description for application component", "Polygon packing library, developed by Prusa Research"), license: "LGPL", url: "https://github.com/tamasmeszaros/libnest2d" });
projectsModel.append({ name: "libSavitar", description: catalog.i18nc("@label Description for application component", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" });
projectsModel.append({ name: "libCharon", description: catalog.i18nc("@label Description for application component", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" });
//Direct dependencies of the front-end.
projectsModel.append({ name: "Python", description: catalog.i18nc("@label Description for application dependency", "Programming language"), license: "Python", url: "http://python.org/" });
projectsModel.append({ name: "Qt6", description: catalog.i18nc("@label Description for application dependency", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" });
projectsModel.append({ name: "PyQt", description: catalog.i18nc("@label Description for application dependency", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" });
projectsModel.append({ name: "SIP", description: catalog.i18nc("@label Description for application dependency", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" });
projectsModel.append({ name: "Protobuf", description: catalog.i18nc("@label Description for application dependency", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" });
projectsModel.append({ name: "Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" });
//CuraEngine's dependencies.
projectsModel.append({ name: "Clipper", description: catalog.i18nc("@label Description for application dependency", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" });
projectsModel.append({ name: "RapidJSON", description: catalog.i18nc("@label Description for application dependency", "JSON parser"), license: "MIT", url: "https://rapidjson.org/" });
projectsModel.append({ name: "STB", description: catalog.i18nc("@label Description for application dependency", "Utility functions, including an image loader"), license: "Public Domain", url: "https://github.com/nothings/stb" });
projectsModel.append({ name: "Boost", description: catalog.i18nc("@label Description for application dependency", "Utility library, including Voronoi generation"), license: "Boost", url: "https://www.boost.org/" });
//Python modules.
projectsModel.append({ name: "Certifi", description: catalog.i18nc("@label Description for application dependency", "Root Certificates for validating SSL trustworthiness"), license: "MPL", url: "https://github.com/certifi/python-certifi" });
projectsModel.append({ name: "Cryptography", description: catalog.i18nc("@label Description for application dependency", "Root Certificates for validating SSL trustworthiness"), license: "APACHE and BSD", url: "https://cryptography.io/" });
projectsModel.append({ name: "Future", description: catalog.i18nc("@label Description for application dependency", "Compatibility between Python 2 and 3"), license: "MIT", url: "https://python-future.org/" });
projectsModel.append({ name: "keyring", description: catalog.i18nc("@label Description for application dependency", "Support library for system keyring access"), license: "MIT", url: "https://github.com/jaraco/keyring" });
projectsModel.append({ name: "NumPy", description: catalog.i18nc("@label Description for application dependency", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" });
projectsModel.append({ name: "NumPy-STL", description: catalog.i18nc("@label Description for application dependency", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" });
projectsModel.append({ name: "PyClipper", description: catalog.i18nc("@label Description for application dependency", "Python bindings for Clipper"), license: "MIT", url: "https://github.com/fonttools/pyclipper" });
projectsModel.append({ name: "PySerial", description: catalog.i18nc("@label Description for application dependency", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" });
projectsModel.append({ name: "SciPy", description: catalog.i18nc("@label Description for application dependency", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" });
projectsModel.append({ name: "Sentry", description: catalog.i18nc("@Label Description for application dependency", "Python Error tracking library"), license: "BSD 2-Clause 'Simplified'", url: "https://sentry.io/for/python/" });
projectsModel.append({ name: "Trimesh", description: catalog.i18nc("@label Description for application dependency", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" });
projectsModel.append({ name: "python-zeroconf", description: catalog.i18nc("@label Description for application dependency", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" });
//Building/packaging.
projectsModel.append({ name: "CMake", description: catalog.i18nc("@label Description for development tool", "Universal build system configuration"), license: "BSD 3-Clause", url: "https://cmake.org/" });
projectsModel.append({ name: "Conan", description: catalog.i18nc("@label Description for development tool", "Dependency and package manager"), license: "MIT", url: "https://conan.io/" });
projectsModel.append({ name: "Pyinstaller", description: catalog.i18nc("@label Description for development tool", "Packaging Python-applications"), license: "GPLv2", url: "https://pyinstaller.org/" });
projectsModel.append({ name: "AppImageKit", description: catalog.i18nc("@label Description for development tool", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" });
projectsModel.append({ name: "NSIS", description: catalog.i18nc("@label Description for development tool", "Generating Windows installers"), license: "Zlib", url: "https://nsis.sourceforge.io/" });
}
}
AboutDialogVersionsList{
id: projectBuildInfoList
}
onVisibleChanged:
{
projectsList.visible = true;
projectBuildInfoList.visible = false;
}
rightButtons: Cura.TertiaryButton