diff --git a/CMakeLists.txt b/CMakeLists.txt index 7846e219e3..f61737fbcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,8 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + project(cura) -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.18) include(GNUInstallDirs) @@ -8,9 +11,6 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) set(URANIUM_DIR "${CMAKE_SOURCE_DIR}/../Uranium" CACHE PATH "The location of the Uranium repository") set(URANIUM_SCRIPTS_DIR "${URANIUM_DIR}/scripts" CACHE PATH "The location of the scripts directory of the Uranium repository") -# Tests -include(CuraTests) - option(CURA_DEBUGMODE "Enable debug dialog and other debug features" OFF) if(CURA_DEBUGMODE) set(_cura_debugmode "ON") @@ -32,17 +32,25 @@ configure_file(${CMAKE_SOURCE_DIR}/com.ultimaker.cura.desktop.in ${CMAKE_BINARY_ configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) +if(NOT DEFINED Python_VERSION) + set(Python_VERSION + 3.10 + CACHE STRING "Python Version" FORCE) + message(STATUS "Setting Python version to ${Python_VERSION}. Set Python_VERSION if you want to compile against an other version.") +endif() +if(APPLE) + set(Python_FIND_FRAMEWORK NEVER) +endif() +find_package(Python ${Python_VERSION} EXACT REQUIRED COMPONENTS Interpreter) +message(STATUS "Linking and building ${project_name} against Python ${Python_VERSION}") +if(NOT DEFINED Python_SITELIB_LOCAL) + set(Python_SITELIB_LOCAL + "${Python_SITELIB}" + CACHE PATH "Local alternative site-package location to install Cura" FORCE) +endif() -# FIXME: The new FindPython3 finds the system's Python3.6 rather than the Python3.5 that we built for Cura's environment. -# So we're using the old method here, with FindPythonInterp for now. -find_package(PythonInterp 3 REQUIRED) - -set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) - -set(Python3_VERSION ${PYTHON_VERSION_STRING}) -set(Python3_VERSION_MAJOR ${PYTHON_VERSION_MAJOR}) -set(Python3_VERSION_MINOR ${PYTHON_VERSION_MINOR}) -set(Python3_VERSION_PATCH ${PYTHON_VERSION_PATCH}) +# Tests +include(CuraTests) if(NOT ${URANIUM_DIR} STREQUAL "") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${URANIUM_DIR}/cmake") @@ -58,30 +66,15 @@ if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "") endif() endif() - -install(DIRECTORY resources - DESTINATION ${CMAKE_INSTALL_DATADIR}/cura) +install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura) include(CuraPluginInstall) +install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(DIRECTORY cura DESTINATION "${Python_SITELIB_LOCAL}") +install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION "${Python_SITELIB_LOCAL}/cura/") if(NOT APPLE AND NOT WIN32) - install(FILES cura_app.py - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE - RENAME cura) - if(EXISTS /etc/debian_version) - install(DIRECTORY cura - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}/dist-packages - FILES_MATCHING PATTERN *.py) - install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}/dist-packages/cura) - else() - install(DIRECTORY cura - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages - FILES_MATCHING PATTERN *.py) - install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/cura) - endif() install(FILES ${CMAKE_BINARY_DIR}/com.ultimaker.cura.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install(FILES ${CMAKE_SOURCE_DIR}/resources/images/cura-icon.png @@ -91,13 +84,4 @@ if(NOT APPLE AND NOT WIN32) install(FILES cura.sharedmimeinfo DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/ RENAME cura.xml ) -else() - install(FILES cura_app.py - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install(DIRECTORY cura - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages - FILES_MATCHING PATTERN *.py) - install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py - DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/cura) endif() diff --git a/cmake/CuraPluginInstall.cmake b/cmake/CuraPluginInstall.cmake index 8d9efe1f12..79b86ebdc6 100644 --- a/cmake/CuraPluginInstall.cmake +++ b/cmake/CuraPluginInstall.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # CuraPluginInstall.cmake is released under the terms of the LGPLv3 or higher. # @@ -11,19 +11,6 @@ option(PRINT_PLUGIN_LIST "Should the list of plugins that are installed be printed?" ON) -# FIXME: Remove the code for CMake <3.12 once we have switched over completely. -# FindPython3 is a new module since CMake 3.12. It deprecates FindPythonInterp and FindPythonLibs. The FindPython3 -# module is copied from the CMake repository here so in CMake <3.12 we can still use it. -if(${CMAKE_VERSION} VERSION_LESS 3.12) - # Use FindPythonInterp and FindPythonLibs for CMake <3.12 - find_package(PythonInterp 3 REQUIRED) - - set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) -else() - # Use FindPython3 for CMake >=3.12 - find_package(Python3 REQUIRED COMPONENTS Interpreter) -endif() - # Options or configuration variables set(CURA_NO_INSTALL_PLUGINS "" CACHE STRING "A list of plugins that should not be installed, separated with ';' or ','.") @@ -97,7 +84,7 @@ foreach(_plugin_json_path ${_plugin_json_list}) if(${PRINT_PLUGIN_LIST}) message(STATUS "[-] PLUGIN TO REMOVE : ${_rel_plugin_dir}") endif() - execute_process(COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_bundled_packages_json.py + execute_process(COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_bundled_packages_json.py -d ${CMAKE_CURRENT_SOURCE_DIR}/resources/bundled_packages ${_plugin_dir_name} RESULT_VARIABLE _mod_json_result) diff --git a/cmake/CuraTests.cmake b/cmake/CuraTests.cmake index b8127a70ec..09e8dadbab 100644 --- a/cmake/CuraTests.cmake +++ b/cmake/CuraTests.cmake @@ -1,15 +1,9 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. include(CTest) include(CMakeParseArguments) -# FIXME: The new FindPython3 finds the system's Python3.6 rather than the Python3.5 that we built for Cura's environment. -# So we're using the old method here, with FindPythonInterp for now. -find_package(PythonInterp 3 REQUIRED) - -set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) - add_custom_target(test-verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose) function(cura_add_test) @@ -40,7 +34,7 @@ function(cura_add_test) if (NOT ${test_exists}) add_test( NAME ${_NAME} - COMMAND ${Python3_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY} + COMMAND ${Python_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY} ) set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C) set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}") @@ -53,14 +47,14 @@ endfunction() #Add code style test. add_test( NAME "code-style" - COMMAND ${Python3_EXECUTABLE} run_mypy.py + COMMAND ${Python_EXECUTABLE} run_mypy.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) #Add test for import statements which are not compatible with all builds add_test( NAME "invalid-imports" - COMMAND ${Python3_EXECUTABLE} scripts/check_invalid_imports.py + COMMAND ${Python_EXECUTABLE} scripts/check_invalid_imports.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) @@ -78,6 +72,6 @@ endforeach() #Add test for whether the shortcut alt-keys are unique in every translation. add_test( NAME "shortcut-keys" - COMMAND ${Python3_EXECUTABLE} scripts/check_shortcut_keys.py + COMMAND ${Python_EXECUTABLE} scripts/check_shortcut_keys.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) diff --git a/cura/API/Account.py b/cura/API/Account.py index 9f1184a0a0..b63983e0cc 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -1,8 +1,8 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import enum from datetime import datetime -from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QTimer, Q_ENUMS +from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QTimer, pyqtEnum from typing import Any, Optional, Dict, TYPE_CHECKING, Callable from UM.Logger import Logger @@ -18,7 +18,7 @@ if TYPE_CHECKING: i18n_catalog = i18nCatalog("cura") -class SyncState: +class SyncState(enum.IntEnum): """QML: Cura.AccountSyncState""" SYNCING = 0 SUCCESS = 1 @@ -41,7 +41,7 @@ class Account(QObject): # The interval in which sync services are automatically triggered SYNC_INTERVAL = 60.0 # seconds - Q_ENUMS(SyncState) + pyqtEnum(SyncState) loginStateChanged = pyqtSignal(bool) """Signal emitted when user logged in or out""" @@ -269,10 +269,10 @@ class Account(QObject): return self._authorization_service.getAccessToken() @pyqtProperty("QVariantMap", notify = userProfileChanged) - def userProfile(self) -> Optional[Dict[str, Optional[str]]]: + def userProfile(self) -> Dict[str, Optional[str]]: """None if no user is logged in otherwise the logged in user as a dict containing containing user_id, username and profile_image_url """ if not self._user_profile: - return None + return {} return self._user_profile.__dict__ @pyqtProperty(str, notify=lastSyncDateTimeChanged) diff --git a/cura/API/ConnectionStatus.py b/cura/API/ConnectionStatus.py index 36f804e3cf..bcdddb717a 100644 --- a/cura/API/ConnectionStatus.py +++ b/cura/API/ConnectionStatus.py @@ -1,6 +1,6 @@ from typing import Optional -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty from UM.TaskManagement.HttpRequestManager import HttpRequestManager diff --git a/cura/API/__init__.py b/cura/API/__init__.py index 447be98e4b..f5ffeb92f0 100644 --- a/cura/API/__init__.py +++ b/cura/API/__init__.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional, TYPE_CHECKING -from PyQt5.QtCore import QObject, pyqtProperty +from PyQt6.QtCore import QObject, pyqtProperty from cura.API.Backups import Backups from cura.API.ConnectionStatus import ConnectionStatus @@ -34,12 +34,13 @@ class CuraAPI(QObject): raise RuntimeError("Tried to create singleton '{class_name}' more than once.".format(class_name = CuraAPI.__name__)) if application is None: raise RuntimeError("Upon first time creation, the application must be set.") - cls.__instance = super(CuraAPI, cls).__new__(cls) + instance = super(CuraAPI, cls).__new__(cls) cls._application = application - return cls.__instance + return instance def __init__(self, application: Optional["CuraApplication"] = None) -> None: super().__init__(parent = CuraAPI._application) + CuraAPI.__instance = self self._account = Account(self._application) diff --git a/cura/ApplicationMetadata.py b/cura/ApplicationMetadata.py index f367f61cc7..cb80c12416 100644 --- a/cura/ApplicationMetadata.py +++ b/cura/ApplicationMetadata.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. # --------- @@ -13,7 +13,7 @@ DEFAULT_CURA_DEBUG_MODE = False # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # CuraVersion.py.in template. -CuraSDKVersion = "7.9.0" +CuraSDKVersion = "8.0.0" try: from cura.CuraVersion import CuraAppName # type: ignore diff --git a/cura/AutoSave.py b/cura/AutoSave.py index 3205f48af1..c8ea9b756f 100644 --- a/cura/AutoSave.py +++ b/cura/AutoSave.py @@ -1,7 +1,7 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QTimer +from PyQt6.QtCore import QTimer from typing import Any, TYPE_CHECKING from UM.Logger import Logger diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index fa230866e9..fe607915c1 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -31,7 +31,7 @@ from cura.Settings.GlobalStack import GlobalStack from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Settings.ExtruderManager import ExtruderManager -from PyQt5.QtCore import QTimer +from PyQt6.QtCore import QTimer if TYPE_CHECKING: diff --git a/cura/CameraAnimation.py b/cura/CameraAnimation.py index 37f230a30d..699655a31e 100644 --- a/cura/CameraAnimation.py +++ b/cura/CameraAnimation.py @@ -2,8 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QVariantAnimation, QEasingCurve -from PyQt5.QtGui import QVector3D +from PyQt6.QtCore import QVariantAnimation, QEasingCurve +from PyQt6.QtGui import QVector3D from UM.Math.Vector import Vector @@ -13,7 +13,7 @@ class CameraAnimation(QVariantAnimation): super().__init__(parent) self._camera_tool = None self.setDuration(300) - self.setEasingCurve(QEasingCurve.OutQuad) + self.setEasingCurve(QEasingCurve.Type.OutQuad) def setCameraTool(self, camera_tool): self._camera_tool = camera_tool diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 35e9aa832b..bda9568666 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -20,9 +20,9 @@ try: except ImportError: with_sentry_sdk = False -from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl -from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QUrl +from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit, QGroupBox, QCheckBox, QPushButton +from PyQt6.QtGui import QDesktopServices from UM.Application import Application from UM.Logger import Logger @@ -136,8 +136,8 @@ class CrashHandler: # "backup and start clean" and "close" buttons buttons = QDialogButtonBox() - buttons.addButton(QDialogButtonBox.Close) - buttons.addButton(catalog.i18nc("@action:button", "Backup and Reset Configuration"), QDialogButtonBox.AcceptRole) + buttons.addButton(QDialogButtonBox.StandardButton.Close) + buttons.addButton(catalog.i18nc("@action:button", "Backup and Reset Configuration"), QDialogButtonBox.ButtonRole.AcceptRole) buttons.rejected.connect(self._closeEarlyCrashDialog) buttons.accepted.connect(self._backupAndStartClean) @@ -161,7 +161,7 @@ class CrashHandler: QDesktopServices.openUrl(QUrl.fromLocalFile( path )) def _showDetailedReport(self): - self.dialog.exec_() + self.dialog.exec() def _createDialog(self): """Creates a modal dialog.""" @@ -409,12 +409,12 @@ class CrashHandler: def _buttonsWidget(self): buttons = QDialogButtonBox() - buttons.addButton(QDialogButtonBox.Close) + buttons.addButton(QDialogButtonBox.StandardButton.Close) # Like above, this will be served as a separate detailed report dialog if the application has not yet been # fully loaded. In this case, "send report" will be a check box in the early crash dialog, so there is no # need for this extra button. if self.has_started: - buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.AcceptRole) + buttons.addButton(catalog.i18nc("@action:button", "Send report"), QDialogButtonBox.ButtonRole.AcceptRole) buttons.accepted.connect(self._sendCrashReport) buttons.rejected.connect(self.dialog.close) @@ -456,5 +456,5 @@ class CrashHandler: def _show(self): # When the exception is in the skip_exception_types list, the dialog is not created, so we don't need to show it if self.dialog: - self.dialog.exec_() + self.dialog.exec() os._exit(1) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 8b232ad1bf..193803325f 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -1,8 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QObject, QUrl +from PyQt6.QtGui import QDesktopServices from typing import List, cast from UM.Event import CallFunctionEvent diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5c94bf73b0..a242aa5363 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,6 +1,6 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import enum import os import sys import tempfile @@ -8,10 +8,10 @@ import time from typing import cast, TYPE_CHECKING, Optional, Callable, List, Any, Dict import numpy -from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS -from PyQt5.QtGui import QColor, QIcon -from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtCore import QObject, QTimer, QUrl, pyqtSignal, pyqtProperty, QEvent, pyqtEnum, QCoreApplication +from PyQt6.QtGui import QColor, QIcon +from PyQt6.QtQml import qmlRegisterUncreatableType, qmlRegisterUncreatableMetaObject, qmlRegisterSingletonType, qmlRegisterType +from PyQt6.QtWidgets import QMessageBox import UM.Util import cura.Settings.cura_empty_instance_containers @@ -122,7 +122,6 @@ if TYPE_CHECKING: numpy.seterr(all = "ignore") - class CuraApplication(QtApplication): # SettingVersion represents the set of settings available in the machine/extruder definitions. # You need to make sure that this version number needs to be increased if there is any non-backwards-compatible @@ -131,7 +130,7 @@ class CuraApplication(QtApplication): Created = False - class ResourceTypes: + class ResourceTypes(enum.IntEnum): QmlFiles = Resources.UserType + 1 Firmware = Resources.UserType + 2 QualityInstanceContainer = Resources.UserType + 3 @@ -145,7 +144,7 @@ class CuraApplication(QtApplication): SettingVisibilityPreset = Resources.UserType + 11 IntentInstanceContainer = Resources.UserType + 12 - Q_ENUMS(ResourceTypes) + pyqtEnum(ResourceTypes) def __init__(self, *args, **kwargs): super().__init__(name = ApplicationMetadata.CuraAppName, @@ -350,6 +349,7 @@ class CuraApplication(QtApplication): app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable))) Resources.addSearchPath(os.path.join(app_root, "share", "cura", "resources")) + Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "share", "cura", "resources")) Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources")) if not hasattr(sys, "frozen"): @@ -622,11 +622,14 @@ class CuraApplication(QtApplication): # the QML code then gets `null` as the global stack and can deal with that as it deems fit. self.getMachineManager().setActiveMachine(None) + QtApplication.getInstance().closeAllWindows() + main_window = self.getMainWindow() if main_window is not None: main_window.close() - else: - self.exit(0) + + QtApplication.closeAllWindows() + QCoreApplication.quit() # This function first performs all upon-exit checks such as USB printing that is in progress. # Use this to close the application. @@ -678,6 +681,22 @@ class CuraApplication(QtApplication): self._setLoadingHint(self._i18n_catalog.i18nc("@info:progress", "Initializing Active Machine...")) super().setGlobalContainerStack(stack) + showMessageBox = pyqtSignal(str,str, str, str, int, int, + arguments = ["title", "text", "informativeText", "detailedText","buttons", "icon"]) + """A reusable dialogbox""" + + def messageBox(self, title, text, + informativeText = "", + detailedText = "", + buttons = QMessageBox.StandardButton.Ok, + icon = QMessageBox.Icon.NoIcon, + callback = None, + callback_arguments = [] + ): + self._message_box_callback = callback + self._message_box_callback_arguments = callback_arguments + self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon) + showDiscardOrKeepProfileChanges = pyqtSignal() def discardOrKeepProfileChanges(self) -> bool: @@ -858,7 +877,7 @@ class CuraApplication(QtApplication): self._auto_save = AutoSave(self) self._auto_save.initialize() - self.exec_() + self.exec() def __setUpSingleInstanceServer(self): if self._use_single_instance: @@ -1075,12 +1094,18 @@ class CuraApplication(QtApplication): def event(self, event): """Handle Qt events""" - if event.type() == QEvent.FileOpen: + if event.type() == QEvent.Type.FileOpen: if self._plugins_loaded: self._openFile(event.file()) else: self._open_file_queue.append(event.file()) + if int(event.type()) == 20: # 'QEvent.Type.Quit' enum isn't there, even though it should be according to docs. + # Once we're at this point, everything should have been flushed already (past OnExitCallbackManager). + # It's more difficult to call sys.exit(0): That requires that it happens as the result of a pyqtSignal-emit. + # (See https://doc.qt.io/qt-6/qcoreapplication.html#quit) + os._exit(0) + return super().event(event) def getAutoSave(self) -> Optional[AutoSave]: @@ -1121,16 +1146,16 @@ class CuraApplication(QtApplication): engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion) self.processEvents() - qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") + qmlRegisterUncreatableMetaObject(CuraApplication.staticMetaObject, "Cura", 1, 0, "ResourceTypes", "ResourceTypes is an enum-only type") self.processEvents() - qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 0, "SceneController", self.getCuraSceneController) - qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager) - qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager) - qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, "IntentManager", self.getIntentManager) - qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager) - qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager) - qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) + qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 0, self.getCuraSceneController, "SceneController") + qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, self.getExtruderManager, "ExtruderManager") + qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, self.getMachineManager, "MachineManager") + qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager") + qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager") + qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManager, "SimpleModeSettingsManager") + qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, self.getMachineActionManager, "MachineActionManager") self.processEvents() qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil") @@ -1153,16 +1178,16 @@ class CuraApplication(QtApplication): qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") - qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel", self.getQualityManagementModel) - qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, "MaterialManagementModel", self.getMaterialManagementModel) + qmlRegisterSingletonType(QualityManagementModel, "Cura", 1, 0, self.getQualityManagementModel, "QualityManagementModel") + qmlRegisterSingletonType(MaterialManagementModel, "Cura", 1, 5, self.getMaterialManagementModel, "MaterialManagementModel") self.processEvents() qmlRegisterType(DiscoveredPrintersModel, "Cura", 1, 0, "DiscoveredPrintersModel") qmlRegisterType(DiscoveredCloudPrintersModel, "Cura", 1, 7, "DiscoveredCloudPrintersModel") qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, - "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel) + self.getQualityProfilesDropDownMenuModel, "QualityProfilesDropDownMenuModel") qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, - "CustomQualityProfilesDropDownMenuModel", self.getCustomQualityProfilesDropDownMenuModel) + self.getCustomQualityProfilesDropDownMenuModel, "CustomQualityProfilesDropDownMenuModel") qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel") qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel") @@ -1174,14 +1199,14 @@ class CuraApplication(QtApplication): qmlRegisterType(FirstStartMachineActionsModel, "Cura", 1, 0, "FirstStartMachineActionsModel") qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel") - qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance) + qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, ContainerManager.getInstance, "ContainerManager") qmlRegisterType(SidebarCustomMenuItemsModel, "Cura", 1, 0, "SidebarCustomMenuItemsModel") qmlRegisterType(PrinterOutputDevice, "Cura", 1, 0, "PrinterOutputDevice") from cura.API import CuraAPI - qmlRegisterSingletonType(CuraAPI, "Cura", 1, 1, "API", self.getCuraAPI) - qmlRegisterUncreatableType(Account, "Cura", 1, 0, "AccountSyncState", "Could not create AccountSyncState") + qmlRegisterSingletonType(CuraAPI, "Cura", 1, 1, self.getCuraAPI, "API") + qmlRegisterUncreatableMetaObject(CuraApplication.staticMetaObject, "Cura", 1, 0, "AccountSyncState", "AccountSyncState is an enum-only type") # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index af75aa7b66..17d6832ac6 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -13,7 +13,7 @@ catalog = i18nCatalog("cura") if TYPE_CHECKING: from UM.Qt.QtApplication import QtApplication - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject class CuraPackageManager(PackageManager): diff --git a/cura/CuraView.py b/cura/CuraView.py index 86d4254a7d..6b20cf18d3 100644 --- a/cura/CuraView.py +++ b/cura/CuraView.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QUrl +from PyQt6.QtCore import pyqtProperty, QUrl from UM.Resources import Resources from UM.View.View import View diff --git a/cura/MachineAction.py b/cura/MachineAction.py index 74b742ef4d..15d9ab1ca1 100644 --- a/cura/MachineAction.py +++ b/cura/MachineAction.py @@ -4,7 +4,7 @@ import os from typing import Optional -from PyQt5.QtCore import QObject, QUrl, pyqtSlot, pyqtProperty, pyqtSignal +from PyQt6.QtCore import QObject, QUrl, pyqtSlot, pyqtProperty, pyqtSignal from UM.Logger import Logger from UM.PluginObject import PluginObject diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 8213734348..2bc8a6bc58 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -5,7 +5,7 @@ import time from collections import deque -from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, QTimer, pyqtSignal, pyqtProperty from typing import Optional, Any, Set from UM.Logger import Logger @@ -53,6 +53,8 @@ class MachineErrorChecker(QObject): self._keys_to_check = set() # type: Set[str] + self._num_keys_to_check_per_update = 10 + def initialize(self) -> None: self._error_check_timer.timeout.connect(self._rescheduleCheck) @@ -162,37 +164,37 @@ class MachineErrorChecker(QObject): self._check_in_progress = True - # If there is nothing to check any more, it means there is no error. - if not self._stacks_and_keys_to_check: - # Finish - self._setResult(False) - return + for i in range(self._num_keys_to_check_per_update): + # If there is nothing to check any more, it means there is no error. + if not self._stacks_and_keys_to_check: + # Finish + self._setResult(False) + return - # Get the next stack and key to check - stack, key = self._stacks_and_keys_to_check.popleft() + # Get the next stack and key to check + stack, key = self._stacks_and_keys_to_check.popleft() - enabled = stack.getProperty(key, "enabled") - if not enabled: - self._application.callLater(self._checkStack) - return + enabled = stack.getProperty(key, "enabled") + if not enabled: + continue - validation_state = stack.getProperty(key, "validationState") - if validation_state is None: - # Setting is not validated. This can happen if there is only a setting definition. - # We do need to validate it, because a setting definitions value can be set by a function, which could - # be an invalid setting. - definition = stack.getSettingDefinition(key) - validator_type = SettingDefinition.getValidatorForType(definition.type) - if validator_type: - validator = validator_type(key) - validation_state = validator(stack) - if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid): - # Since we don't know if any of the settings we didn't check is has an error value, store the list for the - # next check. - keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check} - keys_to_recheck.add(key) - self._setResult(True, keys_to_recheck = keys_to_recheck) - return + validation_state = stack.getProperty(key, "validationState") + if validation_state is None: + # Setting is not validated. This can happen if there is only a setting definition. + # We do need to validate it, because a setting definitions value can be set by a function, which could + # be an invalid setting. + definition = stack.getSettingDefinition(key) + validator_type = SettingDefinition.getValidatorForType(definition.type) + if validator_type: + validator = validator_type(key) + validation_state = validator(stack) + if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid): + # Since we don't know if any of the settings we didn't check is has an error value, store the list for the + # next check. + keys_to_recheck = {setting_key for stack, setting_key in self._stacks_and_keys_to_check} + keys_to_recheck.add(key) + self._setResult(True, keys_to_recheck = keys_to_recheck) + continue # Schedule the check for the next key self._application.callLater(self._checkStack) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index d4706ae5ef..88736826fd 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -129,7 +129,7 @@ class MachineNode(ContainerNode): if name not in groups_by_name: # CURA-6599 # For some reason, QML will get null or fail to convert type for MachineManager.activeQualityChangesGroup() to - # a QObject. Setting the object ownership to QQmlEngine.CppOwnership doesn't work, but setting the object + # a QObject. Setting the object ownership to QQmlEngine.ObjectOwnership.CppOwnership doesn't work, but setting the object # parent to application seems to work. from cura.CuraApplication import CuraApplication groups_by_name[name] = QualityChangesGroup(name, quality_type = quality_changes["quality_type"], diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 776d540867..2521d3686d 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -3,7 +3,7 @@ from typing import Dict, Set -from PyQt5.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty +from PyQt6.QtCore import Qt, QTimer, pyqtSignal, pyqtProperty from UM.Qt.ListModel import ListModel from UM.Logger import Logger @@ -61,22 +61,22 @@ class BaseMaterialsModel(ListModel): ContainerTree.getInstance().materialsChanged.connect(self._materialsListChanged) self._application.getMaterialManagementModel().favoritesChanged.connect(self._onChanged) - self.addRoleName(Qt.UserRole + 1, "root_material_id") - self.addRoleName(Qt.UserRole + 2, "id") - self.addRoleName(Qt.UserRole + 3, "GUID") - self.addRoleName(Qt.UserRole + 4, "name") - self.addRoleName(Qt.UserRole + 5, "brand") - self.addRoleName(Qt.UserRole + 6, "description") - self.addRoleName(Qt.UserRole + 7, "material") - self.addRoleName(Qt.UserRole + 8, "color_name") - self.addRoleName(Qt.UserRole + 9, "color_code") - self.addRoleName(Qt.UserRole + 10, "density") - self.addRoleName(Qt.UserRole + 11, "diameter") - self.addRoleName(Qt.UserRole + 12, "approximate_diameter") - self.addRoleName(Qt.UserRole + 13, "adhesion_info") - self.addRoleName(Qt.UserRole + 14, "is_read_only") - self.addRoleName(Qt.UserRole + 15, "container_node") - self.addRoleName(Qt.UserRole + 16, "is_favorite") + self.addRoleName(Qt.ItemDataRole.UserRole + 1, "root_material_id") + self.addRoleName(Qt.ItemDataRole.UserRole + 2, "id") + self.addRoleName(Qt.ItemDataRole.UserRole + 3, "GUID") + self.addRoleName(Qt.ItemDataRole.UserRole + 4, "name") + self.addRoleName(Qt.ItemDataRole.UserRole + 5, "brand") + self.addRoleName(Qt.ItemDataRole.UserRole + 6, "description") + self.addRoleName(Qt.ItemDataRole.UserRole + 7, "material") + self.addRoleName(Qt.ItemDataRole.UserRole + 8, "color_name") + self.addRoleName(Qt.ItemDataRole.UserRole + 9, "color_code") + self.addRoleName(Qt.ItemDataRole.UserRole + 10, "density") + self.addRoleName(Qt.ItemDataRole.UserRole + 11, "diameter") + self.addRoleName(Qt.ItemDataRole.UserRole + 12, "approximate_diameter") + self.addRoleName(Qt.ItemDataRole.UserRole + 13, "adhesion_info") + self.addRoleName(Qt.ItemDataRole.UserRole + 14, "is_read_only") + self.addRoleName(Qt.ItemDataRole.UserRole + 15, "container_node") + self.addRoleName(Qt.ItemDataRole.UserRole + 16, "is_favorite") def _onChanged(self) -> None: self._update_timer.start() diff --git a/cura/Machines/Models/BuildPlateModel.py b/cura/Machines/Models/BuildPlateModel.py index 3697dd2762..716792ceb6 100644 --- a/cura/Machines/Models/BuildPlateModel.py +++ b/cura/Machines/Models/BuildPlateModel.py @@ -1,14 +1,14 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt +from PyQt6.QtCore import Qt from UM.Logger import Logger from UM.Qt.ListModel import ListModel class BuildPlateModel(ListModel): - NameRole = Qt.UserRole + 1 - ContainerNodeRole = Qt.UserRole + 2 + NameRole = Qt.ItemDataRole.UserRole + 1 + ContainerNodeRole = Qt.ItemDataRole.UserRole + 2 def __init__(self, parent = None): super().__init__(parent) diff --git a/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py index ce4b87da2b..54f0e44a2a 100644 --- a/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py @@ -10,7 +10,7 @@ from cura.Machines.ContainerTree import ContainerTree from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel if TYPE_CHECKING: - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject from UM.Settings.Interfaces import ContainerInterface diff --git a/cura/Machines/Models/DiscoveredCloudPrintersModel.py b/cura/Machines/Models/DiscoveredCloudPrintersModel.py index 692ed49593..c6935b2e34 100644 --- a/cura/Machines/Models/DiscoveredCloudPrintersModel.py +++ b/cura/Machines/Models/DiscoveredCloudPrintersModel.py @@ -1,6 +1,6 @@ from typing import Optional, TYPE_CHECKING, List, Dict -from PyQt5.QtCore import QObject, pyqtSlot, Qt, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, pyqtSlot, Qt, pyqtSignal, pyqtProperty from UM.Qt.ListModel import ListModel @@ -12,10 +12,10 @@ class DiscoveredCloudPrintersModel(ListModel): """Model used to inform the application about newly added cloud printers, which are discovered from the user's account """ - DeviceKeyRole = Qt.UserRole + 1 - DeviceNameRole = Qt.UserRole + 2 - DeviceTypeRole = Qt.UserRole + 3 - DeviceFirmwareVersionRole = Qt.UserRole + 4 + DeviceKeyRole = Qt.ItemDataRole.UserRole + 1 + DeviceNameRole = Qt.ItemDataRole.UserRole + 2 + DeviceTypeRole = Qt.ItemDataRole.UserRole + 3 + DeviceFirmwareVersionRole = Qt.ItemDataRole.UserRole + 4 cloudPrintersDetectedChanged = pyqtSignal(bool) diff --git a/cura/Machines/Models/DiscoveredPrintersModel.py b/cura/Machines/Models/DiscoveredPrintersModel.py index 459ec4d795..4c245a927a 100644 --- a/cura/Machines/Models/DiscoveredPrintersModel.py +++ b/cura/Machines/Models/DiscoveredPrintersModel.py @@ -3,7 +3,7 @@ from typing import Callable, Dict, List, Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject, QTimer +from PyQt6.QtCore import pyqtSlot, pyqtProperty, pyqtSignal, QObject, QTimer from UM.i18n import i18nCatalog from UM.Logger import Logger diff --git a/cura/Machines/Models/ExtrudersModel.py b/cura/Machines/Models/ExtrudersModel.py index 5ae3c19874..2677894bff 100644 --- a/cura/Machines/Models/ExtrudersModel.py +++ b/cura/Machines/Models/ExtrudersModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer +from PyQt6.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer from typing import Iterable, TYPE_CHECKING from UM.i18n import i18nCatalog @@ -23,43 +23,43 @@ class ExtrudersModel(ListModel): """ # The ID of the container stack for the extruder. - IdRole = Qt.UserRole + 1 + IdRole = Qt.ItemDataRole.UserRole + 1 - NameRole = Qt.UserRole + 2 + NameRole = Qt.ItemDataRole.UserRole + 2 """Human-readable name of the extruder.""" - ColorRole = Qt.UserRole + 3 + ColorRole = Qt.ItemDataRole.UserRole + 3 """Colour of the material loaded in the extruder.""" - IndexRole = Qt.UserRole + 4 + IndexRole = Qt.ItemDataRole.UserRole + 4 """Index of the extruder, which is also the value of the setting itself. An index of 0 indicates the first extruder, an index of 1 the second one, and so on. This is the value that will be saved in instance containers. """ # The ID of the definition of the extruder. - DefinitionRole = Qt.UserRole + 5 + DefinitionRole = Qt.ItemDataRole.UserRole + 5 # The material of the extruder. - MaterialRole = Qt.UserRole + 6 + MaterialRole = Qt.ItemDataRole.UserRole + 6 # The variant of the extruder. - VariantRole = Qt.UserRole + 7 - StackRole = Qt.UserRole + 8 + VariantRole = Qt.ItemDataRole.UserRole + 7 + StackRole = Qt.ItemDataRole.UserRole + 8 - MaterialBrandRole = Qt.UserRole + 9 - ColorNameRole = Qt.UserRole + 10 + MaterialBrandRole = Qt.ItemDataRole.UserRole + 9 + ColorNameRole = Qt.ItemDataRole.UserRole + 10 - EnabledRole = Qt.UserRole + 11 + EnabledRole = Qt.ItemDataRole.UserRole + 11 """Is the extruder enabled?""" - MaterialTypeRole = Qt.UserRole + 12 + MaterialTypeRole = Qt.ItemDataRole.UserRole + 12 """The type of the material (e.g. PLA, ABS, PETG, etc.).""" defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] """List of colours to display if there is no material or the material has no known colour. """ - MaterialNameRole = Qt.UserRole + 13 + MaterialNameRole = Qt.ItemDataRole.UserRole + 13 def __init__(self, parent = None): """Initialises the extruders model, defining the roles and listening for changes in the data. diff --git a/cura/Machines/Models/FirstStartMachineActionsModel.py b/cura/Machines/Models/FirstStartMachineActionsModel.py index 7d83f0bff2..b9a1a7cf04 100644 --- a/cura/Machines/Models/FirstStartMachineActionsModel.py +++ b/cura/Machines/Models/FirstStartMachineActionsModel.py @@ -3,7 +3,7 @@ from typing import Optional, Dict, Any, TYPE_CHECKING -from PyQt5.QtCore import QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot +from PyQt6.QtCore import QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot from UM.Qt.ListModel import ListModel @@ -19,9 +19,9 @@ class FirstStartMachineActionsModel(ListModel): - action : the MachineAction object itself """ - TitleRole = Qt.UserRole + 1 - ContentRole = Qt.UserRole + 2 - ActionRole = Qt.UserRole + 3 + TitleRole = Qt.ItemDataRole.UserRole + 1 + ContentRole = Qt.ItemDataRole.UserRole + 2 + ActionRole = Qt.ItemDataRole.UserRole + 3 def __init__(self, application: "CuraApplication", parent: Optional[QObject] = None) -> None: super().__init__(parent) diff --git a/cura/Machines/Models/GlobalStacksModel.py b/cura/Machines/Models/GlobalStacksModel.py index f27a1ec00b..033d89952c 100644 --- a/cura/Machines/Models/GlobalStacksModel.py +++ b/cura/Machines/Models/GlobalStacksModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, QTimer, pyqtProperty, pyqtSignal +from PyQt6.QtCore import Qt, QTimer, pyqtProperty, pyqtSignal from typing import List, Optional from UM.Qt.ListModel import ListModel @@ -15,14 +15,14 @@ from cura.UltimakerCloud.UltimakerCloudConstants import META_CAPABILITIES # To class GlobalStacksModel(ListModel): - NameRole = Qt.UserRole + 1 - IdRole = Qt.UserRole + 2 - HasRemoteConnectionRole = Qt.UserRole + 3 - ConnectionTypeRole = Qt.UserRole + 4 - MetaDataRole = Qt.UserRole + 5 - DiscoverySourceRole = Qt.UserRole + 6 # For separating local and remote printers in the machine management page - RemovalWarningRole = Qt.UserRole + 7 - IsOnlineRole = Qt.UserRole + 8 + NameRole = Qt.ItemDataRole.UserRole + 1 + IdRole = Qt.ItemDataRole.UserRole + 2 + HasRemoteConnectionRole = Qt.ItemDataRole.UserRole + 3 + ConnectionTypeRole = Qt.ItemDataRole.UserRole + 4 + MetaDataRole = Qt.ItemDataRole.UserRole + 5 + DiscoverySourceRole = Qt.ItemDataRole.UserRole + 6 # For separating local and remote printers in the machine management page + RemovalWarningRole = Qt.ItemDataRole.UserRole + 7 + IsOnlineRole = Qt.ItemDataRole.UserRole + 8 def __init__(self, parent = None) -> None: super().__init__(parent) diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py index aeb1f878ca..14e3c4d35e 100644 --- a/cura/Machines/Models/IntentCategoryModel.py +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -2,14 +2,14 @@ #Cura is released under the terms of the LGPLv3 or higher. import collections -from PyQt5.QtCore import Qt, QTimer +from PyQt6.QtCore import Qt, QTimer from typing import TYPE_CHECKING, Optional, Dict from cura.Machines.Models.IntentModel import IntentModel from cura.Settings.IntentManager import IntentManager from UM.Qt.ListModel import ListModel from UM.Settings.ContainerRegistry import ContainerRegistry #To update the list if anything changes. -from PyQt5.QtCore import pyqtSignal +from PyQt6.QtCore import pyqtSignal import cura.CuraApplication if TYPE_CHECKING: from UM.Settings.ContainerRegistry import ContainerInterface @@ -21,11 +21,11 @@ catalog = i18nCatalog("cura") class IntentCategoryModel(ListModel): """Lists the intent categories that are available for the current printer configuration. """ - NameRole = Qt.UserRole + 1 - IntentCategoryRole = Qt.UserRole + 2 - WeightRole = Qt.UserRole + 3 - QualitiesRole = Qt.UserRole + 4 - DescriptionRole = Qt.UserRole + 5 + NameRole = Qt.ItemDataRole.UserRole + 1 + IntentCategoryRole = Qt.ItemDataRole.UserRole + 2 + WeightRole = Qt.ItemDataRole.UserRole + 3 + QualitiesRole = Qt.ItemDataRole.UserRole + 4 + DescriptionRole = Qt.ItemDataRole.UserRole + 5 modelUpdated = pyqtSignal() diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py index 0ec7e268f0..9fb8ff0376 100644 --- a/cura/Machines/Models/IntentModel.py +++ b/cura/Machines/Models/IntentModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional, Dict, Any, Set, List -from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal, QTimer +from PyQt6.QtCore import Qt, QObject, pyqtProperty, pyqtSignal, QTimer import cura.CuraApplication from UM.Qt.ListModel import ListModel @@ -15,11 +15,11 @@ from cura.Machines.QualityGroup import QualityGroup class IntentModel(ListModel): - NameRole = Qt.UserRole + 1 - QualityTypeRole = Qt.UserRole + 2 - LayerHeightRole = Qt.UserRole + 3 - AvailableRole = Qt.UserRole + 4 - IntentRole = Qt.UserRole + 5 + NameRole = Qt.ItemDataRole.UserRole + 1 + QualityTypeRole = Qt.ItemDataRole.UserRole + 2 + LayerHeightRole = Qt.ItemDataRole.UserRole + 3 + AvailableRole = Qt.ItemDataRole.UserRole + 4 + IntentRole = Qt.ItemDataRole.UserRole + 5 def __init__(self, parent: Optional[QObject] = None) -> None: super().__init__(parent) diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py index b0594cb286..0438fc76fa 100644 --- a/cura/Machines/Models/MaterialBrandsModel.py +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -1,7 +1,9 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtQml import QQmlEngine + from UM.Qt.ListModel import ListModel from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel @@ -9,10 +11,11 @@ class MaterialTypesModel(ListModel): def __init__(self, parent = None): super().__init__(parent) + QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) - self.addRoleName(Qt.UserRole + 1, "name") - self.addRoleName(Qt.UserRole + 2, "brand") - self.addRoleName(Qt.UserRole + 3, "colors") + self.addRoleName(Qt.ItemDataRole.UserRole + 1, "name") + self.addRoleName(Qt.ItemDataRole.UserRole + 2, "brand") + self.addRoleName(Qt.ItemDataRole.UserRole + 3, "colors") class MaterialBrandsModel(BaseMaterialsModel): @@ -20,9 +23,10 @@ class MaterialBrandsModel(BaseMaterialsModel): def __init__(self, parent = None): super().__init__(parent) + QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) - self.addRoleName(Qt.UserRole + 1, "name") - self.addRoleName(Qt.UserRole + 2, "material_types") + self.addRoleName(Qt.ItemDataRole.UserRole + 1, "name") + self.addRoleName(Qt.ItemDataRole.UserRole + 2, "material_types") self._update() @@ -74,16 +78,15 @@ class MaterialBrandsModel(BaseMaterialsModel): material_type_item_list = [] brand_item = { "name": brand, - "material_types": MaterialTypesModel(self) + "material_types": MaterialTypesModel() } for material_type, material_list in material_dict.items(): material_type_item = { "name": material_type, "brand": brand, - "colors": BaseMaterialsModel(self) + "colors": BaseMaterialsModel() } - material_type_item["colors"].clear() # Sort materials by name material_list = sorted(material_list, key = lambda x: x["name"].upper()) diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py index ce31193eb8..53d0cca0a2 100644 --- a/cura/Machines/Models/MaterialManagementModel.py +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -2,8 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. import copy # To duplicate materials. -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl +from PyQt6.QtGui import QDesktopServices from typing import Any, Dict, Optional, TYPE_CHECKING import uuid # To generate new GUIDs for new materials. diff --git a/cura/Machines/Models/MultiBuildPlateModel.py b/cura/Machines/Models/MultiBuildPlateModel.py index 8e2f086e3b..300c35754b 100644 --- a/cura/Machines/Models/MultiBuildPlateModel.py +++ b/cura/Machines/Models/MultiBuildPlateModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QTimer, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QTimer, pyqtSignal, pyqtProperty from UM.Application import Application from UM.Scene.Camera import Camera diff --git a/cura/Machines/Models/NozzleModel.py b/cura/Machines/Models/NozzleModel.py index 5f7f8b02f5..2083866ac5 100644 --- a/cura/Machines/Models/NozzleModel.py +++ b/cura/Machines/Models/NozzleModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt +from PyQt6.QtCore import Qt from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -10,9 +10,9 @@ from cura.Machines.ContainerTree import ContainerTree class NozzleModel(ListModel): - IdRole = Qt.UserRole + 1 - HotendNameRole = Qt.UserRole + 2 - ContainerNodeRole = Qt.UserRole + 3 + IdRole = Qt.ItemDataRole.UserRole + 1 + HotendNameRole = Qt.ItemDataRole.UserRole + 2 + ContainerNodeRole = Qt.ItemDataRole.UserRole + 3 def __init__(self, parent = None): super().__init__(parent) diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index 63c1ead29d..8db8719784 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Any, cast, Dict, Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, QObject, Qt, QTimer +from PyQt6.QtCore import pyqtSlot, QObject, Qt, QTimer from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -29,13 +29,13 @@ if TYPE_CHECKING: class QualityManagementModel(ListModel): """This the QML model for the quality management page.""" - NameRole = Qt.UserRole + 1 - IsReadOnlyRole = Qt.UserRole + 2 - QualityGroupRole = Qt.UserRole + 3 - QualityTypeRole = Qt.UserRole + 4 - QualityChangesGroupRole = Qt.UserRole + 5 - IntentCategoryRole = Qt.UserRole + 6 - SectionNameRole = Qt.UserRole + 7 + NameRole = Qt.ItemDataRole.UserRole + 1 + IsReadOnlyRole = Qt.ItemDataRole.UserRole + 2 + QualityGroupRole = Qt.ItemDataRole.UserRole + 3 + QualityTypeRole = Qt.ItemDataRole.UserRole + 4 + QualityChangesGroupRole = Qt.ItemDataRole.UserRole + 5 + IntentCategoryRole = Qt.ItemDataRole.UserRole + 6 + SectionNameRole = Qt.ItemDataRole.UserRole + 7 def __init__(self, parent: Optional["QObject"] = None) -> None: super().__init__(parent) diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index f7316e9c09..b3f92a06f4 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, QTimer +from PyQt6.QtCore import Qt, QTimer import cura.CuraApplication # Imported this way to prevent circular dependencies. from UM.Logger import Logger @@ -13,14 +13,14 @@ from cura.Machines.Models.MachineModelUtils import fetchLayerHeight class QualityProfilesDropDownMenuModel(ListModel): """QML Model for all built-in quality profiles. This model is used for the drop-down quality menu.""" - NameRole = Qt.UserRole + 1 - QualityTypeRole = Qt.UserRole + 2 - LayerHeightRole = Qt.UserRole + 3 - LayerHeightUnitRole = Qt.UserRole + 4 - AvailableRole = Qt.UserRole + 5 - QualityGroupRole = Qt.UserRole + 6 - QualityChangesGroupRole = Qt.UserRole + 7 - IsExperimentalRole = Qt.UserRole + 8 + NameRole = Qt.ItemDataRole.UserRole + 1 + QualityTypeRole = Qt.ItemDataRole.UserRole + 2 + LayerHeightRole = Qt.ItemDataRole.UserRole + 3 + LayerHeightUnitRole = Qt.ItemDataRole.UserRole + 4 + AvailableRole = Qt.ItemDataRole.UserRole + 5 + QualityGroupRole = Qt.ItemDataRole.UserRole + 6 + QualityChangesGroupRole = Qt.ItemDataRole.UserRole + 7 + IsExperimentalRole = Qt.ItemDataRole.UserRole + 8 def __init__(self, parent = None): super().__init__(parent) diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py index 2051f1748d..f3808db1fb 100644 --- a/cura/Machines/Models/QualitySettingsModel.py +++ b/cura/Machines/Models/QualitySettingsModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt +from PyQt6.QtCore import pyqtProperty, pyqtSignal, Qt from typing import Set import cura.CuraApplication @@ -17,13 +17,13 @@ import os class QualitySettingsModel(ListModel): """This model is used to show details settings of the selected quality in the quality management page.""" - KeyRole = Qt.UserRole + 1 - LabelRole = Qt.UserRole + 2 - UnitRole = Qt.UserRole + 3 - ProfileValueRole = Qt.UserRole + 4 - ProfileValueSourceRole = Qt.UserRole + 5 - UserValueRole = Qt.UserRole + 6 - CategoryRole = Qt.UserRole + 7 + KeyRole = Qt.ItemDataRole.UserRole + 1 + LabelRole = Qt.ItemDataRole.UserRole + 2 + UnitRole = Qt.ItemDataRole.UserRole + 3 + ProfileValueRole = Qt.ItemDataRole.UserRole + 4 + ProfileValueSourceRole = Qt.ItemDataRole.UserRole + 5 + UserValueRole = Qt.ItemDataRole.UserRole + 6 + CategoryRole = Qt.ItemDataRole.UserRole + 7 GLOBAL_STACK_POSITION = -1 diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index 2ca0960de4..d4c590dc74 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -3,7 +3,7 @@ from typing import Optional, List -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from UM.Logger import Logger from UM.Preferences import Preferences diff --git a/cura/Machines/Models/UserChangesModel.py b/cura/Machines/Models/UserChangesModel.py index 43bbe8a663..171e910dd8 100644 --- a/cura/Machines/Models/UserChangesModel.py +++ b/cura/Machines/Models/UserChangesModel.py @@ -4,7 +4,7 @@ import os from collections import OrderedDict -from PyQt5.QtCore import pyqtSlot, Qt +from PyQt6.QtCore import pyqtSlot, Qt from UM.Application import Application from UM.Logger import Logger @@ -15,12 +15,12 @@ from UM.Qt.ListModel import ListModel class UserChangesModel(ListModel): - KeyRole = Qt.UserRole + 1 - LabelRole = Qt.UserRole + 2 - ExtruderRole = Qt.UserRole + 3 - OriginalValueRole = Qt.UserRole + 4 - UserValueRole = Qt.UserRole + 6 - CategoryRole = Qt.UserRole + 7 + KeyRole = Qt.ItemDataRole.UserRole + 1 + LabelRole = Qt.ItemDataRole.UserRole + 2 + ExtruderRole = Qt.ItemDataRole.UserRole + 3 + OriginalValueRole = Qt.ItemDataRole.UserRole + 4 + UserValueRole = Qt.ItemDataRole.UserRole + 6 + CategoryRole = Qt.ItemDataRole.UserRole + 7 def __init__(self, parent = None): super().__init__(parent = parent) diff --git a/cura/Machines/QualityChangesGroup.py b/cura/Machines/QualityChangesGroup.py index 668fff785a..f30d04b8a8 100644 --- a/cura/Machines/QualityChangesGroup.py +++ b/cura/Machines/QualityChangesGroup.py @@ -3,7 +3,7 @@ from typing import Any, Dict, Optional -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal +from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal class QualityChangesGroup(QObject): diff --git a/cura/OAuth2/AuthorizationHelpers.py b/cura/OAuth2/AuthorizationHelpers.py index 77e3c66c11..a654ee4bdb 100644 --- a/cura/OAuth2/AuthorizationHelpers.py +++ b/cura/OAuth2/AuthorizationHelpers.py @@ -4,7 +4,7 @@ from base64 import b64encode from datetime import datetime from hashlib import sha512 -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtNetwork import QNetworkReply import secrets from typing import Callable, Optional import urllib.parse diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 0343af68a8..f2e6cd27ec 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -6,8 +6,8 @@ from datetime import datetime, timedelta from typing import Callable, Dict, Optional, TYPE_CHECKING, Union from urllib.parse import urlencode, quote_plus -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QUrl +from PyQt6.QtGui import QDesktopServices from UM.Logger import Logger from UM.Message import Message diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index a2b8a87577..e054528c42 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -1,7 +1,7 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QTimer +from PyQt6.QtCore import QTimer from UM.Application import Application from UM.Logger import Logger diff --git a/cura/PrintJobPreviewImageProvider.py b/cura/PrintJobPreviewImageProvider.py index 321164adeb..90c115a4b4 100644 --- a/cura/PrintJobPreviewImageProvider.py +++ b/cura/PrintJobPreviewImageProvider.py @@ -1,6 +1,6 @@ -from PyQt5.QtGui import QImage -from PyQt5.QtQuick import QQuickImageProvider -from PyQt5.QtCore import QSize +from PyQt6.QtGui import QImage +from PyQt6.QtQuick import QQuickImageProvider +from PyQt6.QtCore import QSize from UM.Application import Application from typing import Tuple @@ -8,7 +8,7 @@ from typing import Tuple class PrintJobPreviewImageProvider(QQuickImageProvider): def __init__(self): - super().__init__(QQuickImageProvider.Image) + super().__init__(QQuickImageProvider.ImageType.Image) def requestImage(self, id: str, size: QSize) -> Tuple[QImage, QSize]: """Request a new image. diff --git a/cura/PrinterOutput/FirmwareUpdater.py b/cura/PrinterOutput/FirmwareUpdater.py index cfcfb2204f..0c1c427ff2 100644 --- a/cura/PrinterOutput/FirmwareUpdater.py +++ b/cura/PrinterOutput/FirmwareUpdater.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, QUrl, pyqtSignal, pyqtProperty from enum import IntEnum from threading import Thread diff --git a/cura/PrinterOutput/GenericOutputController.py b/cura/PrinterOutput/GenericOutputController.py index c160459776..9531a1ab89 100644 --- a/cura/PrinterOutput/GenericOutputController.py +++ b/cura/PrinterOutput/GenericOutputController.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Set, Union, Optional -from PyQt5.QtCore import QTimer +from PyQt6.QtCore import QTimer from .PrinterOutputController import PrinterOutputController diff --git a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py index 4fbf951f45..4cc3e81f56 100644 --- a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py +++ b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional -from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal +from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal from .MaterialOutputModel import MaterialOutputModel diff --git a/cura/PrinterOutput/Models/ExtruderOutputModel.py b/cura/PrinterOutput/Models/ExtruderOutputModel.py index bcd0f579c2..ae1b175f3f 100644 --- a/cura/PrinterOutput/Models/ExtruderOutputModel.py +++ b/cura/PrinterOutput/Models/ExtruderOutputModel.py @@ -3,7 +3,7 @@ from typing import Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot +from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot from .ExtruderConfigurationModel import ExtruderConfigurationModel diff --git a/cura/PrinterOutput/Models/MaterialOutputModel.py b/cura/PrinterOutput/Models/MaterialOutputModel.py index 3714824a89..89509ace72 100644 --- a/cura/PrinterOutput/Models/MaterialOutputModel.py +++ b/cura/PrinterOutput/Models/MaterialOutputModel.py @@ -3,7 +3,7 @@ from typing import Optional -from PyQt5.QtCore import pyqtProperty, QObject +from PyQt6.QtCore import pyqtProperty, QObject class MaterialOutputModel(QObject): diff --git a/cura/PrinterOutput/Models/PrintJobOutputModel.py b/cura/PrinterOutput/Models/PrintJobOutputModel.py index f7404f71ed..164dc5cb67 100644 --- a/cura/PrinterOutput/Models/PrintJobOutputModel.py +++ b/cura/PrinterOutput/Models/PrintJobOutputModel.py @@ -3,8 +3,8 @@ from typing import Optional, TYPE_CHECKING, List -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot, QUrl -from PyQt5.QtGui import QImage +from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot, QUrl +from PyQt6.QtGui import QImage if TYPE_CHECKING: from cura.PrinterOutput.PrinterOutputController import PrinterOutputController @@ -58,7 +58,7 @@ class PrintJobOutputModel(QObject): # requires a QUrl to function, updates correctly we add an increasing number. This causes to see the QUrl # as new (instead of relying on cached version and thus forces an update. temp = "image://print_job_preview/" + str(self._preview_image_id) + "/" + self._key - return QUrl(temp, QUrl.TolerantMode) + return QUrl(temp, QUrl.ParsingMode.TolerantMode) def getPreviewImage(self) -> Optional[QImage]: return self._preview_image @@ -119,16 +119,16 @@ class PrintJobOutputModel(QObject): @pyqtProperty(int, notify = timeTotalChanged) def timeTotal(self) -> int: - return self._time_total + return int(self._time_total) @pyqtProperty(int, notify = timeElapsedChanged) def timeElapsed(self) -> int: - return self._time_elapsed + return int(self._time_elapsed) @pyqtProperty(int, notify = timeElapsedChanged) def timeRemaining(self) -> int: # Never get a negative time remaining - return max(self.timeTotal - self.timeElapsed, 0) + return int(max(self.timeTotal - self.timeElapsed, 0)) @pyqtProperty(float, notify = timeElapsedChanged) def progress(self) -> float: diff --git a/cura/PrinterOutput/Models/PrinterConfigurationModel.py b/cura/PrinterOutput/Models/PrinterConfigurationModel.py index 54f52134b2..85c69abcd3 100644 --- a/cura/PrinterOutput/Models/PrinterConfigurationModel.py +++ b/cura/PrinterOutput/Models/PrinterConfigurationModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal +from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal from typing import List MYPY = False diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index 37464b0b7d..2f7091e014 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot, QUrl +from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant, pyqtSlot, QUrl from typing import List, Dict, Optional, TYPE_CHECKING from UM.Math.Vector import Vector from cura.PrinterOutput.Peripheral import Peripheral diff --git a/cura/PrinterOutput/NetworkMJPGImage.py b/cura/PrinterOutput/NetworkMJPGImage.py index 0bfcfab764..a482b40ad8 100644 --- a/cura/PrinterOutput/NetworkMJPGImage.py +++ b/cura/PrinterOutput/NetworkMJPGImage.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Aldo Hoeben / fieldOfView # NetworkMJPGImage is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QUrl, pyqtProperty, pyqtSignal, pyqtSlot, QRect, QByteArray -from PyQt5.QtGui import QImage, QPainter -from PyQt5.QtQuick import QQuickPaintedItem -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager +from PyQt6.QtCore import QUrl, pyqtProperty, pyqtSignal, pyqtSlot, QRect, QByteArray +from PyQt6.QtGui import QImage, QPainter +from PyQt6.QtQuick import QQuickPaintedItem +from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from UM.Logger import Logger diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 42c1cd78aa..0fc387a53f 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -9,8 +9,8 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionState, ConnectionType -from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication +from PyQt6.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication from time import time from typing import Callable, Dict, List, Optional, Union from enum import IntEnum @@ -146,8 +146,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): url = QUrl("http://" + self._address + self._api_prefix + target) request = QNetworkRequest(url) if content_type is not None: - request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) - request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) + request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) + request.setHeader(QNetworkRequest.KnownHeaders.UserAgentHeader, self._user_agent) return request def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: @@ -162,10 +162,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): if not content_header.startswith("form-data;"): content_header = "form-data; " + content_header - part.setHeader(QNetworkRequest.ContentDispositionHeader, content_header) + part.setHeader(QNetworkRequest.KnownHeaders.ContentDispositionHeader, content_header) if content_type is not None: - part.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + part.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) part.setBody(data) return part @@ -290,7 +290,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): on_progress: Optional[Callable[[int, int], None]] = None) -> QNetworkReply: self._validateManager() request = self._createEmptyRequest(target, content_type=None) - multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) + multi_post_part = QHttpMultiPart(QHttpMultiPart.ContentType.FormDataType) for part in parts: multi_post_part.append(part) @@ -311,7 +311,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): def postForm(self, target: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: post_part = QHttpPart() - post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) + post_part.setHeader(QNetworkRequest.KnownHeaders.ContentDispositionHeader, header_data) post_part.setBody(body_data) self.postFormWithParts(target, [post_part], on_finished, on_progress) @@ -357,10 +357,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): def _handleOnFinished(self, reply: QNetworkReply) -> None: # Due to garbage collection, we need to cache certain bits of post operations. # As we don't want to keep them around forever, delete them if we get a reply. - if reply.operation() == QNetworkAccessManager.PostOperation: + if reply.operation() == QNetworkAccessManager.Operation.PostOperation: self._clearCachedMultiPart(reply) - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) is None: # No status code means it never even reached remote. return diff --git a/cura/PrinterOutput/PrinterOutputDevice.py b/cura/PrinterOutput/PrinterOutputDevice.py index bc7dc4baf4..d3a5e252d3 100644 --- a/cura/PrinterOutput/PrinterOutputDevice.py +++ b/cura/PrinterOutput/PrinterOutputDevice.py @@ -1,11 +1,11 @@ -# Copyright (c) 2021 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from enum import IntEnum from typing import Callable, List, Optional, Union -from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl +from PyQt6.QtWidgets import QMessageBox import cura.CuraApplication # Imported like this to prevent circular imports. from UM.Logger import Logger @@ -137,9 +137,11 @@ class PrinterOutputDevice(QObject, OutputDevice): """ if self.connectionState != connection_state: self._connection_state = connection_state - global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() - if global_stack: - global_stack.setMetaDataEntry("is_online", self.isConnected()) + application = cura.CuraApplication.CuraApplication.getInstance() + if application is not None: # Might happen during the closing of Cura or in a test. + global_stack = application.getGlobalContainerStack() + if global_stack is not None: + global_stack.setMetaDataEntry("is_online", self.isConnected()) self.connectionStateChanged.emit(self._id) @pyqtProperty(int, constant = True) diff --git a/cura/PrinterOutput/UploadMaterialsJob.py b/cura/PrinterOutput/UploadMaterialsJob.py index 7a08a198c1..b6fbafeb24 100644 --- a/cura/PrinterOutput/UploadMaterialsJob.py +++ b/cura/PrinterOutput/UploadMaterialsJob.py @@ -5,7 +5,7 @@ import enum import functools # For partial methods to use as callbacks with information pre-filled. import json # To serialise metadata for API calls. import os # To delete the archive when we're done. -from PyQt5.QtCore import QUrl +from PyQt6.QtCore import QUrl import tempfile # To create an archive before we upload it. import cura.CuraApplication # Imported like this to prevent circular imports. @@ -21,7 +21,7 @@ from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING if TYPE_CHECKING: - from PyQt5.QtNetwork import QNetworkReply + from PyQt6.QtNetwork import QNetworkReply from cura.UltimakerCloud.CloudMaterialSync import CloudMaterialSync catalog = i18nCatalog("cura") diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 7d299b1617..b15eaf2ae1 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -1,7 +1,7 @@ # Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QTimer +from PyQt6.QtCore import QTimer from UM.Application import Application from UM.Math.Polygon import Polygon diff --git a/cura/Scene/CuraSceneController.py b/cura/Scene/CuraSceneController.py index 99a6eee0e2..1d604b74b6 100644 --- a/cura/Scene/CuraSceneController.py +++ b/cura/Scene/CuraSceneController.py @@ -1,7 +1,7 @@ from UM.Logger import Logger -from PyQt5.QtCore import Qt, pyqtSlot, QObject, QTimer -from PyQt5.QtWidgets import QApplication +from PyQt6.QtCore import Qt, pyqtSlot, QObject, QTimer +from PyQt6.QtWidgets import QApplication from UM.Scene.Camera import Camera from cura.UI.ObjectsModel import ObjectsModel @@ -107,8 +107,8 @@ class CuraSceneController(QObject): """Either select or deselect an item""" modifiers = QApplication.keyboardModifiers() - ctrl_is_active = modifiers & Qt.ControlModifier - shift_is_active = modifiers & Qt.ShiftModifier + ctrl_is_active = modifiers & Qt.KeyboardModifier.ControlModifier + shift_is_active = modifiers & Qt.KeyboardModifier.ShiftModifier if ctrl_is_active: item = self._objects_model.getItem(index) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 518eaaa8fa..67a1643d0b 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -6,8 +6,8 @@ import urllib.parse import uuid from typing import Any, cast, Dict, List, TYPE_CHECKING, Union -from PyQt5.QtCore import QObject, QUrl -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtCore import QObject, QUrl +from PyQt6.QtWidgets import QMessageBox from UM.i18n import i18nCatalog from UM.FlameProfiler import pyqtSlot @@ -47,11 +47,11 @@ class ContainerManager(QObject): def __init__(self, application: "CuraApplication") -> None: if ContainerManager.__instance is not None: raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) - ContainerManager.__instance = self try: super().__init__(parent = application) except TypeError: super().__init__() + ContainerManager.__instance = self self._container_name_filters = {} # type: Dict[str, Dict[str, Any]] @@ -206,7 +206,7 @@ class ContainerManager(QObject): if os.path.exists(file_url): result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"), catalog.i18nc("@label Don't translate the XML tag !", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_url)) - if result == QMessageBox.No: + if result == QMessageBox.StandardButton.No: return {"status": "cancelled", "message": "User cancelled"} try: diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 6130019f4d..6ff856efcb 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -6,7 +6,7 @@ import re import configparser from typing import Any, cast, Dict, Optional, List, Union, Tuple -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtWidgets import QMessageBox from UM.Decorators import override from UM.Settings.ContainerFormatError import ContainerFormatError @@ -139,7 +139,7 @@ class CuraContainerRegistry(ContainerRegistry): if os.path.exists(file_name): result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"), catalog.i18nc("@label Don't translate the XML tag !", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_name)) - if result == QMessageBox.No: + if result == QMessageBox.StandardButton.No: return False profile_writer = self._findProfileWriter(extension, description) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index f594ad3d0c..5348deb4bd 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Any, cast, List, Optional, Dict -from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject +from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject from UM.Application import Application from UM.Decorators import override @@ -75,7 +75,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes) - @pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setUserChanges, notify = pyqtContainersChanged) def userChanges(self) -> InstanceContainer: """Get the user changes container. @@ -92,7 +92,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes, postpone_emit = postpone_emit) - @pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setQualityChanges, notify = pyqtContainersChanged) def qualityChanges(self) -> InstanceContainer: """Get the quality changes container. @@ -109,7 +109,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.Intent, new_intent, postpone_emit = postpone_emit) - @pyqtProperty(InstanceContainer, fset = setIntent, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setIntent, notify = pyqtContainersChanged) def intent(self) -> InstanceContainer: """Get the quality container. @@ -126,7 +126,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.Quality, new_quality, postpone_emit = postpone_emit) - @pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setQuality, notify = pyqtContainersChanged) def quality(self) -> InstanceContainer: """Get the quality container. @@ -143,7 +143,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit) - @pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setMaterial, notify = pyqtContainersChanged) def material(self) -> InstanceContainer: """Get the material container. @@ -160,7 +160,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.Variant, new_variant) - @pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setVariant, notify = pyqtContainersChanged) def variant(self) -> InstanceContainer: """Get the variant container. @@ -177,7 +177,7 @@ class CuraContainerStack(ContainerStack): self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes) - @pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged) + @pyqtProperty(QObject, fset = setDefinitionChanges, notify = pyqtContainersChanged) def definitionChanges(self) -> InstanceContainer: """Get the definition changes container. diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 0fdc27ec81..07c074254f 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt. +from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant # For communicating data and events to Qt. from UM.FlameProfiler import pyqtSlot import cura.CuraApplication # To get the global container stack to find the current machine. @@ -31,9 +31,9 @@ class ExtruderManager(QObject): if ExtruderManager.__instance is not None: raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) - ExtruderManager.__instance = self super().__init__(parent) + ExtruderManager.__instance = self self._application = cura.CuraApplication.CuraApplication.getInstance() diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 2a9838c671..e93193818c 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -3,7 +3,7 @@ from typing import Any, Dict, TYPE_CHECKING, Optional -from PyQt5.QtCore import pyqtProperty, pyqtSignal +from PyQt6.QtCore import pyqtProperty, pyqtSignal from UM.Decorators import override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index d40ae08236..f0a6946f88 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -6,7 +6,7 @@ import threading from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List import uuid -from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal +from PyQt6.QtCore import pyqtProperty, pyqtSlot, pyqtSignal from UM.Decorators import deprecated, override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index a556a86dd8..2dfec02201 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot +from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING from UM.Logger import Logger diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 648b1e9cae..1c7a8f0e98 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -6,7 +6,7 @@ import re import unicodedata from typing import Any, List, Dict, TYPE_CHECKING, Optional, cast, Set -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer +from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator diff --git a/cura/Settings/MachineNameValidator.py b/cura/Settings/MachineNameValidator.py index 99a5c7da0a..409365ef97 100644 --- a/cura/Settings/MachineNameValidator.py +++ b/cura/Settings/MachineNameValidator.py @@ -1,8 +1,8 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtSlot, pyqtProperty, QObject, pyqtSignal, QRegExp -from PyQt5.QtGui import QValidator +from PyQt6.QtCore import pyqtSlot, pyqtProperty, QObject, pyqtSignal +from PyQt6.QtGui import QValidator import os #For statvfs. import urllib #To escape machine names for how they're saved to file. @@ -65,6 +65,6 @@ class MachineNameValidator(QObject): self.validation_regex = "a^" #Never matches (unless you manage to get "a" before the start of the string... good luck). self.validationChanged.emit() - @pyqtProperty("QRegExp", notify=validationChanged) + @pyqtProperty(str, notify=validationChanged) def machineNameRegex(self): - return QRegExp(self.machine_name_regex) \ No newline at end of file + return str(self.machine_name_regex) \ No newline at end of file diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 34dfaeb616..5ae00ae271 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional, TYPE_CHECKING -from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal +from PyQt6.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal from UM.FlameProfiler import pyqtSlot from UM.Application import Application from UM.Logger import Logger diff --git a/cura/Settings/SettingVisibilityPreset.py b/cura/Settings/SettingVisibilityPreset.py index e8a4211d69..f41f38faa3 100644 --- a/cura/Settings/SettingVisibilityPreset.py +++ b/cura/Settings/SettingVisibilityPreset.py @@ -3,7 +3,7 @@ import urllib.parse from configparser import ConfigParser from typing import List -from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal +from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal from UM.Logger import Logger from UM.MimeTypeDatabase import MimeTypeDatabase diff --git a/cura/Settings/SidebarCustomMenuItemsModel.py b/cura/Settings/SidebarCustomMenuItemsModel.py index 7177d26923..22b7a9b227 100644 --- a/cura/Settings/SidebarCustomMenuItemsModel.py +++ b/cura/Settings/SidebarCustomMenuItemsModel.py @@ -4,14 +4,14 @@ from typing import Any from UM.Qt.ListModel import ListModel -from PyQt5.QtCore import pyqtSlot, Qt +from PyQt6.QtCore import pyqtSlot, Qt class SidebarCustomMenuItemsModel(ListModel): - name_role = Qt.UserRole + 1 - actions_role = Qt.UserRole + 2 - menu_item_role = Qt.UserRole + 3 - menu_item_icon_name_role = Qt.UserRole + 5 + name_role = Qt.ItemDataRole.UserRole + 1 + actions_role = Qt.ItemDataRole.UserRole + 2 + menu_item_role = Qt.ItemDataRole.UserRole + 3 + menu_item_icon_name_role = Qt.ItemDataRole.UserRole + 5 def __init__(self, parent=None): super().__init__(parent) diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py index 6650a9b333..af8707f235 100644 --- a/cura/Settings/SimpleModeSettingsManager.py +++ b/cura/Settings/SimpleModeSettingsManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty from UM.Application import Application diff --git a/cura/SingleInstance.py b/cura/SingleInstance.py index 597a4d5f32..61ab1204fe 100644 --- a/cura/SingleInstance.py +++ b/cura/SingleInstance.py @@ -5,7 +5,7 @@ import json import os from typing import List, Optional -from PyQt5.QtNetwork import QLocalServer, QLocalSocket +from PyQt6.QtNetwork import QLocalServer, QLocalSocket from UM.Qt.QtApplication import QtApplication #For typing. from UM.Logger import Logger @@ -29,7 +29,7 @@ class SingleInstance: single_instance_socket.connectToServer("ultimaker-cura") single_instance_socket.waitForConnected(msecs = 3000) # wait for 3 seconds - if single_instance_socket.state() != QLocalSocket.ConnectedState: + if single_instance_socket.state() != QLocalSocket.LocalSocketState.ConnectedState: return False # We only send the files that need to be opened. @@ -37,7 +37,7 @@ class SingleInstance: Logger.log("i", "No file need to be opened, do nothing.") return True - if single_instance_socket.state() == QLocalSocket.ConnectedState: + if single_instance_socket.state() == QLocalSocket.LocalSocketState.ConnectedState: Logger.log("i", "Connection has been made to the single-instance Cura socket.") # Protocol is one line of JSON terminated with a carriage return. diff --git a/cura/Snapshot.py b/cura/Snapshot.py index a7b813610f..1266d3dcb1 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -2,10 +2,11 @@ # Cura is released under the terms of the LGPLv3 or higher. import numpy -from PyQt5 import QtCore -from PyQt5.QtCore import QCoreApplication -from PyQt5.QtGui import QImage +from PyQt6 import QtCore +from PyQt6.QtCore import QCoreApplication +from PyQt6.QtGui import QImage +from UM.Logger import Logger from cura.PreviewPass import PreviewPass from UM.Application import Application @@ -20,7 +21,7 @@ class Snapshot: def getImageBoundaries(image: QImage): # Look at the resulting image to get a good crop. # Get the pixels as byte array - pixel_array = image.bits().asarray(image.byteCount()) + pixel_array = image.bits().asarray(image.sizeInBytes()) width, height = image.width(), image.height() # Convert to numpy array, assume it's 32 bit (it should always be) pixels = numpy.frombuffer(pixel_array, dtype=numpy.uint8).reshape([height, width, 4]) @@ -64,6 +65,7 @@ class Snapshot: bbox = bbox + node.getBoundingBox() # If there is no bounding box, it means that there is no model in the buildplate if bbox is None: + Logger.log("w", "Unable to create snapshot as we seem to have an empty buildplate") return None look_at = bbox.center @@ -96,6 +98,7 @@ class Snapshot: try: min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) except (ValueError, AttributeError): + Logger.logException("w", "Failed to crop the snapshot!") return None size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height) @@ -117,7 +120,7 @@ class Snapshot: # Scale it to the correct size scaled_image = cropped_image.scaled( width, height, - aspectRatioMode = QtCore.Qt.IgnoreAspectRatio, - transformMode = QtCore.Qt.SmoothTransformation) + aspectRatioMode = QtCore.Qt.AspectRatioMode.IgnoreAspectRatio, + transformMode = QtCore.Qt.TransformationMode.SmoothTransformation) return scaled_image diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 6c4d46dd72..869ed309dc 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QUrl +from PyQt6.QtCore import pyqtProperty, QUrl from UM.Stage import Stage diff --git a/cura/UI/CuraSplashScreen.py b/cura/UI/CuraSplashScreen.py index dfeba843a2..331fb2e880 100644 --- a/cura/UI/CuraSplashScreen.py +++ b/cura/UI/CuraSplashScreen.py @@ -1,9 +1,9 @@ # Copyright (c) 2020 Ultimaker B.V. # Uranium is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, QCoreApplication, QTimer -from PyQt5.QtGui import QPixmap, QColor, QFont, QPen, QPainter -from PyQt5.QtWidgets import QSplashScreen +from PyQt6.QtCore import Qt, QCoreApplication, QTimer +from PyQt6.QtGui import QPixmap, QColor, QFont, QPen, QPainter +from PyQt6.QtWidgets import QSplashScreen from UM.Resources import Resources from UM.Application import Application @@ -63,8 +63,8 @@ class CuraSplashScreen(QSplashScreen): painter.save() painter.setPen(QColor(255, 255, 255, 255)) - painter.setRenderHint(QPainter.Antialiasing) - painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.RenderHint.Antialiasing) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) version = Application.getInstance().getVersion().split("-") @@ -74,9 +74,9 @@ class CuraSplashScreen(QSplashScreen): painter.setFont(font) if len(version) == 1: - painter.drawText(40, 104 + self._version_y_offset, round(330 * self._scale), round(230 * self._scale), Qt.AlignLeft | Qt.AlignTop, version[0] if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType) - elif len(version) > 1: - painter.drawText(40, 104 + self._version_y_offset, round(330 * self._scale), round(230 * self._scale), Qt.AlignLeft | Qt.AlignTop, version[0] +" "+ version[1] if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType) + painter.drawText(40, 104 + self._version_y_offset, round(330 * self._scale), round(230 * self._scale), Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop, version[0] if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType) + elif len(version) > 1: + painter.drawText(40, 104 + self._version_y_offset, round(330 * self._scale), round(230 * self._scale), Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop, f"{version[0]}-{version[1]}" if not ApplicationMetadata.IsAlternateVersion else ApplicationMetadata.CuraBuildType) # Draw the loading image pen = QPen() @@ -93,8 +93,8 @@ class CuraSplashScreen(QSplashScreen): pen.setColor(QColor(255, 255, 255, 255)) painter.setPen(pen) painter.setFont(font) - painter.drawText(70, 320, 170, 24, - Qt.AlignLeft | Qt.AlignVCenter | Qt.TextWordWrap, + painter.drawText(70, 308, 170, 48, + Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextWordWrap, self._current_message) painter.restore() @@ -106,7 +106,7 @@ class CuraSplashScreen(QSplashScreen): self._current_message = message self.messageChanged.emit(message) - QCoreApplication.flush() + QCoreApplication.processEvents() # Used to be .flush() -- this might be the closest alternative, but uncertain. self.repaint() def close(self): diff --git a/cura/UI/MachineActionManager.py b/cura/UI/MachineActionManager.py index 5e31de32c2..7a4ce92de0 100644 --- a/cura/UI/MachineActionManager.py +++ b/cura/UI/MachineActionManager.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Optional, List, Set, Dict -from PyQt5.QtCore import QObject +from PyQt6.QtCore import QObject from UM.FlameProfiler import pyqtSlot from UM.Logger import Logger diff --git a/cura/UI/MachineSettingsManager.py b/cura/UI/MachineSettingsManager.py index 1d2604c3c9..077dc29f6c 100644 --- a/cura/UI/MachineSettingsManager.py +++ b/cura/UI/MachineSettingsManager.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional, TYPE_CHECKING -from PyQt5.QtCore import QObject, pyqtSlot +from PyQt6.QtCore import QObject, pyqtSlot from UM.i18n import i18nCatalog diff --git a/cura/UI/ObjectsModel.py b/cura/UI/ObjectsModel.py index 64a6e89054..884d516f08 100644 --- a/cura/UI/ObjectsModel.py +++ b/cura/UI/ObjectsModel.py @@ -4,7 +4,7 @@ from UM.Logger import Logger import re from typing import Dict, List, Optional, Union -from PyQt5.QtCore import QTimer, Qt +from PyQt6.QtCore import QTimer, Qt from UM.Application import Application from UM.Qt.ListModel import ListModel @@ -34,14 +34,14 @@ class _NodeInfo: class ObjectsModel(ListModel): """Keep track of all objects in the project""" - NameRole = Qt.UserRole + 1 - SelectedRole = Qt.UserRole + 2 - OutsideAreaRole = Qt.UserRole + 3 - BuilplateNumberRole = Qt.UserRole + 4 - NodeRole = Qt.UserRole + 5 - PerObjectSettingsCountRole = Qt.UserRole + 6 - MeshTypeRole = Qt.UserRole + 7 - ExtruderNumberRole = Qt.UserRole + 8 + NameRole = Qt.ItemDataRole.UserRole + 1 + SelectedRole = Qt.ItemDataRole.UserRole + 2 + OutsideAreaRole = Qt.ItemDataRole.UserRole + 3 + BuilplateNumberRole = Qt.ItemDataRole.UserRole + 4 + NodeRole = Qt.ItemDataRole.UserRole + 5 + PerObjectSettingsCountRole = Qt.ItemDataRole.UserRole + 6 + MeshTypeRole = Qt.ItemDataRole.UserRole + 7 + ExtruderNumberRole = Qt.ItemDataRole.UserRole + 8 def __init__(self, parent = None) -> None: super().__init__(parent) diff --git a/cura/UI/PrintInformation.py b/cura/UI/PrintInformation.py index 2135c6fe81..e18fc38247 100644 --- a/cura/UI/PrintInformation.py +++ b/cura/UI/PrintInformation.py @@ -6,7 +6,7 @@ import math import os from typing import Dict, List, Optional, TYPE_CHECKING -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot, QTimer +from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot, QTimer from UM.Logger import Logger from UM.Qt.Duration import Duration @@ -132,7 +132,7 @@ class PrintInformation(QObject): self._updateJobName() self.preSlicedChanged.emit() - @pyqtProperty(Duration, notify = currentPrintTimeChanged) + @pyqtProperty(QObject, notify = currentPrintTimeChanged) def currentPrintTime(self) -> Duration: return self._current_print_time[self._active_build_plate] diff --git a/cura/UI/RecommendedMode.py b/cura/UI/RecommendedMode.py index 47b617740a..20680c0ca9 100644 --- a/cura/UI/RecommendedMode.py +++ b/cura/UI/RecommendedMode.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSlot +from PyQt6.QtCore import QObject, pyqtSlot from cura import CuraApplication diff --git a/cura/UI/TextManager.py b/cura/UI/TextManager.py index 77dadae809..9fb68cc2fd 100644 --- a/cura/UI/TextManager.py +++ b/cura/UI/TextManager.py @@ -4,7 +4,7 @@ import collections from typing import Optional, Dict, List, cast -from PyQt5.QtCore import QObject, pyqtSlot +from PyQt6.QtCore import QObject, pyqtSlot from UM.i18n import i18nCatalog from UM.Resources import Resources diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index 890e34a31e..26c27418cf 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -6,7 +6,7 @@ import os from collections import deque from typing import TYPE_CHECKING, Optional, List, Dict, Any -from PyQt5.QtCore import QUrl, Qt, pyqtSlot, pyqtProperty, pyqtSignal +from PyQt6.QtCore import QUrl, Qt, pyqtSlot, pyqtProperty, pyqtSignal from UM.i18n import i18nCatalog from UM.Logger import Logger @@ -14,7 +14,7 @@ from UM.Qt.ListModel import ListModel from UM.Resources import Resources if TYPE_CHECKING: - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject from cura.CuraApplication import CuraApplication @@ -36,11 +36,11 @@ class WelcomePagesModel(ListModel): Note that in any case, a page that has its "should_show_function" == False will ALWAYS be skipped. """ - IdRole = Qt.UserRole + 1 # Page ID - PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file - NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to - NextPageButtonTextRole = Qt.UserRole + 4 # The text for the next page button - PreviousPageButtonTextRole = Qt.UserRole + 5 # The text for the previous page button + IdRole = Qt.ItemDataRole.UserRole + 1 # Page ID + PageUrlRole = Qt.ItemDataRole.UserRole + 2 # URL to the page's QML file + NextPageIdRole = Qt.ItemDataRole.UserRole + 3 # The next page ID it should go to + NextPageButtonTextRole = Qt.ItemDataRole.UserRole + 4 # The text for the next page button + PreviousPageButtonTextRole = Qt.ItemDataRole.UserRole + 5 # The text for the previous page button def __init__(self, application: "CuraApplication", parent: Optional["QObject"] = None) -> None: super().__init__(parent) diff --git a/cura/UI/WhatsNewPagesModel.py b/cura/UI/WhatsNewPagesModel.py index b99bdf30f0..4fb7802924 100644 --- a/cura/UI/WhatsNewPagesModel.py +++ b/cura/UI/WhatsNewPagesModel.py @@ -4,7 +4,7 @@ import os from typing import Optional, Dict, List, Tuple, TYPE_CHECKING -from PyQt5.QtCore import pyqtProperty, pyqtSlot +from PyQt6.QtCore import pyqtProperty, pyqtSlot from UM.Logger import Logger from UM.Resources import Resources @@ -12,7 +12,7 @@ from UM.Resources import Resources from cura.UI.WelcomePagesModel import WelcomePagesModel if TYPE_CHECKING: - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject from cura.CuraApplication import CuraApplication diff --git a/cura/UltimakerCloud/CloudMaterialSync.py b/cura/UltimakerCloud/CloudMaterialSync.py index 8848b9bd60..e2eb50f97e 100644 --- a/cura/UltimakerCloud/CloudMaterialSync.py +++ b/cura/UltimakerCloud/CloudMaterialSync.py @@ -1,8 +1,8 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl +from PyQt6.QtGui import QDesktopServices from typing import Dict, Optional, TYPE_CHECKING import zipfile # To export all materials in a .zip archive. diff --git a/cura/UltimakerCloud/UltimakerCloudScope.py b/cura/UltimakerCloud/UltimakerCloudScope.py index bbcc8e2aa9..a173c5c758 100644 --- a/cura/UltimakerCloud/UltimakerCloudScope.py +++ b/cura/UltimakerCloud/UltimakerCloudScope.py @@ -1,7 +1,7 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtNetwork import QNetworkRequest +from PyQt6.QtNetwork import QNetworkRequest from UM.Logger import Logger from UM.TaskManagement.HttpRequestScope import DefaultUserAgentScope diff --git a/cura/Utils/NetworkingUtil.py b/cura/Utils/NetworkingUtil.py index b13f7903b9..5e480b28a9 100644 --- a/cura/Utils/NetworkingUtil.py +++ b/cura/Utils/NetworkingUtil.py @@ -4,7 +4,7 @@ import socket from typing import Optional -from PyQt5.QtCore import QObject, pyqtSlot +from PyQt6.QtCore import QObject, pyqtSlot # diff --git a/cura_app.py b/cura_app.py index 1520d2da67..e11e3c4232 100755 --- a/cura_app.py +++ b/cura_app.py @@ -19,7 +19,7 @@ if sys.platform != "linux": # Turns out the Linux build _does_ use this, but we os.environ["QT_PLUGIN_PATH"] = "" # Security workaround: Don't need it, and introduces an attack vector, so set to nul. os.environ["QML2_IMPORT_PATH"] = "" # Security workaround: Don't need it, and introduces an attack vector, so set to nul. -from PyQt5.QtNetwork import QSslConfiguration, QSslSocket +from PyQt6.QtNetwork import QSslConfiguration, QSslSocket from UM.Platform import Platform from cura import ApplicationMetadata @@ -151,15 +151,15 @@ def exceptHook(hook_type, value, traceback): # The flag "CuraApplication.Created" is set to True when CuraApplication finishes its constructor call. # # Before the "started" flag is set to True, the Qt event loop has not started yet. The event loop is a blocking - # call to the QApplication.exec_(). In this case, we need to: + # call to the QApplication.exec(). In this case, we need to: # 1. Remove all scheduled events so no more unnecessary events will be processed, such as loading the main dialog, # loading the machine, etc. - # 2. Start the Qt event loop with exec_() and show the Crash Dialog. + # 2. Start the Qt event loop with exec() and show the Crash Dialog. # # If the application has finished its initialization and was running fine, and then something causes a crash, # we run the old routine to show the Crash Dialog. # - from PyQt5.Qt import QApplication + from PyQt6.QtWidgets import QApplication if CuraApplication.Created: _crash_handler = CrashHandler(hook_type, value, traceback, has_started) if CuraApplication.splash is not None: @@ -167,7 +167,7 @@ def exceptHook(hook_type, value, traceback): if not has_started: CuraApplication.getInstance().removePostedEvents(None) _crash_handler.early_crash_dialog.show() - sys.exit(CuraApplication.getInstance().exec_()) + sys.exit(CuraApplication.getInstance().exec()) else: _crash_handler.show() else: @@ -178,7 +178,7 @@ def exceptHook(hook_type, value, traceback): if CuraApplication.splash is not None: CuraApplication.splash.close() _crash_handler.early_crash_dialog.show() - sys.exit(application.exec_()) + sys.exit(application.exec()) # Set exception hook to use the crash dialog handler @@ -231,7 +231,7 @@ if Platform.isLinux(): if ApplicationMetadata.CuraDebugMode: ssl_conf = QSslConfiguration.defaultConfiguration() - ssl_conf.setPeerVerifyMode(QSslSocket.VerifyNone) + ssl_conf.setPeerVerifyMode(QSslSocket.PeerVerifyMode.VerifyNone) QSslConfiguration.setDefaultConfiguration(ssl_conf) app = CuraApplication() diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 37ca1e98aa..e8b6a54e46 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -1,11 +1,11 @@ -# Copyright (c) 2021 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. +# Copyright (c) 2021-2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. import os.path import zipfile from typing import List, Optional, Union, TYPE_CHECKING, cast -import Savitar +import pySavitar as Savitar import numpy from UM.Logger import Logger @@ -304,4 +304,4 @@ class ThreeMFReader(MeshReader): unit = "millimeter" scale = conversion_to_mm[unit] - return Vector(scale, scale, scale) \ No newline at end of file + return Vector(scale, scale, scale) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 5f57e49cc6..ddc7922546 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -34,7 +34,7 @@ from cura.Settings.CuraContainerStack import _ContainerIndexes from cura.CuraApplication import CuraApplication from cura.Utils.Threading import call_on_qt_thread -from PyQt5.QtCore import QCoreApplication +from PyQt6.QtCore import QCoreApplication from .WorkspaceDialog import WorkspaceDialog diff --git a/plugins/3MFReader/UpdatableMachinesModel.py b/plugins/3MFReader/UpdatableMachinesModel.py index a332c669e6..9d6eee6c3e 100644 --- a/plugins/3MFReader/UpdatableMachinesModel.py +++ b/plugins/3MFReader/UpdatableMachinesModel.py @@ -3,7 +3,7 @@ from typing import Dict, List -from PyQt5.QtCore import Qt +from PyQt6.QtCore import Qt from UM.Qt.ListModel import ListModel from cura.Settings.GlobalStack import GlobalStack @@ -25,10 +25,10 @@ class UpdatableMachinesModel(ListModel): def __init__(self, parent = None) -> None: super().__init__(parent) - self.addRoleName(Qt.UserRole + 1, "id") - self.addRoleName(Qt.UserRole + 2, "name") - self.addRoleName(Qt.UserRole + 3, "displayName") - self.addRoleName(Qt.UserRole + 4, "type") # Either "default_option" or "machine" + 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]] diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 8d59ec1339..b63d8b6288 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional, Dict, cast -from PyQt5.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication +from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication from UM.FlameProfiler import pyqtSlot from UM.PluginRegistry import PluginRegistry from UM.Application import Application diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index e8739dcf64..e69cced221 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -426,7 +426,7 @@ UM.Dialog width: parent.width height: childrenRect.height visible: manager.hasObjectsOnPlate - UM.RecolorImage + UM.ColorImage { width: warningLabel.height height: width diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index 09fcf6c573..bf0bc05364 100644 --- a/plugins/3MFReader/plugin.json +++ b/plugins/3MFReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for reading 3MF files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 45ba556d65..853aa08513 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -1,5 +1,5 @@ -# Copyright (c) 2015 Ultimaker B.V. -# Uranium is released under the terms of the LGPLv3 or higher. +# Copyright (c) 2015-2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. from typing import Optional from UM.Mesh.MeshWriter import MeshWriter @@ -13,9 +13,9 @@ from cura.CuraApplication import CuraApplication from cura.Utils.Threading import call_on_qt_thread from cura.Snapshot import Snapshot -from PyQt5.QtCore import QBuffer +from PyQt6.QtCore import QBuffer -import Savitar +import pySavitar as Savitar import numpy import datetime @@ -157,7 +157,7 @@ class ThreeMFWriter(MeshWriter): snapshot = self._createSnapshot() if snapshot: thumbnail_buffer = QBuffer() - thumbnail_buffer.open(QBuffer.ReadWrite) + thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite) snapshot.save(thumbnail_buffer, "PNG") thumbnail_file = zipfile.ZipInfo("Metadata/thumbnail.png") diff --git a/plugins/3MFWriter/__init__.py b/plugins/3MFWriter/__init__.py index eff1648489..eb8a596afe 100644 --- a/plugins/3MFWriter/__init__.py +++ b/plugins/3MFWriter/__init__.py @@ -5,21 +5,23 @@ import sys from UM.Logger import Logger try: from . import ThreeMFWriter + threemf_writer_was_imported = True except ImportError: Logger.log("w", "Could not import ThreeMFWriter; libSavitar may be missing") -from . import ThreeMFWorkspaceWriter + threemf_writer_was_imported = False +from . import ThreeMFWorkspaceWriter from UM.i18n import i18nCatalog -from UM.Platform import Platform i18n_catalog = i18nCatalog("cura") + def getMetaData(): workspace_extension = "3mf" metaData = {} - if "3MFWriter.ThreeMFWriter" in sys.modules: + if threemf_writer_was_imported: metaData["mesh_writer"] = { "output": [{ "extension": "3mf", @@ -39,6 +41,7 @@ def getMetaData(): return metaData + def register(app): if "3MFWriter.ThreeMFWriter" in sys.modules: return {"mesh_writer": ThreeMFWriter.ThreeMFWriter(), diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 8e6a5785a7..b59d4ef8e1 100644 --- a/plugins/3MFWriter/plugin.json +++ b/plugins/3MFWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for writing 3MF files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/AMFReader/plugin.json b/plugins/AMFReader/plugin.json index 40b4917b97..2980c00007 100644 --- a/plugins/AMFReader/plugin.json +++ b/plugins/AMFReader/plugin.json @@ -3,5 +3,5 @@ "author": "fieldOfView", "version": "1.0.0", "description": "Provides support for reading AMF files.", - "api": 7 + "api": 8 } diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json index 62a23e5882..23cbaec870 100644 --- a/plugins/CuraDrive/plugin.json +++ b/plugins/CuraDrive/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Backup and restore your configuration.", "version": "1.2.0", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/CuraDrive/src/CreateBackupJob.py b/plugins/CuraDrive/src/CreateBackupJob.py index 12bbc035ac..7d772769ed 100644 --- a/plugins/CuraDrive/src/CreateBackupJob.py +++ b/plugins/CuraDrive/src/CreateBackupJob.py @@ -5,7 +5,7 @@ import threading from datetime import datetime from typing import Any, Dict, Optional -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtNetwork import QNetworkReply from UM.Job import Job from UM.Logger import Logger diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 6dd6f02b97..fde167ec10 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -3,7 +3,7 @@ from typing import Any, Optional, List, Dict, Callable -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtNetwork import QNetworkReply from UM.Logger import Logger from UM.Signal import Signal, signalemitter diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index f42ffea9f7..460284074f 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -5,7 +5,7 @@ import os from datetime import datetime from typing import Any, cast, Dict, List, Optional -from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +from PyQt6.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from UM.Extension import Extension from UM.Logger import Logger diff --git a/plugins/CuraDrive/src/RestoreBackupJob.py b/plugins/CuraDrive/src/RestoreBackupJob.py index f59acbc8b7..54c94b389e 100644 --- a/plugins/CuraDrive/src/RestoreBackupJob.py +++ b/plugins/CuraDrive/src/RestoreBackupJob.py @@ -7,7 +7,7 @@ import threading from tempfile import NamedTemporaryFile from typing import Optional, Any, Dict -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest from UM.Job import Job from UM.Logger import Logger @@ -53,7 +53,7 @@ class RestoreBackupJob(Job): def _onRestoreRequestCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if not HttpRequestManager.replyIndicatesSuccess(reply, error): Logger.warning("Requesting backup failed, response code %s while trying to connect to %s", - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url()) + reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute), reply.url()) self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE self._job_done.set() return diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml index 8ddf8e9d42..b5efc8c41b 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml @@ -17,7 +17,7 @@ RowLayout property alias label: detailName.text property alias value: detailValue.text - UM.RecolorImage + UM.ColorImage { id: icon width: 18 * screenScaleFactor diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml index 01fbca2229..3e050b0482 100644 --- a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml +++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml @@ -23,7 +23,7 @@ Column { id: profileImage fillMode: Image.PreserveAspectFit - source: "../images/backup.svg" + source: Qt.resolvedUrl("../images/backup.svg") anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 4) } diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 8636c465c0..d7ed5fac21 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -1,15 +1,15 @@ -# Copyright (c) 2021 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. +# Copyright (c) 2021-2022 Ultimaker B.V. +# 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. from collections import defaultdict import os -from PyQt5.QtCore import QObject, QTimer, QUrl, pyqtSlot +from PyQt6.QtCore import QObject, QTimer, QUrl, pyqtSlot import sys from time import time from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING -from PyQt5.QtGui import QDesktopServices, QImage +from PyQt6.QtGui import QDesktopServices, QImage from UM.Backend.Backend import Backend, BackendState from UM.Scene.SceneNode import SceneNode @@ -31,7 +31,7 @@ from cura.Utils.Threading import call_on_qt_thread from .ProcessSlicedLayersJob import ProcessSlicedLayersJob from .StartSliceJob import StartSliceJob, StartJobResult -import Arcus +import pyArcus as Arcus if TYPE_CHECKING: from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 74e55ffe2d..be30a9f81c 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -1,5 +1,5 @@ -# Copyright (c) 2021 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. +# Copyright (c) 2021-2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. import numpy from string import Formatter @@ -7,8 +7,8 @@ from enum import IntEnum import time from typing import Any, cast, Dict, List, Optional, Set import re -import Arcus #For typing. -from PyQt5.QtCore import QCoreApplication +import pyArcus as Arcus # For typing. +from PyQt6.QtCore import QCoreApplication from UM.Job import Job from UM.Logger import Logger @@ -94,7 +94,7 @@ class StartSliceJob(Job): super().__init__() self._scene = CuraApplication.getInstance().getController().getScene() #type: Scene - self._slice_message = slice_message #type: Arcus.PythonMessage + self._slice_message: Arcus.PythonMessage = slice_message self._is_cancelled = False #type: bool self._build_plate_number = None #type: Optional[int] diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index e3e9324315..cddf73a067 100644 --- a/plugins/CuraEngineBackend/plugin.json +++ b/plugins/CuraEngineBackend/plugin.json @@ -2,7 +2,7 @@ "name": "CuraEngine Backend", "author": "Ultimaker B.V.", "description": "Provides the link to the CuraEngine slicing backend.", - "api": 7, + "api": 8, "version": "1.0.1", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index e4f26532a5..4299ffc974 100644 --- a/plugins/CuraProfileReader/plugin.json +++ b/plugins/CuraProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing Cura profiles.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index 0dc4453329..6331ad9416 100644 --- a/plugins/CuraProfileWriter/plugin.json +++ b/plugins/CuraProfileWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for exporting Cura profiles.", - "api": 7, + "api": 8, "i18n-catalog":"cura" } diff --git a/plugins/DigitalLibrary/plugin.json b/plugins/DigitalLibrary/plugin.json index 5ed846da6a..95e0eeb0e4 100644 --- a/plugins/DigitalLibrary/plugin.json +++ b/plugins/DigitalLibrary/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Connects to the Digital Library, allowing Cura to open files from and save files to the Digital Library.", "version": "1.1.0", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/DigitalLibrary/resources/qml/CreateNewProjectPopup.qml b/plugins/DigitalLibrary/resources/qml/CreateNewProjectPopup.qml index 74bc52b69e..6846720dda 100644 --- a/plugins/DigitalLibrary/resources/qml/CreateNewProjectPopup.qml +++ b/plugins/DigitalLibrary/resources/qml/CreateNewProjectPopup.qml @@ -1,7 +1,7 @@ //Copyright (C) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.10 +import QtQuick 2.15 import QtQuick.Window 2.2 import QtQuick.Controls 2.3 @@ -90,9 +90,9 @@ Popup left: parent.left right: parent.right } - validator: RegExpValidator + validator: RegularExpressionValidator { - regExp: /^[^\\\/\*\?\|\[\]]{0,99}$/ + regularExpression: /^[^\\\/\*\?\|\[\]]{0,99}$/ } text: PrintInformation.jobName diff --git a/plugins/DigitalLibrary/resources/qml/LoadMoreProjectsCard.qml b/plugins/DigitalLibrary/resources/qml/LoadMoreProjectsCard.qml index 45a0c6886d..abc978be4a 100644 --- a/plugins/DigitalLibrary/resources/qml/LoadMoreProjectsCard.qml +++ b/plugins/DigitalLibrary/resources/qml/LoadMoreProjectsCard.qml @@ -30,14 +30,14 @@ Cura.RoundedRectangle anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - UM.RecolorImage + UM.ColorImage { id: projectImage anchors.verticalCenter: parent.verticalCenter width: UM.Theme.getSize("section").height height: width color: UM.Theme.getColor("text_link") - source: "../images/arrow_down.svg" + source: Qt.resolvedUrl("../images/arrow_down.svg") } Label @@ -65,7 +65,7 @@ Cura.RoundedRectangle { target: projectImage color: UM.Theme.getColor("text_link") - source: "../images/arrow_down.svg" + source: Qt.resolvedUrl("../images/arrow_down.svg") } PropertyChanges { @@ -88,7 +88,7 @@ Cura.RoundedRectangle { target: projectImage color: UM.Theme.getColor("text_link") - source: "../images/arrow_down.svg" + source: Qt.resolvedUrl("../images/arrow_down.svg") } PropertyChanges { @@ -111,7 +111,7 @@ Cura.RoundedRectangle { target: projectImage color: UM.Theme.getColor("action_button_disabled_text") - source: "../images/update.svg" + source: Qt.resolvedUrl("../images/update.svg") } PropertyChanges { diff --git a/plugins/DigitalLibrary/resources/qml/OpenProjectFilesPage.qml b/plugins/DigitalLibrary/resources/qml/OpenProjectFilesPage.qml index 5f594cb79e..3e483b4fef 100644 --- a/plugins/DigitalLibrary/resources/qml/OpenProjectFilesPage.qml +++ b/plugins/DigitalLibrary/resources/qml/OpenProjectFilesPage.qml @@ -1,12 +1,11 @@ //Copyright (C) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import Qt.labs.qmlmodels 1.0 import QtQuick 2.15 import QtQuick.Window 2.2 import QtQuick.Controls 2.3 -import UM 1.2 as UM +import UM 1.6 as UM import Cura 1.6 as Cura import DigitalFactory 1.0 as DF @@ -19,6 +18,7 @@ Item height: parent.height property var fileModel: manager.digitalFactoryFileModel + property var modelRows: manager.digitalFactoryFileModel.items signal openFilePressed() signal selectDifferentProjectPressed() @@ -57,21 +57,19 @@ Item border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - //We can't use Cura's TableView here, since in Cura >= 5.0 this uses QtQuick.TableView, while in Cura < 5.0 this uses QtControls1.TableView. - //So we have to define our own. Once support for 4.13 and earlier is dropped, we can switch to Cura.TableView. - Table + // This is not backwards compatible with Cura < 5.0 due to QT.labs being removed in PyQt6 + Cura.TableView { id: filesTableView anchors.fill: parent anchors.margins: parent.border.width columnHeaders: ["Name", "Uploaded by", "Uploaded at"] - model: TableModel + model: UM.TableModel { - TableModelColumn { display: "fileName" } - TableModelColumn { display: "username" } - TableModelColumn { display: "uploadedAt" } - rows: manager.digitalFactoryFileModel.items + id: tableModel + headers: ["fileName", "username", "uploadedAt"] + rows: modelRows } onCurrentRowChanged: @@ -179,4 +177,10 @@ Item openFilesButton.clicked.connect(base.openFilePressed) selectDifferentProjectButton.clicked.connect(base.selectDifferentProjectPressed) } + + onModelRowsChanged: + { + tableModel.clear() + tableModel.rows = modelRows + } } \ No newline at end of file diff --git a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml index fa648d8bc2..e7114e3312 100644 --- a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml +++ b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml @@ -1,12 +1,11 @@ //Copyright (C) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import Qt.labs.qmlmodels 1.0 -import QtQuick 2.10 +import QtQuick 2.15 import QtQuick.Window 2.2 import QtQuick.Controls 2.3 -import UM 1.5 as UM +import UM 1.6 as UM import Cura 1.6 as Cura import DigitalFactory 1.0 as DF @@ -17,7 +16,9 @@ Item id: base width: parent.width height: parent.height + property var fileModel: manager.digitalFactoryFileModel + property var modelRows: manager.digitalFactoryFileModel.items signal savePressed() signal selectDifferentProjectPressed() @@ -61,9 +62,9 @@ Item anchors.left: parent.left anchors.top: fileNameLabel.bottom anchors.topMargin: UM.Theme.getSize("thin_margin").height - validator: RegExpValidator + validator: RegularExpressionValidator { - regExp: /^[\w\-\. ()]{0,255}$/ + regularExpression: /^[\w\-\. ()]{0,255}$/ } text: PrintInformation.jobName @@ -92,9 +93,8 @@ Item border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - //We can't use Cura's TableView here, since in Cura >= 5.0 this uses QtQuick.TableView, while in Cura < 5.0 this uses QtControls1.TableView. - //So we have to define our own. Once support for 4.13 and earlier is dropped, we can switch to Cura.TableView. - Table + // This is not backwards compatible with Cura < 5.0 due to QT.labs being removed in PyQt6 + Cura.TableView { id: filesTableView anchors.fill: parent @@ -102,11 +102,10 @@ Item allowSelection: false columnHeaders: ["Name", "Uploaded by", "Uploaded at"] - model: TableModel + model: UM.TableModel { - TableModelColumn { display: "fileName" } - TableModelColumn { display: "username" } - TableModelColumn { display: "uploadedAt" } + id: tableModel + headers: ["fileName", "username", "uploadedAt"] rows: manager.digitalFactoryFileModel.items } } @@ -248,4 +247,10 @@ Item saveButton.clicked.connect(base.savePressed) selectDifferentProjectButton.clicked.connect(base.selectDifferentProjectPressed) } + + onModelRowsChanged: + { + tableModel.clear() + tableModel.rows = modelRows + } } diff --git a/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml b/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml index 89ebd0f215..5a98fcb975 100644 --- a/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml +++ b/plugins/DigitalLibrary/resources/qml/SelectProjectPage.qml @@ -6,7 +6,7 @@ import QtQuick.Window 2.2 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.1 -import UM 1.2 as UM +import UM 1.6 as UM import Cura 1.7 as Cura import DigitalFactory 1.0 as DF @@ -56,7 +56,7 @@ Item id: createNewProjectButton text: "New Library project" - visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed) + visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == 2 || manager.retrievingProjectsStatus == 3) // Status is succeeded or failed onClicked: { @@ -99,7 +99,7 @@ Item { id: digitalFactoryImage anchors.horizontalCenter: parent.horizontalCenter - source: searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg" + source: Qt.resolvedUrl(searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg") fillMode: Image.PreserveAspectFit width: parent.width - 2 * UM.Theme.getSize("thick_margin").width } @@ -148,29 +148,7 @@ Item contentHeight: projectsListView.implicitHeight anchors.fill: parent - ScrollBar.vertical: ScrollBar - { - // Vertical ScrollBar, styled similarly to the scrollBar in the settings panel - id: verticalScrollBar - visible: flickableView.contentHeight > flickableView.height - - background: Rectangle - { - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: Math.round(implicitWidth / 2) - color: UM.Theme.getColor("scrollbar_background") - } - - contentItem: Rectangle - { - id: scrollViewHandle - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: Math.round(implicitWidth / 2) - - color: verticalScrollBar.pressed ? UM.Theme.getColor("scrollbar_handle_down") : verticalScrollBar.hovered ? UM.Theme.getColor("scrollbar_handle_hover") : UM.Theme.getColor("scrollbar_handle") - Behavior on color { ColorAnimation { duration: 50; } } - } - } + ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar } Column { diff --git a/plugins/DigitalLibrary/resources/qml/Table.qml b/plugins/DigitalLibrary/resources/qml/Table.qml deleted file mode 100644 index c4aafc1ce4..0000000000 --- a/plugins/DigitalLibrary/resources/qml/Table.qml +++ /dev/null @@ -1,203 +0,0 @@ -//Copyright (C) 2022 Ultimaker B.V. -//Cura is released under the terms of the LGPLv3 or higher. - -import Qt.labs.qmlmodels 1.0 -import QtQuick 2.15 -import QtQuick.Controls 2.15 - -import UM 1.2 as UM - -/* - * A re-sizeable table of data. - * - * This table combines a list of headers with a TableView to show certain roles in a table. - * The columns of the table can be resized. - * When the table becomes too big, you can scroll through the table. When a column becomes too small, the contents of - * the table are elided. - * The table gets Cura's themeing. - */ -Item -{ - id: tableBase - - required property var columnHeaders //The text to show in the headers of each column. - property alias model: tableView.model //A TableModel to display in this table. To use a ListModel for the rows, use "rows: listModel.items" - property int currentRow: -1 //The selected row index. - property var onDoubleClicked: function(row) {} //Something to execute when double clicked. Accepts one argument: The index of the row that was clicked on. - property bool allowSelection: true //Whether to allow the user to select items. - - Row - { - id: headerBar - Repeater - { - id: headerRepeater - model: columnHeaders - Rectangle - { - //minimumWidth: Math.max(1, Math.round(tableBase.width / headerRepeater.count)) - width: 300 - height: UM.Theme.getSize("section").height - - color: UM.Theme.getColor("secondary") - - Label - { - id: contentText - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("narrow_margin").width - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("narrow_margin").width - - text: modelData - font: UM.Theme.getFont("medium_bold") - color: UM.Theme.getColor("text") - elide: Text.ElideRight - } - Rectangle //Resize handle. - { - anchors - { - right: parent.right - top: parent.top - bottom: parent.bottom - } - width: UM.Theme.getSize("thick_lining").width - - color: UM.Theme.getColor("thick_lining") - - MouseArea - { - anchors.fill: parent - - cursorShape: Qt.SizeHorCursor - drag - { - target: parent - axis: Drag.XAxis - } - onMouseXChanged: - { - if(drag.active) - { - let new_width = parent.parent.width + mouseX; - let sum_widths = mouseX; - for(let i = 0; i < headerBar.children.length; ++i) - { - sum_widths += headerBar.children[i].width; - } - if(sum_widths > tableBase.width) - { - new_width -= sum_widths - tableBase.width; //Limit the total width to not exceed the view. - } - let width_fraction = new_width / tableBase.width; //Scale with the same fraction along with the total width, if the table is resized. - parent.parent.width = Qt.binding(function() { return Math.max(10, Math.round(tableBase.width * width_fraction)) }); - } - } - } - } - - onWidthChanged: - { - tableView.forceLayout(); //Rescale table cells underneath as well. - } - } - } - } - - TableView - { - id: tableView - anchors - { - top: headerBar.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - } - - flickableDirection: Flickable.AutoFlickIfNeeded - clip: true - ScrollBar.vertical: ScrollBar - { - // Vertical ScrollBar, styled similarly to the scrollBar in the settings panel - id: verticalScrollBar - visible: tableView.contentHeight > tableView.height - - background: Rectangle - { - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: Math.round(implicitWidth / 2) - color: UM.Theme.getColor("scrollbar_background") - } - - contentItem: Rectangle - { - id: scrollViewHandle - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: Math.round(implicitWidth / 2) - - color: verticalScrollBar.pressed ? UM.Theme.getColor("scrollbar_handle_down") : verticalScrollBar.hovered ? UM.Theme.getColor("scrollbar_handle_hover") : UM.Theme.getColor("scrollbar_handle") - Behavior on color { ColorAnimation { duration: 50; } } - } - } - columnWidthProvider: function(column) - { - return headerBar.children[column].width; //Cells get the same width as their column header. - } - - delegate: Rectangle - { - implicitHeight: Math.max(1, cellContent.height) - - color: UM.Theme.getColor((tableBase.currentRow == row) ? "primary" : ((row % 2 == 0) ? "main_background" : "viewport_background")) - - Label - { - id: cellContent - width: parent.width - - text: display - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - TextMetrics - { - id: cellTextMetrics - text: cellContent.text - font: cellContent.font - elide: cellContent.elide - elideWidth: cellContent.width - } - UM.TooltipArea - { - anchors.fill: parent - - acceptedButtons: Qt.LeftButton - text: (cellTextMetrics.elidedText == cellContent.text) ? "" : cellContent.text //Show full text in tooltip if it was elided. - onClicked: - { - if(tableBase.allowSelection) - { - tableBase.currentRow = row; //Select this row. - } - } - onDoubleClicked: - { - tableBase.onDoubleClicked(row); - } - } - } - - Connections - { - target: model - function onRowCountChanged() - { - tableView.contentY = 0; //When the number of rows is reduced, make sure to scroll back to the start. - } - } - } -} \ No newline at end of file diff --git a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py index a5560f32a8..940711f19c 100644 --- a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py +++ b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py @@ -5,9 +5,9 @@ import threading from json import JSONDecodeError from typing import List, Dict, Any, Callable, Union, Optional -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtCore import QUrl +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtNetwork import QNetworkReply from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger diff --git a/plugins/DigitalLibrary/src/DFFileUploader.py b/plugins/DigitalLibrary/src/DFFileUploader.py index 10fee03c4c..6cad7828e2 100644 --- a/plugins/DigitalLibrary/src/DFFileUploader.py +++ b/plugins/DigitalLibrary/src/DFFileUploader.py @@ -1,7 +1,7 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply from typing import Callable, Any, cast, Optional, Union from UM.Logger import Logger @@ -120,9 +120,9 @@ class DFFileUploader: """ Logger.log("i", "Finished callback %s %s", - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) + reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute), reply.url().toString()) - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) # type: Optional[int] + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) # type: Optional[int] if not status_code: Logger.log("e", "Reply contained no status code.") self._onUploadError(reply, None) diff --git a/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py b/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py index 5301151c5f..3456638ba6 100644 --- a/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py +++ b/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py @@ -7,7 +7,7 @@ import re from time import time from typing import List, Any, Optional, Union, Type, Tuple, Dict, cast, TypeVar, Callable -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest from UM.Logger import Logger from UM.TaskManagement.HttpRequestManager import HttpRequestManager @@ -228,7 +228,7 @@ class DigitalFactoryApiClient: self._anti_gc_callbacks.remove(parse) # Don't try to parse the reply if we didn't get one - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) is None: if on_error is not None: on_error() return @@ -250,7 +250,7 @@ class DigitalFactoryApiClient: :return: A tuple with a status code and a dictionary. """ - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() return status_code, json.loads(response) diff --git a/plugins/DigitalLibrary/src/DigitalFactoryController.py b/plugins/DigitalLibrary/src/DigitalFactoryController.py index ba5ee48888..cee367e188 100644 --- a/plugins/DigitalLibrary/src/DigitalFactoryController.py +++ b/plugins/DigitalLibrary/src/DigitalFactoryController.py @@ -10,9 +10,9 @@ from enum import IntEnum from pathlib import Path from typing import Optional, List, Dict, Any, cast -from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QTimer, QUrl -from PyQt5.QtNetwork import QNetworkReply -from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType +from PyQt6.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, pyqtEnum, QTimer, QUrl, QMetaObject +from PyQt6.QtNetwork import QNetworkReply +from PyQt6.QtQml import qmlRegisterType, qmlRegisterUncreatableMetaObject from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger @@ -32,26 +32,6 @@ from .DigitalFactoryProjectModel import DigitalFactoryProjectModel from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse -class RetrievalStatus(IntEnum): - """ - The status of an http get request. - - This is not an enum, because we want to use it in QML and QML doesn't recognize Python enums. - """ - Idle = 0 - InProgress = 1 - Success = 2 - Failed = 3 - - -class DFRetrievalStatus(QObject): - """ - Used as an intermediate QObject that registers the RetrievalStatus as a recognizable enum in QML, so that it can - be used within QML objects as DigitalFactory.RetrievalStatus. - """ - - Q_ENUMS(RetrievalStatus) - class DigitalFactoryController(QObject): @@ -98,6 +78,19 @@ class DigitalFactoryController(QObject): """Signal to inform whether the user is allowed to create more Library projects.""" userCanCreateNewLibraryProjectChanged = pyqtSignal(bool) + class RetrievalStatus(IntEnum): + """ + The status of an http get request. + + This is not an enum, because we want to use it in QML and QML doesn't recognize Python enums. + """ + Idle = 0 + InProgress = 1 + Success = 2 + Failed = 3 + + pyqtEnum(RetrievalStatus) + def __init__(self, application: CuraApplication) -> None: super().__init__(parent = None) @@ -139,9 +132,9 @@ class DigitalFactoryController(QObject): self._erase_temp_files_lock = threading.Lock() # The statuses which indicate whether Cura is waiting for a response from the DigitalFactory API - self.retrieving_files_status = RetrievalStatus.Idle - self.retrieving_projects_status = RetrievalStatus.Idle - self.creating_new_project_status = RetrievalStatus.Idle + self.retrieving_files_status = self.RetrievalStatus.Idle + self.retrieving_projects_status = self.RetrievalStatus.Idle + self.creating_new_project_status = self.RetrievalStatus.Idle self._application.engineCreatedSignal.connect(self._onEngineCreated) self._application.initializationFinished.connect(self._applicationInitializationFinished) @@ -155,9 +148,9 @@ class DigitalFactoryController(QObject): self._has_preselected_project = False self.preselectedProjectChanged.emit() - self.setRetrievingFilesStatus(RetrievalStatus.Idle) - self.setRetrievingProjectsStatus(RetrievalStatus.Idle) - self.setCreatingNewProjectStatus(RetrievalStatus.Idle) + self.setRetrievingFilesStatus(self.RetrievalStatus.Idle) + self.setRetrievingProjectsStatus(self.RetrievalStatus.Idle) + self.setCreatingNewProjectStatus(self.RetrievalStatus.Idle) self.setSelectedProjectIndex(-1) @@ -182,7 +175,7 @@ class DigitalFactoryController(QObject): self.clear() if self._account.isLoggedIn and self.userAccountHasLibraryAccess(): - self.setRetrievingProjectsStatus(RetrievalStatus.InProgress) + self.setRetrievingProjectsStatus(self.RetrievalStatus.InProgress) if preselected_project_id: self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed) else: @@ -199,8 +192,8 @@ class DigitalFactoryController(QObject): self._project_model.setProjects([df_project]) self.setSelectedProjectIndex(0) self.setHasPreselectedProject(True) - self.setRetrievingProjectsStatus(RetrievalStatus.Success) - self.setCreatingNewProjectStatus(RetrievalStatus.Success) + self.setRetrievingProjectsStatus(self.RetrievalStatus.Success) + self.setCreatingNewProjectStatus(self.RetrievalStatus.Success) def _onGetProjectFailed(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None: reply_string = bytes(reply.readAll()).decode() @@ -216,7 +209,7 @@ class DigitalFactoryController(QObject): """ self.setHasMoreProjectsToLoad(self._api.hasMoreProjectsToLoad()) self._project_model.setProjects(df_projects) - self.setRetrievingProjectsStatus(RetrievalStatus.Success) + self.setRetrievingProjectsStatus(self.RetrievalStatus.Success) @pyqtSlot() def loadMoreProjects(self) -> None: @@ -224,7 +217,7 @@ class DigitalFactoryController(QObject): Initiates the process of retrieving the next page of the projects list from the API. """ self._api.getMoreProjects(on_finished = self.loadMoreProjectsFinished, failed = self._onGetProjectsFailed) - self.setRetrievingProjectsStatus(RetrievalStatus.InProgress) + self.setRetrievingProjectsStatus(self.RetrievalStatus.InProgress) def loadMoreProjectsFinished(self, df_projects: List[DigitalFactoryProjectResponse]) -> None: """ @@ -235,13 +228,13 @@ class DigitalFactoryController(QObject): """ self.setHasMoreProjectsToLoad(self._api.hasMoreProjectsToLoad()) self._project_model.extendProjects(df_projects) - self.setRetrievingProjectsStatus(RetrievalStatus.Success) + self.setRetrievingProjectsStatus(self.RetrievalStatus.Success) def _onGetProjectsFailed(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None: """ Error function, called whenever the retrieval of projects fails. """ - self.setRetrievingProjectsStatus(RetrievalStatus.Failed) + self.setRetrievingProjectsStatus(self.RetrievalStatus.Failed) Logger.log("w", "Failed to retrieve the list of projects from the Digital Library. Error encountered: {}".format(error)) def getProjectFilesFinished(self, df_files_in_project: List[DigitalFactoryFileResponse]) -> None: @@ -255,7 +248,7 @@ class DigitalFactoryController(QObject): # Filter to show only the files that can be opened in Cura self._file_model.setFilters({"file_name": lambda x: Path(x).suffix[1:].lower() in self._supported_file_types}) # the suffix is in format '.xyz', so omit the dot at the start self._file_model.setFiles(df_files_in_project) - self.setRetrievingFilesStatus(RetrievalStatus.Success) + self.setRetrievingFilesStatus(self.RetrievalStatus.Success) def getProjectFilesFailed(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None: """ @@ -265,7 +258,7 @@ class DigitalFactoryController(QObject): Logger.warning(f"Failed to retrieve the list of files in project '{self._project_model._projects[self._selected_project_idx]}' from the Digital Library") except IndexError: Logger.warning(f"Failed to retrieve the list of files in a project from the Digital Library. And failed to get the project too.") - self.setRetrievingFilesStatus(RetrievalStatus.Failed) + self.setRetrievingFilesStatus(self.RetrievalStatus.Failed) @pyqtSlot() def clearProjectSelection(self) -> None: @@ -297,7 +290,7 @@ class DigitalFactoryController(QObject): self.selectedFileIndicesChanged.emit([]) if 0 <= project_idx < len(self._project_model.items): library_project_id = self._project_model.items[project_idx]["libraryProjectId"] - self.setRetrievingFilesStatus(RetrievalStatus.InProgress) + self.setRetrievingFilesStatus(self.RetrievalStatus.InProgress) self._api.getListOfFilesInProject(library_project_id, on_finished = self.getProjectFilesFinished, failed = self.getProjectFilesFailed) @pyqtProperty(int, fset = setSelectedProjectIndex, notify = selectedProjectIndexChanged) @@ -381,7 +374,7 @@ class DigitalFactoryController(QObject): """ if project_name: self._api.createNewProject(project_name, self.setProjectAsPreselected, self._createNewLibraryProjectFailed) - self.setCreatingNewProjectStatus(RetrievalStatus.InProgress) + self.setCreatingNewProjectStatus(self.RetrievalStatus.InProgress) else: Logger.log("w", "No project name provided while attempting to create a new project. Aborting the project creation.") @@ -395,7 +388,7 @@ class DigitalFactoryController(QObject): self._project_creation_error_text = "Error while creating the new project: {}".format(reply_dict["errors"][0]["title"]) self.projectCreationErrorTextChanged.emit() - self.setCreatingNewProjectStatus(RetrievalStatus.Failed) + self.setCreatingNewProjectStatus(self.RetrievalStatus.Failed) Logger.log("e", "Something went wrong while trying to create a new a project. Error: {}".format(reply_string)) def setRetrievingProjectsStatus(self, new_status: RetrievalStatus) -> None: @@ -439,7 +432,7 @@ class DigitalFactoryController(QObject): @staticmethod def _onEngineCreated() -> None: - qmlRegisterUncreatableType(DFRetrievalStatus, "DigitalFactory", 1, 0, "RetrievalStatus", "Could not create RetrievalStatus enum type") + qmlRegisterUncreatableMetaObject(DigitalFactoryController.staticMetaObject, "DigitalFactory", 1, 0, "RetrievalStatus", "RetrievalStatus is an Enum-only type") def _applicationInitializationFinished(self) -> None: self._supported_file_types = self._application.getInstance().getMeshFileHandler().getSupportedFileTypesRead() @@ -565,7 +558,7 @@ class DigitalFactoryController(QObject): self.setSelectedProjectIndex(-1) self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed) self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject) - self.setRetrievingProjectsStatus(RetrievalStatus.InProgress) + self.setRetrievingProjectsStatus(self.RetrievalStatus.InProgress) self._has_preselected_project = new_has_preselected_project self.preselectedProjectChanged.emit() diff --git a/plugins/DigitalLibrary/src/DigitalFactoryFileModel.py b/plugins/DigitalLibrary/src/DigitalFactoryFileModel.py index 535cce0e8f..5c030ead17 100644 --- a/plugins/DigitalLibrary/src/DigitalFactoryFileModel.py +++ b/plugins/DigitalLibrary/src/DigitalFactoryFileModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Dict, Callable -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt6.QtCore import Qt, pyqtSignal from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -13,13 +13,13 @@ DIGITAL_FACTORY_DISPLAY_DATETIME_FORMAT = "%d-%m-%Y %H:%M" class DigitalFactoryFileModel(ListModel): - FileNameRole = Qt.UserRole + 1 - FileIdRole = Qt.UserRole + 2 - FileSizeRole = Qt.UserRole + 3 - LibraryProjectIdRole = Qt.UserRole + 4 - DownloadUrlRole = Qt.UserRole + 5 - UsernameRole = Qt.UserRole + 6 - UploadedAtRole = Qt.UserRole + 7 + FileNameRole = Qt.ItemDataRole.UserRole + 1 + FileIdRole = Qt.ItemDataRole.UserRole + 2 + FileSizeRole = Qt.ItemDataRole.UserRole + 3 + LibraryProjectIdRole = Qt.ItemDataRole.UserRole + 4 + DownloadUrlRole = Qt.ItemDataRole.UserRole + 5 + UsernameRole = Qt.ItemDataRole.UserRole + 6 + UploadedAtRole = Qt.ItemDataRole.UserRole + 7 dfFileModelChanged = pyqtSignal() diff --git a/plugins/DigitalLibrary/src/DigitalFactoryProjectModel.py b/plugins/DigitalLibrary/src/DigitalFactoryProjectModel.py index d76774cab1..92aa92ef4c 100644 --- a/plugins/DigitalLibrary/src/DigitalFactoryProjectModel.py +++ b/plugins/DigitalLibrary/src/DigitalFactoryProjectModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional -from PyQt5.QtCore import Qt, pyqtSignal +from PyQt6.QtCore import Qt, pyqtSignal from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -12,12 +12,12 @@ PROJECT_UPDATED_AT_DATETIME_FORMAT = "%d-%m-%Y" class DigitalFactoryProjectModel(ListModel): - DisplayNameRole = Qt.UserRole + 1 - LibraryProjectIdRole = Qt.UserRole + 2 - DescriptionRole = Qt.UserRole + 3 - ThumbnailUrlRole = Qt.UserRole + 5 - UsernameRole = Qt.UserRole + 6 - LastUpdatedRole = Qt.UserRole + 7 + DisplayNameRole = Qt.ItemDataRole.UserRole + 1 + LibraryProjectIdRole = Qt.ItemDataRole.UserRole + 2 + DescriptionRole = Qt.ItemDataRole.UserRole + 3 + ThumbnailUrlRole = Qt.ItemDataRole.UserRole + 5 + UsernameRole = Qt.ItemDataRole.UserRole + 6 + LastUpdatedRole = Qt.ItemDataRole.UserRole + 7 dfProjectModelChanged = pyqtSignal() diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py index 8d0670c844..e7b1aef6d1 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateChecker.py @@ -1,8 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QUrl +from PyQt6.QtGui import QDesktopServices from typing import Set diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index d979e7f3a5..d59ad032f4 100644 --- a/plugins/FirmwareUpdateChecker/plugin.json +++ b/plugins/FirmwareUpdateChecker/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Checks for firmware updates.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py index 35f338fb04..154b2951a1 100644 --- a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py +++ b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py @@ -8,7 +8,7 @@ from UM.i18n import i18nCatalog from UM.Settings.ContainerRegistry import ContainerRegistry from cura.PrinterOutput.FirmwareUpdater import FirmwareUpdateState -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject +from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject from typing import Optional MYPY = False diff --git a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml index c679c4328c..5c410c274a 100644 --- a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml +++ b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml @@ -5,7 +5,7 @@ import QtQuick 2.2 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 -import QtQuick.Dialogs 1.2 // For filedialog +import QtQuick.Dialogs 6.0 // For filedialog import UM 1.5 as UM import Cura 1.0 as Cura @@ -92,11 +92,10 @@ Cura.MachineAction id: customFirmwareDialog title: catalog.i18nc("@title:window", "Select custom firmware") nameFilters: "Firmware image files (*.hex)" - selectExisting: true onAccepted: { updateProgressDialog.visible = true; - activeOutputDevice.updateFirmware(fileUrl); + activeOutputDevice.updateFirmware(selectedFile); } } diff --git a/plugins/FirmwareUpdater/plugin.json b/plugins/FirmwareUpdater/plugin.json index fc42e11973..c63eba18f5 100644 --- a/plugins/FirmwareUpdater/plugin.json +++ b/plugins/FirmwareUpdater/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a machine actions for updating firmware.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index d91b7278ec..879f79bb01 100644 --- a/plugins/GCodeGzReader/plugin.json +++ b/plugins/GCodeGzReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Reads g-code from a compressed archive.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index 2cb72459a4..bc0757da40 100644 --- a/plugins/GCodeGzWriter/plugin.json +++ b/plugins/GCodeGzWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Writes g-code to a compressed archive.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index 4447650d36..2b887c6f90 100644 --- a/plugins/GCodeProfileReader/plugin.json +++ b/plugins/GCodeProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing profiles from g-code files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index 0c5e3d6247..91370e1b35 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -3,6 +3,6 @@ "author": "Victor Larchenko, Ultimaker B.V.", "version": "1.0.1", "description": "Allows loading and displaying G-code files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index cf28e3a792..6e3ae535ad 100644 --- a/plugins/GCodeWriter/plugin.json +++ b/plugins/GCodeWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Writes g-code to a file.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index 28fd1f9106..44877fed6c 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -1,7 +1,7 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.1 +import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.3 import QtQuick.Window 2.1 @@ -31,7 +31,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Height (mm)") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: peak_height_label @@ -47,7 +47,7 @@ UM.Dialog Layout.minimumWidth: UM.Theme.getSize("setting_control").width selectByMouse: true objectName: "Peak_Height" - validator: RegExpValidator { regExp: /^\d{0,3}([\,|\.]\d*)?$/ } + validator: RegularExpressionValidator { regularExpression: /^\d{0,3}([\,|\.]\d*)?$/ } onTextChanged: manager.onPeakHeightChanged(text) } @@ -64,7 +64,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Base (mm)") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { @@ -81,7 +81,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width objectName: "Base_Height" - validator: RegExpValidator { regExp: /^\d{0,3}([\,|\.]\d*)?$/ } + validator: RegularExpressionValidator { regularExpression: /^\d{0,3}([\,|\.]\d*)?$/ } onTextChanged: manager.onBaseHeightChanged(text) } @@ -98,7 +98,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Width (mm)") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: width_label @@ -115,7 +115,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width focus: true - validator: RegExpValidator { regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } + validator: RegularExpressionValidator { regularExpression: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } onTextChanged: manager.onWidthChanged(text) } @@ -132,7 +132,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Depth (mm)") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: depth_label @@ -149,7 +149,7 @@ UM.Dialog selectByMouse: true objectName: "Depth" focus: true - validator: RegExpValidator { regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } + validator: RegularExpressionValidator { regularExpression: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } onTextChanged: manager.onDepthChanged(text) } @@ -166,7 +166,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: "" - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: lighter_is_higher_label @@ -203,7 +203,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Color Model") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: color_model_label @@ -240,7 +240,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "1mm Transmittance (%)") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { id: transmittance_label @@ -255,7 +255,7 @@ UM.Dialog Layout.minimumWidth: UM.Theme.getSize("setting_control").width selectByMouse: true objectName: "Transmittance" - validator: RegExpValidator { regExp: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } + validator: RegularExpressionValidator { regularExpression: /^[1-9]\d{0,2}([\,|\.]\d*)?$/ } onTextChanged: manager.onTransmittanceChanged(text) UM.ToolTip @@ -272,7 +272,7 @@ UM.Dialog Layout.fillWidth: true Layout.minimumWidth: UM.Theme.getSize("setting_control").width text: catalog.i18nc("@action:label", "Smoothing") - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter MouseArea { diff --git a/plugins/ImageReader/ImageReader.py b/plugins/ImageReader/ImageReader.py index 4a32ed71f1..b6895f5554 100644 --- a/plugins/ImageReader/ImageReader.py +++ b/plugins/ImageReader/ImageReader.py @@ -5,8 +5,8 @@ import numpy import math -from PyQt5.QtGui import QImage, qRed, qGreen, qBlue, qAlpha -from PyQt5.QtCore import Qt +from PyQt6.QtGui import QImage, qRed, qGreen, qBlue, qAlpha +from PyQt6.QtCore import Qt from UM.Mesh.MeshReader import MeshReader from UM.Mesh.MeshBuilder import MeshBuilder @@ -63,7 +63,7 @@ class ImageReader(MeshReader): aspect = height / width if img.width() < 2 or img.height() < 2: - img = img.scaled(width, height, Qt.IgnoreAspectRatio) + img = img.scaled(width, height, Qt.AspectRatioMode.IgnoreAspectRatio) height_from_base = max(height_from_base, 0) base_height = max(base_height, 0) @@ -84,15 +84,15 @@ class ImageReader(MeshReader): width = int(max(round(width * scale_factor), 2)) height = int(max(round(height * scale_factor), 2)) - img = img.scaled(width, height, Qt.IgnoreAspectRatio) + img = img.scaled(width, height, Qt.AspectRatioMode.IgnoreAspectRatio) width_minus_one = width - 1 height_minus_one = height - 1 Job.yieldThread() - texel_width = 1.0 / (width_minus_one) * scale_vector.x - texel_height = 1.0 / (height_minus_one) * scale_vector.z + texel_width = 1.0 / width_minus_one * scale_vector.x + texel_height = 1.0 / height_minus_one * scale_vector.z height_data = numpy.zeros((height, width), dtype = numpy.float32) diff --git a/plugins/ImageReader/ImageReaderUI.py b/plugins/ImageReader/ImageReaderUI.py index 103cd6f7e8..54aa8eadc0 100644 --- a/plugins/ImageReader/ImageReaderUI.py +++ b/plugins/ImageReader/ImageReaderUI.py @@ -4,7 +4,7 @@ import os import threading -from PyQt5.QtCore import Qt, pyqtSignal, QObject +from PyQt6.QtCore import Qt, pyqtSignal, QObject from UM.FlameProfiler import pyqtSlot from UM.Application import Application from UM.PluginRegistry import PluginRegistry @@ -85,7 +85,7 @@ class ImageReaderUI(QObject): Logger.log("d", "Creating ImageReader config UI") path = os.path.join(PluginRegistry.getInstance().getPluginPath("ImageReader"), "ConfigUI.qml") self._ui_view = Application.getInstance().createQmlComponent(path, {"manager": self}) - self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint) + self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowType.WindowCloseButtonHint & ~Qt.WindowType.WindowMinimizeButtonHint & ~Qt.WindowType.WindowMaximizeButtonHint) self._disable_size_callbacks = False @pyqtSlot() diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index 90ead02b89..5fcaccfbdb 100644 --- a/plugins/ImageReader/plugin.json +++ b/plugins/ImageReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Enables ability to generate printable geometry from 2D image files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 9611da5a4c..df7703b3f6 100644 --- a/plugins/LegacyProfileReader/plugin.json +++ b/plugins/LegacyProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing profiles from legacy Cura versions.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.py b/plugins/MachineSettingsAction/MachineSettingsAction.py index 96bfa7062b..0f618635af 100755 --- a/plugins/MachineSettingsAction/MachineSettingsAction.py +++ b/plugins/MachineSettingsAction/MachineSettingsAction.py @@ -3,7 +3,7 @@ from typing import Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtProperty +from PyQt6.QtCore import pyqtProperty import UM.i18n from UM.FlameProfiler import pyqtSlot @@ -18,7 +18,7 @@ from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.cura_empty_instance_containers import isEmptyContainer if TYPE_CHECKING: - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject catalog = UM.i18n.i18nCatalog("cura") diff --git a/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml b/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml index db581e8cf5..32c8f8df63 100644 --- a/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml +++ b/plugins/MachineSettingsAction/MachineSettingsPrinterTab.qml @@ -47,7 +47,7 @@ Item Column { Layout.fillWidth: true - Layout.alignment: Qt.AlignTop + Layout.alignment: Qt.AlignmentFlag.AlignTop spacing: base.columnSpacing @@ -178,7 +178,7 @@ Item Column { Layout.fillWidth: true - Layout.alignment: Qt.AlignTop + Layout.alignment: Qt.AlignmentFlag.AlignTop spacing: base.columnSpacing diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 51d88020ae..255f6fb2c1 100644 --- a/plugins/MachineSettingsAction/plugin.json +++ b/plugins/MachineSettingsAction/plugin.json @@ -3,6 +3,6 @@ "author": "fieldOfView, Ultimaker B.V.", "version": "1.0.1", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/Marketplace/CloudApiModel.py b/plugins/Marketplace/CloudApiModel.py new file mode 100644 index 0000000000..90173c5390 --- /dev/null +++ b/plugins/Marketplace/CloudApiModel.py @@ -0,0 +1,29 @@ +from typing import Union + +from cura import ApplicationMetadata +from cura.UltimakerCloud import UltimakerCloudConstants + + +class CloudApiModel: + sdk_version: Union[str, int] = ApplicationMetadata.CuraSDKVersion + cloud_api_version: str = UltimakerCloudConstants.CuraCloudAPIVersion + cloud_api_root: str = UltimakerCloudConstants.CuraCloudAPIRoot + api_url: str = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( + cloud_api_root = cloud_api_root, + cloud_api_version = cloud_api_version, + sdk_version = sdk_version + ) + + # https://api.ultimaker.com/cura-packages/v1/user/packages + api_url_user_packages = "{cloud_api_root}/cura-packages/v{cloud_api_version}/user/packages".format( + cloud_api_root = cloud_api_root, + cloud_api_version = cloud_api_version, + ) + + @classmethod + def userPackageUrl(cls, package_id: str) -> str: + """https://api.ultimaker.com/cura-packages/v1/user/packages/{package_id}""" + + return (CloudApiModel.api_url_user_packages + "/{package_id}").format( + package_id = package_id + ) diff --git a/plugins/Marketplace/CloudSync/CloudApiClient.py b/plugins/Marketplace/CloudSync/CloudApiClient.py new file mode 100644 index 0000000000..cf83567f17 --- /dev/null +++ b/plugins/Marketplace/CloudSync/CloudApiClient.py @@ -0,0 +1,55 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Logger import Logger +from UM.TaskManagement.HttpRequestManager import HttpRequestManager +from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope +from cura.CuraApplication import CuraApplication +from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope +from ..CloudApiModel import CloudApiModel + + +class CloudApiClient: + """Manages Cloud subscriptions + + When a package is added to a user's account, the user is 'subscribed' to that package. + Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins + + Singleton: use CloudApiClient.getInstance() instead of CloudApiClient() + """ + + __instance = None + + @classmethod + def getInstance(cls, app: CuraApplication): + if not cls.__instance: + cls.__instance = CloudApiClient(app) + return cls.__instance + + def __init__(self, app: CuraApplication) -> None: + if self.__instance is not None: + raise RuntimeError("This is a Singleton. use getInstance()") + + self._scope: JsonDecoratorScope = JsonDecoratorScope(UltimakerCloudScope(app)) + + app.getPackageManager().packageInstalled.connect(self._onPackageInstalled) + + def unsubscribe(self, package_id: str) -> None: + url = CloudApiModel.userPackageUrl(package_id) + HttpRequestManager.getInstance().delete(url = url, scope = self._scope) + + def _subscribe(self, package_id: str) -> None: + """You probably don't want to use this directly. All installed packages will be automatically subscribed.""" + + Logger.debug("Subscribing to {}", package_id) + data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version) + HttpRequestManager.getInstance().put( + url = CloudApiModel.api_url_user_packages, + data = data.encode(), + scope = self._scope + ) + + def _onPackageInstalled(self, package_id: str): + if CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: + # We might already be subscribed, but checking would take one extra request. Instead, simply subscribe + self._subscribe(package_id) diff --git a/plugins/Marketplace/CloudSync/CloudPackageChecker.py b/plugins/Marketplace/CloudSync/CloudPackageChecker.py new file mode 100644 index 0000000000..53087e0502 --- /dev/null +++ b/plugins/Marketplace/CloudSync/CloudPackageChecker.py @@ -0,0 +1,166 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import json +from typing import List, Dict, Any, Set +from typing import Optional + +from PyQt6.QtCore import QObject +from PyQt6.QtNetwork import QNetworkReply + +from UM import i18nCatalog +from UM.Logger import Logger +from UM.Message import Message +from UM.Signal import Signal +from UM.TaskManagement.HttpRequestManager import HttpRequestManager +from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope +from cura.API.Account import SyncState +from cura.CuraApplication import CuraApplication, ApplicationMetadata +from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope +from .SubscribedPackagesModel import SubscribedPackagesModel +from ..CloudApiModel import CloudApiModel + + +class CloudPackageChecker(QObject): + + SYNC_SERVICE_NAME = "CloudPackageChecker" + + def __init__(self, application: CuraApplication) -> None: + super().__init__() + + self.discrepancies = Signal() # Emits SubscribedPackagesModel + self._application: CuraApplication = application + self._scope = JsonDecoratorScope(UltimakerCloudScope(application)) + self._model = SubscribedPackagesModel() + self._message: Optional[Message] = None + + self._application.initializationFinished.connect(self._onAppInitialized) + self._i18n_catalog = i18nCatalog("cura") + self._sdk_version = ApplicationMetadata.CuraSDKVersion + + self._last_notified_packages = set() # type: Set[str] + """Packages for which a notification has been shown. No need to bother the user twice for equal content""" + + # This is a plugin, so most of the components required are not ready when + # this is initialized. Therefore, we wait until the application is ready. + def _onAppInitialized(self) -> None: + self._package_manager = self._application.getPackageManager() + # initial check + self._getPackagesIfLoggedIn() + + self._application.getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged) + self._application.getCuraAPI().account.syncRequested.connect(self._getPackagesIfLoggedIn) + + def _onLoginStateChanged(self) -> None: + # reset session + self._last_notified_packages = set() + self._getPackagesIfLoggedIn() + + def _getPackagesIfLoggedIn(self) -> None: + if self._application.getCuraAPI().account.isLoggedIn: + self._getUserSubscribedPackages() + else: + self._hideSyncMessage() + + def _getUserSubscribedPackages(self) -> None: + self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SYNCING) + url = CloudApiModel.api_url_user_packages + self._application.getHttpRequestManager().get(url, + callback = self._onUserPackagesRequestFinished, + error_callback = self._onUserPackagesRequestFinished, + timeout = 10, + scope = self._scope) + + def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None: + if error is not None or HttpRequestManager.safeHttpStatus(reply) != 200: + Logger.log("w", + "Requesting user packages failed, response code %s while trying to connect to %s", + HttpRequestManager.safeHttpStatus(reply), reply.url()) + self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR) + return + + try: + json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) + # Check for errors: + if "errors" in json_data: + for error in json_data["errors"]: + Logger.log("e", "%s", error["title"]) + self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.ERROR) + return + self._handleCompatibilityData(json_data["data"]) + except json.decoder.JSONDecodeError: + Logger.log("w", "Received invalid JSON for user subscribed packages from the Web Marketplace") + + self._application.getCuraAPI().account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS) + + def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None: + user_subscribed_packages = {plugin["package_id"] for plugin in subscribed_packages_payload} + user_installed_packages = self._package_manager.getAllInstalledPackageIDs() + + # We need to re-evaluate the dismissed packages + # (i.e. some package might got updated to the correct SDK version in the meantime, + # hence remove them from the Dismissed Incompatible list) + self._package_manager.reEvaluateDismissedPackages(subscribed_packages_payload, self._sdk_version) + user_dismissed_packages = self._package_manager.getDismissedPackages() + if user_dismissed_packages: + user_installed_packages.update(user_dismissed_packages) + + # We check if there are packages installed in Web Marketplace but not in Cura marketplace + package_discrepancy = list(user_subscribed_packages.difference(user_installed_packages)) + + if user_subscribed_packages != self._last_notified_packages: + # scenario: + # 1. user subscribes to a package + # 2. dismisses the license/unsubscribes + # 3. subscribes to the same package again + # in this scenario we want to notify the user again. To capture that there was a change during + # step 2, we clear the last_notified after step 2. This way, the user will be notified after + # step 3 even though the list of packages for step 1 and 3 are equal + self._last_notified_packages = set() + + if package_discrepancy: + account = self._application.getCuraAPI().account + account.setUpdatePackagesAction(lambda: self._onSyncButtonClicked(None, None)) + + if user_subscribed_packages == self._last_notified_packages: + # already notified user about these + return + + Logger.log("d", "Discrepancy found between Cloud subscribed packages and Cura installed packages") + self._model.addDiscrepancies(package_discrepancy) + self._model.initialize(self._package_manager, subscribed_packages_payload) + self._showSyncMessage() + self._last_notified_packages = user_subscribed_packages + + def _showSyncMessage(self) -> None: + """Show the message if it is not already shown""" + + if self._message is not None: + self._message.show() + return + + 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", )) + sync_message.addAction("sync", + name = self._i18n_catalog.i18nc("@action:button", "Sync"), + icon = "", + description = "Sync your plugins and print profiles to Ultimaker Cura.", + button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) + sync_message.actionTriggered.connect(self._onSyncButtonClicked) + sync_message.show() + self._message = sync_message + + def _hideSyncMessage(self) -> None: + """Hide the message if it is showing""" + + if self._message is not None: + self._message.hide() + self._message = None + + def _onSyncButtonClicked(self, sync_message: Optional[Message], sync_message_action: Optional[str]) -> None: + if sync_message is not None: + sync_message.hide() + self._hideSyncMessage() # Should be the same message, but also sets _message to None + self.discrepancies.emit(self._model) diff --git a/plugins/Marketplace/CloudSync/DiscrepanciesPresenter.py b/plugins/Marketplace/CloudSync/DiscrepanciesPresenter.py new file mode 100644 index 0000000000..182cc75929 --- /dev/null +++ b/plugins/Marketplace/CloudSync/DiscrepanciesPresenter.py @@ -0,0 +1,44 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os +from typing import Optional + +from PyQt6.QtCore import QObject + +from UM.Qt.QtApplication import QtApplication +from UM.Signal import Signal +from .SubscribedPackagesModel import SubscribedPackagesModel + + +class DiscrepanciesPresenter(QObject): + """Shows a list of packages to be added or removed. The user can select which packages to (un)install. The user's + + choices are emitted on the `packageMutations` Signal. + """ + + def __init__(self, app: QtApplication) -> None: + super().__init__() + + self.packageMutations = Signal() # Emits SubscribedPackagesModel + + self._app = app + self._package_manager = app.getPackageManager() + self._dialog: Optional[QObject] = None + self._compatibility_dialog_path = "resources/qml/CompatibilityDialog.qml" + + def present(self, plugin_path: str, model: SubscribedPackagesModel) -> None: + path = os.path.join(plugin_path, self._compatibility_dialog_path) + self._dialog = self._app.createQmlComponent(path, {"subscribedPackagesModel": model, "handler": self}) + assert self._dialog + self._dialog.accepted.connect(lambda: self._onConfirmClicked(model)) + + def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None: + # If there are incompatible packages - automatically dismiss them + if model.getIncompatiblePackages(): + self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages()) + # For now, all compatible packages presented to the user should be installed. + # Later, we might remove items for which the user unselected the package + if model.getCompatiblePackages(): + model.setItems(model.getCompatiblePackages()) + self.packageMutations.emit(model) diff --git a/plugins/Marketplace/CloudSync/DownloadPresenter.py b/plugins/Marketplace/CloudSync/DownloadPresenter.py new file mode 100644 index 0000000000..8325c27eb7 --- /dev/null +++ b/plugins/Marketplace/CloudSync/DownloadPresenter.py @@ -0,0 +1,153 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import tempfile +from typing import Dict, List, Any + +from PyQt6.QtNetwork import QNetworkReply + +from UM.i18n import i18nCatalog +from UM.Logger import Logger +from UM.Message import Message +from UM.Signal import Signal +from UM.TaskManagement.HttpRequestManager import HttpRequestManager +from cura.CuraApplication import CuraApplication +from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope +from .SubscribedPackagesModel import SubscribedPackagesModel + +i18n_catalog = i18nCatalog("cura") + + +class DownloadPresenter: + """Downloads a set of packages from the Ultimaker Cloud Marketplace + + use download() exactly once: should not be used for multiple sets of downloads since this class contains state + """ + + DISK_WRITE_BUFFER_SIZE = 256 * 1024 # 256 KB + + def __init__(self, app: CuraApplication) -> None: + # Emits (Dict[str, str], List[str]) # (success_items, error_items) + # Dict{success_package_id, temp_file_path} + # List[errored_package_id] + self.done = Signal() + + self._app = app + self._scope = UltimakerCloudScope(app) + + self._started = False + self._progress_message = self._createProgressMessage() + self._progress: Dict[str, Dict[str, Any]] = {} + self._error: List[str] = [] + + def download(self, model: SubscribedPackagesModel) -> None: + if self._started: + Logger.error("Download already started. Create a new %s instead", self.__class__.__name__) + return + + manager = HttpRequestManager.getInstance() + for item in model.items: + package_id = item["package_id"] + + def finishedCallback(reply: QNetworkReply, pid = package_id) -> None: + self._onFinished(pid, reply) + + def progressCallback(rx: int, rt: int, pid = package_id) -> None: + self._onProgress(pid, rx, rt) + + def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, pid = package_id) -> None: + self._onError(pid) + + request_data = manager.get( + item["download_url"], + callback = finishedCallback, + download_progress_callback = progressCallback, + error_callback = errorCallback, + scope = self._scope) + + self._progress[package_id] = { + "received": 0, + "total": 1, # make sure this is not considered done yet. Also divByZero-safe + "file_written": None, + "request_data": request_data, + "package_model": item + } + + self._started = True + self._progress_message.show() + + def abort(self) -> None: + manager = HttpRequestManager.getInstance() + for item in self._progress.values(): + manager.abortRequest(item["request_data"]) + + # Aborts all current operations and returns a copy with the same settings such as app and scope + def resetCopy(self) -> "DownloadPresenter": + self.abort() + self.done.disconnectAll() + return DownloadPresenter(self._app) + + def _createProgressMessage(self) -> Message: + return Message(i18n_catalog.i18nc("@info:generic", "Syncing..."), + lifetime = 0, + use_inactivity_timer = False, + progress = 0.0, + 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"] + + try: + with tempfile.NamedTemporaryFile(mode = "wb+", suffix = ".curapackage", delete = False) as temp_file: + bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE) + while bytes_read: + temp_file.write(bytes_read) + bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE) + self._app.processEvents() + self._progress[package_id]["file_written"] = temp_file.name + except IOError as e: + Logger.logException("e", "Failed to write downloaded package to temp file", e) + self._onError(package_id) + temp_file.close() + + self._checkDone() + + def _onProgress(self, package_id: str, rx: int, rt: int) -> None: + self._progress[package_id]["received"] = rx + self._progress[package_id]["total"] = rt + + received = 0 + total = 0 + for item in self._progress.values(): + received += item["received"] + total += item["total"] + + if total == 0: # Total download size is 0, or unknown, or there are no progress items at all. + self._progress_message.setProgress(100.0) + return + + self._progress_message.setProgress(100.0 * (received / total)) # [0 .. 100] % + + def _onError(self, package_id: str) -> None: + self._progress.pop(package_id) + self._error.append(package_id) + self._checkDone() + + def _checkDone(self) -> bool: + for item in self._progress.values(): + if not item["file_written"]: + return False + + success_items = { + package_id: + { + "package_path": value["file_written"], + "icon_url": value["package_model"]["icon_url"] + } + for package_id, value in self._progress.items() + } + error_items = [package_id for package_id in self._error] + + self._progress_message.hide() + self.done.emit(success_items, error_items) + return True diff --git a/plugins/Marketplace/CloudSync/LicenseModel.py b/plugins/Marketplace/CloudSync/LicenseModel.py new file mode 100644 index 0000000000..777ecca1e9 --- /dev/null +++ b/plugins/Marketplace/CloudSync/LicenseModel.py @@ -0,0 +1,80 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal +from UM.i18n import i18nCatalog + +catalog = i18nCatalog("cura") + + +# Model for the ToolboxLicenseDialog +class LicenseModel(QObject): + DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline") + ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree") + + dialogTitleChanged = pyqtSignal() + packageNameChanged = pyqtSignal() + licenseTextChanged = pyqtSignal() + iconChanged = pyqtSignal() + + def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT, parent = None) -> None: + super().__init__(parent) + + self._current_page_idx = 0 + self._page_count = 1 + self._dialogTitle = "" + self._license_text = "" + self._package_name = "" + self._icon_url = "" + self._decline_button_text = decline_button_text + + @pyqtProperty(str, constant = True) + def acceptButtonText(self): + return self.ACCEPT_BUTTON_TEXT + + @pyqtProperty(str, constant = True) + def declineButtonText(self): + return self._decline_button_text + + @pyqtProperty(str, notify=dialogTitleChanged) + def dialogTitle(self) -> str: + return self._dialogTitle + + @pyqtProperty(str, notify=packageNameChanged) + def packageName(self) -> str: + return self._package_name + + def setPackageName(self, name: str) -> None: + self._package_name = name + self.packageNameChanged.emit() + + @pyqtProperty(str, notify=iconChanged) + def iconUrl(self) -> str: + return self._icon_url + + def setIconUrl(self, url: str): + self._icon_url = url + self.iconChanged.emit() + + @pyqtProperty(str, notify=licenseTextChanged) + def licenseText(self) -> str: + return self._license_text + + def setLicenseText(self, license_text: str) -> None: + if self._license_text != license_text: + self._license_text = license_text + self.licenseTextChanged.emit() + + def setCurrentPageIdx(self, idx: int) -> None: + self._current_page_idx = idx + self._updateDialogTitle() + + def setPageCount(self, count: int) -> None: + self._page_count = count + self._updateDialogTitle() + + def _updateDialogTitle(self): + self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement") + if self._page_count > 1: + self._dialogTitle = self._dialogTitle + " ({}/{})".format(self._current_page_idx + 1, self._page_count) + self.dialogTitleChanged.emit() diff --git a/plugins/Marketplace/CloudSync/LicensePresenter.py b/plugins/Marketplace/CloudSync/LicensePresenter.py new file mode 100644 index 0000000000..aecf70e061 --- /dev/null +++ b/plugins/Marketplace/CloudSync/LicensePresenter.py @@ -0,0 +1,139 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os +from typing import Dict, Optional, List, Any + +from PyQt6.QtCore import QObject, pyqtSlot + +from UM.Logger import Logger +from UM.PackageManager import PackageManager +from UM.Signal import Signal +from cura.CuraApplication import CuraApplication +from UM.i18n import i18nCatalog + +from .LicenseModel import LicenseModel + + +class LicensePresenter(QObject): + """Presents licenses for a set of packages for the user to accept or reject. + + Call present() exactly once to show a licenseDialog for a set of packages + Before presenting another set of licenses, create a new instance using resetCopy(). + + licenseAnswers emits a list of Dicts containing answers when the user has made a choice for all provided packages. + """ + + def __init__(self, app: CuraApplication) -> None: + super().__init__() + self._presented = False + """Whether present() has been called and state is expected to be initialized""" + + self._dialog: Optional[QObject] = None + self._package_manager: PackageManager = app.getPackageManager() + # Emits List[Dict[str, [Any]] containing for example + # [{ "package_id": "BarbarianPlugin", "package_path" : "/tmp/dg345as", "accepted" : True }] + self.licenseAnswers = Signal() + + self._current_package_idx = 0 + self._package_models: List[Dict] = [] + + self._catalog = i18nCatalog("cura") + decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account") + self._license_model: LicenseModel = LicenseModel(decline_button_text=decline_button_text) + self._page_count = 0 + + self._app = app + + self._compatibility_dialog_path = "resources/qml/MultipleLicenseDialog.qml" + + def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None: + """Show a license dialog for multiple packages where users can read a license and accept or decline them + + :param plugin_path: Root directory of the Toolbox plugin + :param packages: Dict[package id, file path] + """ + if self._presented: + Logger.error("{clazz} is single-use. Create a new {clazz} instead", clazz=self.__class__.__name__) + return + + path = os.path.join(plugin_path, self._compatibility_dialog_path) + + self._initState(packages) + + if self._page_count == 0: + self.licenseAnswers.emit(self._package_models) + return + + if self._dialog is None: + context_properties = { + "licenseModel": self._license_model, + "handler": self + } + self._dialog = self._app.createQmlComponent(path, context_properties) + self._presentCurrentPackage() + self._presented = True + + def resetCopy(self) -> "LicensePresenter": + """Clean up and return a new copy with the same settings such as app""" + if self._dialog: + self._dialog.close() + self.licenseAnswers.disconnectAll() + return LicensePresenter(self._app) + + @pyqtSlot() + def onLicenseAccepted(self) -> None: + self._package_models[self._current_package_idx]["accepted"] = True + self._checkNextPage() + + @pyqtSlot() + def onLicenseDeclined(self) -> None: + self._package_models[self._current_package_idx]["accepted"] = False + self._checkNextPage() + + def _initState(self, packages: Dict[str, Dict[str, Any]]) -> None: + implicitly_accepted_count = 0 + + for package_id, item in packages.items(): + item["package_id"] = package_id + try: + item["licence_content"] = self._package_manager.getPackageLicense(item["package_path"]) + except EnvironmentError as e: + Logger.error(f"Could not open downloaded package {package_id} to read license file! {type(e)} - {e}") + continue # Skip this package. + if item["licence_content"] is None: + # Implicitly accept when there is no license + item["accepted"] = True + implicitly_accepted_count = implicitly_accepted_count + 1 + self._package_models.append(item) + else: + item["accepted"] = None #: None: no answer yet + # When presenting the packages, we want to show packages which have a license first. + # In fact, we don't want to show the others at all because they are implicitly accepted + self._package_models.insert(0, item) + CuraApplication.getInstance().processEvents() + self._page_count = len(self._package_models) - implicitly_accepted_count + self._license_model.setPageCount(self._page_count) + + def _presentCurrentPackage(self) -> None: + package_model = self._package_models[self._current_package_idx] + package_info = self._package_manager.getPackageInfo(package_model["package_path"]) + + self._license_model.setCurrentPageIdx(self._current_package_idx) + self._license_model.setPackageName(package_info["display_name"]) + self._license_model.setIconUrl(package_model["icon_url"]) + self._license_model.setLicenseText(package_model["licence_content"]) + if self._dialog: + self._dialog.open() # Does nothing if already open + + def _checkNextPage(self) -> None: + if self._current_package_idx + 1 < self._page_count: + self._current_package_idx += 1 + self._presentCurrentPackage() + else: + if self._dialog: + self._dialog.close() + self.licenseAnswers.emit(self._package_models) + + + diff --git a/plugins/Marketplace/CloudSync/RestartApplicationPresenter.py b/plugins/Marketplace/CloudSync/RestartApplicationPresenter.py new file mode 100644 index 0000000000..1e1ebc33dc --- /dev/null +++ b/plugins/Marketplace/CloudSync/RestartApplicationPresenter.py @@ -0,0 +1,35 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM import i18nCatalog +from UM.Message import Message +from cura.CuraApplication import CuraApplication + + +class RestartApplicationPresenter: + """Presents a dialog telling the user that a restart is required to apply changes + + Since we cannot restart Cura, the app is closed instead when the button is clicked + """ + def __init__(self, app: CuraApplication) -> None: + self._app = app + self._i18n_catalog = i18nCatalog("cura") + + def present(self) -> None: + app_name = self._app.getApplicationDisplayName() + + message = Message(self._i18n_catalog.i18nc("@info:generic", + "You need to quit and restart {} before changes have effect.", + app_name)) + + message.addAction("quit", + name="Quit " + app_name, + icon = "", + description="Close the application", + button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) + + message.actionTriggered.connect(self._quitClicked) + message.show() + + def _quitClicked(self, *_): + self._app.windowClosed() diff --git a/plugins/Marketplace/CloudSync/SubscribedPackagesModel.py b/plugins/Marketplace/CloudSync/SubscribedPackagesModel.py new file mode 100644 index 0000000000..96b1d58bbf --- /dev/null +++ b/plugins/Marketplace/CloudSync/SubscribedPackagesModel.py @@ -0,0 +1,74 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt6.QtCore import Qt, pyqtProperty + +from UM.PackageManager import PackageManager +from UM.Qt.ListModel import ListModel +from UM.Version import Version + +from cura import ApplicationMetadata +from typing import List, Dict, Any + + +class SubscribedPackagesModel(ListModel): + def __init__(self, parent = None): + super().__init__(parent) + + self._items = [] + self._metadata = None + self._discrepancies = None + self._sdk_version = ApplicationMetadata.CuraSDKVersion + + self.addRoleName(Qt.ItemDataRole.UserRole + 1, "package_id") + self.addRoleName(Qt.ItemDataRole.UserRole + 2, "display_name") + self.addRoleName(Qt.ItemDataRole.UserRole + 3, "icon_url") + self.addRoleName(Qt.ItemDataRole.UserRole + 4, "is_compatible") + self.addRoleName(Qt.ItemDataRole.UserRole + 5, "is_dismissed") + + @pyqtProperty(bool, constant=True) + def hasCompatiblePackages(self) -> bool: + for item in self._items: + if item['is_compatible']: + return True + return False + + @pyqtProperty(bool, constant=True) + def hasIncompatiblePackages(self) -> bool: + for item in self._items: + if not item['is_compatible']: + return True + return False + + def addDiscrepancies(self, discrepancy: List[str]) -> None: + self._discrepancies = discrepancy + + def getCompatiblePackages(self) -> List[Dict[str, Any]]: + return [package for package in self._items if package["is_compatible"]] + + def getIncompatiblePackages(self) -> List[str]: + return [package["package_id"] for package in self._items if not package["is_compatible"]] + + def initialize(self, package_manager: PackageManager, subscribed_packages_payload: List[Dict[str, Any]]) -> None: + self._items.clear() + for item in subscribed_packages_payload: + if item["package_id"] not in self._discrepancies: + continue + package = { + "package_id": item["package_id"], + "display_name": item["display_name"], + "sdk_versions": item["sdk_versions"], + "download_url": item["download_url"], + "md5_hash": item["md5_hash"], + "is_dismissed": False, + } + + compatible = any(package_manager.isPackageCompatible(Version(version)) for version in item["sdk_versions"]) + package.update({"is_compatible": compatible}) + + try: + package.update({"icon_url": item["icon_url"]}) + except KeyError: # There is no 'icon_url" in the response payload for this package + package.update({"icon_url": ""}) + self._items.append(package) + self.setItems(self._items) diff --git a/plugins/Marketplace/CloudSync/SyncOrchestrator.py b/plugins/Marketplace/CloudSync/SyncOrchestrator.py new file mode 100644 index 0000000000..a68d3c8fa2 --- /dev/null +++ b/plugins/Marketplace/CloudSync/SyncOrchestrator.py @@ -0,0 +1,114 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os +from typing import List, Dict, Any, cast + +from UM import i18n_catalog +from UM.Extension import Extension +from UM.Logger import Logger +from UM.Message import Message +from UM.PluginRegistry import PluginRegistry +from cura.CuraApplication import CuraApplication +from .CloudPackageChecker import CloudPackageChecker +from .CloudApiClient import CloudApiClient +from .DiscrepanciesPresenter import DiscrepanciesPresenter +from .DownloadPresenter import DownloadPresenter +from .LicensePresenter import LicensePresenter +from .RestartApplicationPresenter import RestartApplicationPresenter +from .SubscribedPackagesModel import SubscribedPackagesModel + + +class SyncOrchestrator(Extension): + """Orchestrates the synchronizing of packages from the user account to the installed packages + + Example flow: + + - CloudPackageChecker compares a list of packages the user `subscribed` to in their account + If there are `discrepancies` between the account and locally installed packages, they are emitted + - DiscrepanciesPresenter shows a list of packages to be added or removed to the user. It emits the `packageMutations` + the user selected to be performed + - The SyncOrchestrator uses PackageManager to remove local packages the users wants to see removed + - The DownloadPresenter shows a download progress dialog. It emits A tuple of succeeded and failed downloads + - The LicensePresenter extracts licenses from the downloaded packages and presents a license for each package to + be installed. It emits the `licenseAnswers` signal for accept or declines + - The CloudApiClient removes the declined packages from the account + - The SyncOrchestrator uses PackageManager to install the downloaded packages and delete temp files. + - The RestartApplicationPresenter notifies the user that a restart is required for changes to take effect + """ + + def __init__(self, app: CuraApplication) -> None: + super().__init__() + # Differentiate This PluginObject from the Marketplace. self.getId() includes _name. + # getPluginId() will return the same value for The Marketplace extension and this one + self._name = "SyncOrchestrator" + + self._package_manager = app.getPackageManager() + # Keep a reference to the CloudApiClient. it watches for installed packages and subscribes to them + self._cloud_api: CloudApiClient = CloudApiClient.getInstance(app) + + self._checker: CloudPackageChecker = CloudPackageChecker(app) + self._checker.discrepancies.connect(self._onDiscrepancies) + + self._discrepancies_presenter: DiscrepanciesPresenter = DiscrepanciesPresenter(app) + self._discrepancies_presenter.packageMutations.connect(self._onPackageMutations) + + self._download_presenter: DownloadPresenter = DownloadPresenter(app) + + self._license_presenter: LicensePresenter = LicensePresenter(app) + self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers) + + self._restart_presenter = RestartApplicationPresenter(app) + + def _onDiscrepancies(self, model: SubscribedPackagesModel) -> None: + plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId())) + self._discrepancies_presenter.present(plugin_path, model) + + def _onPackageMutations(self, mutations: SubscribedPackagesModel) -> None: + self._download_presenter = self._download_presenter.resetCopy() + self._download_presenter.done.connect(self._onDownloadFinished) + self._download_presenter.download(mutations) + + def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None: + """Called when a set of packages have finished downloading + + :param success_items:: Dict[package_id, Dict[str, str]] + :param error_items:: List[package_id] + """ + if error_items: + message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items))) + self._showErrorMessage(message) + + plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath(self.getPluginId())) + self._license_presenter = self._license_presenter.resetCopy() + self._license_presenter.licenseAnswers.connect(self._onLicenseAnswers) + self._license_presenter.present(plugin_path, success_items) + + # Called when user has accepted / declined all licenses for the downloaded packages + def _onLicenseAnswers(self, answers: List[Dict[str, Any]]) -> None: + has_changes = False # True when at least one package is installed + + for item in answers: + if item["accepted"]: + # install and subscribe packages + if not self._package_manager.installPackage(item["package_path"]): + message = "Could not install {}".format(item["package_id"]) + self._showErrorMessage(message) + continue + has_changes = True + else: + self._cloud_api.unsubscribe(item["package_id"]) + # delete temp file + try: + os.remove(item["package_path"]) + except EnvironmentError as e: # File was already removed, no access rights, etc. + Logger.error("Can't delete temporary package file: {err}".format(err = str(e))) + + if has_changes: + self._restart_presenter.present() + + def _showErrorMessage(self, text: str): + """Logs an error and shows it to the user""" + + Logger.error(text) + Message(text, lifetime = 0, message_type = Message.MessageType.ERROR).show() diff --git a/plugins/Marketplace/CloudSync/__init__.py b/plugins/Marketplace/CloudSync/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/Marketplace/LocalPackageList.py b/plugins/Marketplace/LocalPackageList.py index a609e72d33..ae719b2699 100644 --- a/plugins/Marketplace/LocalPackageList.py +++ b/plugins/Marketplace/LocalPackageList.py @@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, QObject +from PyQt6.QtCore import pyqtSlot, QObject from UM.Version import Version from UM.i18n import i18nCatalog @@ -15,8 +15,8 @@ from .PackageModel import PackageModel from .Constants import PACKAGE_UPDATES_URL if TYPE_CHECKING: - from PyQt5.QtCore import QObject - from PyQt5.QtNetwork import QNetworkReply + from PyQt6.QtCore import QObject + from PyQt6.QtNetwork import QNetworkReply catalog = i18nCatalog("cura") diff --git a/plugins/Marketplace/Marketplace.py b/plugins/Marketplace/Marketplace.py index b9632e5fb0..171e3f915b 100644 --- a/plugins/Marketplace/Marketplace.py +++ b/plugins/Marketplace/Marketplace.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from typing import Optional, cast from cura.CuraApplication import CuraApplication # Creating QML objects and managing packages. diff --git a/plugins/Marketplace/PackageList.py b/plugins/Marketplace/PackageList.py index 04b602002c..30162afe6c 100644 --- a/plugins/Marketplace/PackageList.py +++ b/plugins/Marketplace/PackageList.py @@ -4,7 +4,7 @@ import tempfile import json import os.path -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, Qt +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, Qt from typing import cast, Dict, Optional, Set, TYPE_CHECKING from UM.i18n import i18nCatalog @@ -22,8 +22,8 @@ from .PackageModel import PackageModel from .Constants import USER_PACKAGES_URL, PACKAGES_URL if TYPE_CHECKING: - from PyQt5.QtCore import QObject - from PyQt5.QtNetwork import QNetworkReply + from PyQt6.QtCore import QObject + from PyQt6.QtNetwork import QNetworkReply catalog = i18nCatalog("cura") @@ -32,7 +32,7 @@ class PackageList(ListModel): """ A List model for Packages, this class serves as parent class for more detailed implementations. such as Packages obtained from Remote or Local source """ - PackageRole = Qt.UserRole + 1 + PackageRole = Qt.ItemDataRole.UserRole + 1 DISK_WRITE_BUFFER_SIZE = 256 * 1024 # 256 KB def __init__(self, parent: Optional["QObject"] = None) -> None: diff --git a/plugins/Marketplace/PackageModel.py b/plugins/Marketplace/PackageModel.py index 29c8abe653..bd1b0681fc 100644 --- a/plugins/Marketplace/PackageModel.py +++ b/plugins/Marketplace/PackageModel.py @@ -5,8 +5,8 @@ import re from enum import Enum from typing import Any, cast, Dict, List, Optional -from PyQt5.QtCore import pyqtProperty, QObject, pyqtSignal, pyqtSlot -from PyQt5.QtQml import QQmlEngine +from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal, pyqtSlot +from PyQt6.QtQml import QQmlEngine from cura.CuraApplication import CuraApplication from cura.CuraPackageManager import CuraPackageManager @@ -31,7 +31,7 @@ class PackageModel(QObject): :param parent: The parent QML object that controls the lifetime of this model (normally a PackageList). """ super().__init__(parent) - QQmlEngine.setObjectOwnership(self, QQmlEngine.CppOwnership) + QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) self._package_manager: CuraPackageManager = cast(CuraPackageManager, CuraApplication.getInstance().getPackageManager()) self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry() diff --git a/plugins/Marketplace/RemotePackageList.py b/plugins/Marketplace/RemotePackageList.py index 4c82f2b29a..c20efabbc2 100644 --- a/plugins/Marketplace/RemotePackageList.py +++ b/plugins/Marketplace/RemotePackageList.py @@ -1,8 +1,8 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot +from PyQt6.QtNetwork import QNetworkReply from typing import Optional, TYPE_CHECKING from UM.i18n import i18nCatalog @@ -14,7 +14,7 @@ from .PackageList import PackageList from .PackageModel import PackageModel # The contents of this list. if TYPE_CHECKING: - from PyQt5.QtCore import QObject + from PyQt6.QtCore import QObject catalog = i18nCatalog("cura") @@ -138,9 +138,10 @@ class RemotePackageList(PackageList): :param reply: The reply with packages. This will most likely be incomplete and should be ignored. :param error: The error status of the request. """ - if error == QNetworkReply.NetworkError.OperationCanceledError: + if error == QNetworkReply.NetworkError.OperationCanceledError or error == QNetworkReply.NetworkError.ProtocolUnknownError: Logger.debug("Cancelled request for packages.") self._ongoing_requests["get_packages"] = None + self.setIsLoading(False) return # Don't show an error about this to the user. Logger.error("Could not reach Marketplace server.") self.setErrorMessage(catalog.i18nc("@info:error", "Could not reach Marketplace.")) diff --git a/plugins/Marketplace/__init__.py b/plugins/Marketplace/__init__.py index bd65062ba6..ef1beab33e 100644 --- a/plugins/Marketplace/__init__.py +++ b/plugins/Marketplace/__init__.py @@ -1,6 +1,6 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +from .CloudSync.SyncOrchestrator import SyncOrchestrator from .Marketplace import Marketplace def getMetaData(): @@ -14,4 +14,4 @@ def register(app): """ Register the plug-in object with Uranium. """ - return { "extension": Marketplace() } + return { "extension": [Marketplace(), SyncOrchestrator(app)] } diff --git a/plugins/Marketplace/plugin.json b/plugins/Marketplace/plugin.json index 7eeeb5c986..9cb81461a1 100644 --- a/plugins/Marketplace/plugin.json +++ b/plugins/Marketplace/plugin.json @@ -2,7 +2,7 @@ "name": "Marketplace", "author": "Ultimaker B.V.", "version": "1.0.0", - "api": 7, + "api": 8, "description": "Manages extensions to the application and allows browsing extensions from the Ultimaker website.", "i18n-catalog": "cura" } diff --git a/plugins/Marketplace/resources/images/Plugin.svg b/plugins/Marketplace/resources/images/Plugin.svg index 51356d842c..b7616b5876 100644 --- a/plugins/Marketplace/resources/images/Plugin.svg +++ b/plugins/Marketplace/resources/images/Plugin.svg @@ -1,3 +1,3 @@ - - + + diff --git a/plugins/Marketplace/resources/images/Spool.svg b/plugins/Marketplace/resources/images/Spool.svg index dae9b43030..3faffb7a52 100644 --- a/plugins/Marketplace/resources/images/Spool.svg +++ b/plugins/Marketplace/resources/images/Spool.svg @@ -1,3 +1,3 @@ - - + + diff --git a/plugins/Marketplace/resources/qml/CompatibilityDialog.qml b/plugins/Marketplace/resources/qml/CompatibilityDialog.qml new file mode 100644 index 0000000000..331502a7a7 --- /dev/null +++ b/plugins/Marketplace/resources/qml/CompatibilityDialog.qml @@ -0,0 +1,146 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Marketplace is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.3 + +import UM 1.5 as UM +import Cura 1.6 as Cura + + +UM.Dialog +{ + visible: true + title: catalog.i18nc("@title", "Changes from your account") + width: UM.Theme.getSize("popup_dialog").width + height: UM.Theme.getSize("popup_dialog").height + minimumWidth: width + maximumWidth: minimumWidth + minimumHeight: height + maximumHeight: minimumHeight + margin: 0 + + property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? catalog.i18nc("@button", "Dismiss") : catalog.i18nc("@button", "Next") + + Rectangle + { + id: root + anchors.fill: parent + color: UM.Theme.getColor("main_background") + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + ScrollView + { + width: parent.width + height: parent.height - nextButton.height - nextButton.anchors.margins * 2 // We want some leftover space for the button at the bottom + clip: true + + Column + { + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + + // Compatible packages + UM.Label + { + text: catalog.i18nc("@label", "The following packages will be added:") + visible: subscribedPackagesModel.hasCompatiblePackages + height: contentHeight + UM.Theme.getSize("default_margin").height + } + Repeater + { + model: subscribedPackagesModel + Component + { + Item + { + width: parent.width + property int lineHeight: 60 + visible: model.is_compatible + height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the compatible packages here + Image + { + id: packageIcon + source: model.icon_url || Qt.resolvedUrl("../images/placeholder.svg") + height: lineHeight + width: height + sourceSize.height: height + sourceSize.width: width + mipmap: true + fillMode: Image.PreserveAspectFit + } + UM.Label + { + text: model.display_name + font: UM.Theme.getFont("medium_bold") + anchors.left: packageIcon.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: packageIcon.verticalCenter + elide: Text.ElideRight + } + } + } + } + + // Incompatible packages + UM.Label + { + text: catalog.i18nc("@label", "The following packages can not be installed because of an incompatible Cura version:") + visible: subscribedPackagesModel.hasIncompatiblePackages + height: contentHeight + UM.Theme.getSize("default_margin").height + } + Repeater + { + model: subscribedPackagesModel + Component + { + Item + { + width: parent.width + property int lineHeight: 60 + visible: !model.is_compatible && !model.is_dismissed + height: visible ? (lineHeight + UM.Theme.getSize("default_margin").height) : 0 // We only show the incompatible packages here + Image + { + id: packageIcon + source: model.icon_url || Qt.resolvedUrl("../images/placeholder.svg") + height: lineHeight + width: height + sourceSize.height: height + sourceSize.width: width + mipmap: true + fillMode: Image.PreserveAspectFit + } + UM.Label + { + text: model.display_name + font: UM.Theme.getFont("medium_bold") + anchors.left: packageIcon.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: packageIcon.verticalCenter + elide: Text.ElideRight + } + } + } + } + } + + } // End of ScrollView + + Cura.PrimaryButton + { + id: nextButton + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: UM.Theme.getSize("default_margin").height + text: actionButtonText + onClicked: accept() + } + } +} diff --git a/plugins/Marketplace/resources/qml/LicenseDialog.qml b/plugins/Marketplace/resources/qml/LicenseDialog.qml index 09d7c5b59a..2f3f4ffc12 100644 --- a/plugins/Marketplace/resources/qml/LicenseDialog.qml +++ b/plugins/Marketplace/resources/qml/LicenseDialog.qml @@ -2,7 +2,6 @@ //Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 -import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 @@ -34,7 +33,7 @@ UM.Dialog spacing: UM.Theme.getSize("default_margin").width leftPadding: UM.Theme.getSize("narrow_margin").width - UM.RecolorImage + UM.ColorImage { id: icon width: UM.Theme.getSize("marketplace_large_icon").width @@ -50,7 +49,7 @@ UM.Dialog font: UM.Theme.getFont("large") anchors.verticalCenter: icon.verticalCenter height: UM.Theme.getSize("marketplace_large_icon").height - verticalAlignment: Qt.AlignVCenter + verticalAlignment: Qt.AlignmentFlag.AlignVCenter wrapMode: Text.Wrap renderType: Text.NativeRendering } diff --git a/plugins/Marketplace/resources/qml/ManageButton.qml b/plugins/Marketplace/resources/qml/ManageButton.qml index 36022ffd54..5f4b3dcadd 100644 --- a/plugins/Marketplace/resources/qml/ManageButton.qml +++ b/plugins/Marketplace/resources/qml/ManageButton.qml @@ -46,7 +46,7 @@ Item height: UM.Theme.getSize("action_button").height width: childrenRect.width - UM.RecolorImage + UM.ColorImage { id: busyIndicator visible: parent.visible diff --git a/plugins/Marketplace/resources/qml/ManagePackagesButton.qml b/plugins/Marketplace/resources/qml/ManagePackagesButton.qml index fc9e7d757f..a32de2b809 100644 --- a/plugins/Marketplace/resources/qml/ManagePackagesButton.qml +++ b/plugins/Marketplace/resources/qml/ManagePackagesButton.qml @@ -33,7 +33,7 @@ TabButton visible: root.hovered } - UM.RecolorImage + UM.ColorImage { id: icon diff --git a/plugins/Marketplace/resources/qml/Marketplace.qml b/plugins/Marketplace/resources/qml/Marketplace.qml index 8fcba852bd..887886fd22 100644 --- a/plugins/Marketplace/resources/qml/Marketplace.qml +++ b/plugins/Marketplace/resources/qml/Marketplace.qml @@ -251,7 +251,7 @@ Window margins: UM.Theme.getSize("default_margin").width } spacing: UM.Theme.getSize("default_margin").width - UM.RecolorImage + UM.ColorImage { id: bannerIcon source: UM.Theme.getIcon("Plugin") diff --git a/plugins/Marketplace/resources/qml/MultipleLicenseDialog.qml b/plugins/Marketplace/resources/qml/MultipleLicenseDialog.qml new file mode 100644 index 0000000000..a3d4b60221 --- /dev/null +++ b/plugins/Marketplace/resources/qml/MultipleLicenseDialog.qml @@ -0,0 +1,98 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Marketplace is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import UM 1.5 as UM +import Cura 1.6 as Cura + +UM.Dialog +{ + id: licenseDialog + title: licenseModel.dialogTitle + + minimumWidth: UM.Theme.getSize("modal_window_minimum").width + minimumHeight: UM.Theme.getSize("modal_window_minimum").height + width: minimumWidth + height: minimumHeight + backgroundColor: UM.Theme.getColor("main_background") + margin: UM.Theme.getSize("default_margin").width + + ColumnLayout + { + anchors.fill: parent + spacing: UM.Theme.getSize("thick_margin").height + + UM.I18nCatalog { id: catalog; name: "cura" } + + UM.Label + { + id: licenseHeader + Layout.fillWidth: true + text: catalog.i18nc("@label", "You need to accept the license to install the package") + } + + Row { + id: packageRow + + Layout.fillWidth: true + height: childrenRect.height + spacing: UM.Theme.getSize("default_margin").width + leftPadding: UM.Theme.getSize("narrow_margin").width + + Image + { + id: icon + width: UM.Theme.getSize("card_icon").width + height: width + sourceSize.width: width + sourceSize.height: height + fillMode: Image.PreserveAspectFit + source: licenseModel.iconUrl || Qt.resolvedUrl("../images/placeholder.svg") + mipmap: true + } + + UM.Label + { + id: packageName + text: licenseModel.packageName + + font.bold: true + anchors.verticalCenter: icon.verticalCenter + height: contentHeight + } + } + + Cura.ScrollableTextArea + { + Layout.fillWidth: true + Layout.fillHeight: true + anchors.topMargin: UM.Theme.getSize("default_margin").height + + textArea.text: licenseModel.licenseText + textArea.readOnly: true + } + } + + rightButtons: + [ + Cura.PrimaryButton + { + text: licenseModel.acceptButtonText + onClicked: handler.onLicenseAccepted() + } + ] + + leftButtons: + [ + Cura.SecondaryButton + { + id: declineButton + text: licenseModel.declineButtonText + onClicked: handler.onLicenseDeclined() + } + ] +} diff --git a/plugins/Marketplace/resources/qml/OnboardBanner.qml b/plugins/Marketplace/resources/qml/OnboardBanner.qml index 7d973cb74a..8217a7d03f 100644 --- a/plugins/Marketplace/resources/qml/OnboardBanner.qml +++ b/plugins/Marketplace/resources/qml/OnboardBanner.qml @@ -19,8 +19,7 @@ Rectangle implicitHeight: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height color: UM.Theme.getColor("action_panel_secondary") - // Icon - UM.RecolorImage + UM.ColorImage { id: onboardingIcon anchors @@ -33,7 +32,6 @@ Rectangle height: UM.Theme.getSize("banner_icon_size").height } - // Close button UM.SimpleButton { id: onboardingClose @@ -52,8 +50,8 @@ Rectangle onClicked: onRemove() } - // Body - Label { + UM.Label + { id: infoText anchors { @@ -63,14 +61,10 @@ Rectangle margins: UM.Theme.getSize("default_margin").width } - font: UM.Theme.getFont("default") - - renderType: Text.NativeRendering color: UM.Theme.getColor("primary_text") - wrapMode: Text.Wrap elide: Text.ElideRight - onLineLaidOut: + onLineLaidOut: (line) => { if(line.isLast) { @@ -102,7 +96,7 @@ Rectangle id: readMoreButton anchors.left: infoText.left anchors.bottom: infoText.bottom - text: "Learn More" + text: catalog.i18nc("@button:label", "Learn More") textFont: UM.Theme.getFont("default") textColor: infoText.color leftPadding: 0 diff --git a/plugins/Marketplace/resources/qml/PackageCardHeader.qml b/plugins/Marketplace/resources/qml/PackageCardHeader.qml index 1c39d4b063..9d401c5253 100644 --- a/plugins/Marketplace/resources/qml/PackageCardHeader.qml +++ b/plugins/Marketplace/resources/qml/PackageCardHeader.qml @@ -47,23 +47,21 @@ Item sourceSize.width: width } - UM.RecolorImage + UM.ColorImage { visible: !parent.packageHasIcon anchors.fill: parent - sourceSize.height: height - sourceSize.width: width color: UM.Theme.getColor("text") source: { switch (packageData.packageType) { case "plugin": - return "../images/Plugin.svg"; + return Qt.resolvedUrl("../images/Plugin.svg"); case "material": - return "../images/Spool.svg"; + return Qt.resolvedUrl("../images/Spool.svg"); default: - return "../images/placeholder.svg"; + return Qt.resolvedUrl("../images/placeholder.svg"); } } } @@ -121,9 +119,9 @@ Item topPadding: UM.Theme.getSize("narrow_margin").width bottomPadding: UM.Theme.getSize("narrow_margin").width - Layout.preferredWidth: UM.Theme.getSize("card_tiny_icon").width + 2 * padding - Layout.preferredHeight: UM.Theme.getSize("card_tiny_icon").width + 2 * padding - contentItem: UM.RecolorImage + width: UM.Theme.getSize("card_tiny_icon").width + 2 * padding + height: UM.Theme.getSize("card_tiny_icon").width + 2 * padding + contentItem: UM.ColorImage { source: UM.Theme.getIcon("LinkExternal") color: UM.Theme.getColor("icon") diff --git a/plugins/Marketplace/resources/qml/PackageDetails.qml b/plugins/Marketplace/resources/qml/PackageDetails.qml index 6e5ec0fbf5..218669d81e 100644 --- a/plugins/Marketplace/resources/qml/PackageDetails.qml +++ b/plugins/Marketplace/resources/qml/PackageDetails.qml @@ -31,7 +31,7 @@ Item Cura.SecondaryButton { - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter Layout.preferredHeight: UM.Theme.getSize("action_button").height Layout.preferredWidth: height @@ -47,7 +47,7 @@ Item Label { - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter Layout.fillWidth: true text: detailPage.title @@ -93,4 +93,4 @@ Item } } } -} \ No newline at end of file +} diff --git a/plugins/Marketplace/resources/qml/PackagePage.qml b/plugins/Marketplace/resources/qml/PackagePage.qml index e590ee6091..455b2b8d5d 100644 --- a/plugins/Marketplace/resources/qml/PackagePage.qml +++ b/plugins/Marketplace/resources/qml/PackagePage.qml @@ -46,7 +46,7 @@ Rectangle // But we re-use the package page for the manage plugins as well. The one user that doesn't see // the num downloads is an acceptable "sacrifice" to make this easy to fix. visible: packageData.downloadCount != "0" - UM.RecolorImage + UM.ColorImage { id: downloadsIcon width: UM.Theme.getSize("card_tiny_icon").width diff --git a/plugins/Marketplace/resources/qml/Packages.qml b/plugins/Marketplace/resources/qml/Packages.qml index 56fffe036e..74a18bccd3 100644 --- a/plugins/Marketplace/resources/qml/Packages.qml +++ b/plugins/Marketplace/resources/qml/Packages.qml @@ -1,9 +1,10 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.15 import QtQuick.Controls 2.15 -import UM 1.4 as UM + +import UM 1.6 as UM ListView @@ -51,23 +52,7 @@ ListView } } - ScrollBar.vertical: ScrollBar - { - // Vertical ScrollBar, styled similarly to the scrollBar in the settings panel - id: verticalScrollBar - visible: packages.contentHeight > packages.height - anchors.right: parent.right - background: Item {} - - contentItem: Rectangle - { - id: scrollViewHandle - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: Math.round(implicitWidth / 2) - color: verticalScrollBar.pressed ? UM.Theme.getColor("scrollbar_handle_down") : verticalScrollBar.hovered ? UM.Theme.getColor("scrollbar_handle_hover") : UM.Theme.getColor("scrollbar_handle") - Behavior on color { ColorAnimation { duration: 50; } } - } - } + ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar } delegate: MouseArea { @@ -213,7 +198,7 @@ ListView status: UM.StatusIcon.Status.ERROR visible: false } - UM.RecolorImage + UM.ColorImage { id: loadMoreIcon anchors.fill: parent diff --git a/plugins/Marketplace/resources/qml/VerifiedIcon.qml b/plugins/Marketplace/resources/qml/VerifiedIcon.qml index 6b9d7b6017..e701c5a91b 100644 --- a/plugins/Marketplace/resources/qml/VerifiedIcon.qml +++ b/plugins/Marketplace/resources/qml/VerifiedIcon.qml @@ -32,7 +32,7 @@ Control anchors.fill: parent color: UM.Theme.getColor("action_button_hovered") radius: width - UM.RecolorImage + UM.ColorImage { anchors.fill: parent color: UM.Theme.getColor("primary") diff --git a/plugins/ModelChecker/ModelChecker.py b/plugins/ModelChecker/ModelChecker.py index 321ce8d007..b24c40546d 100644 --- a/plugins/ModelChecker/ModelChecker.py +++ b/plugins/ModelChecker/ModelChecker.py @@ -3,7 +3,7 @@ import os -from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty, QTimer +from PyQt6.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty, QTimer from UM.Application import Application from UM.Extension import Extension diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 41c49c9a25..f734aff2b1 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import UM 1.2 as UM +import UM 1.5 as UM UM.SimpleButton @@ -14,7 +14,7 @@ UM.SimpleButton width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height - iconSource: "model_checker.svg" + iconSource: Qt.resolvedUrl("model_checker.svg") anchors.verticalCenter: parent ? parent.verticalCenter : undefined color: UM.Theme.getColor("text_scene") hoverColor: UM.Theme.getColor("text_scene_hover") diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index bf546879c7..39d71e30e0 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -2,7 +2,7 @@ "name": "Model Checker", "author": "Ultimaker B.V.", "version": "1.0.1", - "api": 7, + "api": 8, "description": "Checks models and print configuration for possible printing issues and give suggestions.", "i18n-catalog": "cura" } diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index aa717ce95c..8992606d90 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -123,7 +123,7 @@ Rectangle visible: !isNetworkConfigured && isNetworkConfigurable width: childrenRect.width - UM.RecolorImage + UM.ColorImage { id: externalLinkIcon anchors.verticalCenter: parent.verticalCenter diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json index bcf42763dc..b69ac3469d 100644 --- a/plugins/MonitorStage/plugin.json +++ b/plugins/MonitorStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a monitor stage in Cura.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py index 9fca618322..15f37e499d 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -1,7 +1,7 @@ # Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty +from PyQt6.QtCore import pyqtProperty from UM.FlameProfiler import pyqtSlot from UM.Application import Application diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index a53f051db8..2d4b3e01e5 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -79,7 +79,7 @@ Item { id: normalButton text: catalog.i18nc("@label", "Normal model") - toolItem: UM.RecolorImage + toolItem: UM.ColorImage { source: UM.Theme.getIcon("Infill0") color: UM.Theme.getColor("icon") @@ -94,7 +94,7 @@ Item { id: supportMeshButton text: catalog.i18nc("@label", "Print as support") - toolItem: UM.RecolorImage + toolItem: UM.ColorImage { source: UM.Theme.getIcon("MeshTypeSupport") color: UM.Theme.getColor("icon") @@ -109,7 +109,7 @@ Item { id: overlapMeshButton text: catalog.i18nc("@label", "Modify settings for overlaps") - toolItem: UM.RecolorImage + toolItem: UM.ColorImage { source: UM.Theme.getIcon("MeshTypeIntersect") color: UM.Theme.getColor("icon") @@ -124,7 +124,7 @@ Item { id: antiOverhangMeshButton text: catalog.i18nc("@label", "Don't support overlaps") - toolItem: UM.RecolorImage + toolItem: UM.ColorImage { source: UM.Theme.getIcon("BlockSupportOverlaps") color: UM.Theme.getColor("icon") @@ -196,7 +196,7 @@ Item height: parent.height width: UM.Theme.getSize("setting").width + UM.Theme.getSize("default_margin").width - ScrollBar.vertical: UM.ScrollBar {} + ScrollBar.vertical: UM.ScrollBar { id: scrollBar } clip: true spacing: UM.Theme.getSize("default_lining").height @@ -240,11 +240,11 @@ Item delegate: Row { - spacing: - UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").width Loader { id: settingLoader - width: UM.Theme.getSize("setting").width + width: UM.Theme.getSize("setting").width - removeButton.width - scrollBar.width height: UM.Theme.getSize("section").height + UM.Theme.getSize("narrow_margin").height enabled: provider.properties.enabled === "True" property var definition: model @@ -297,19 +297,19 @@ Item Button { - width: Math.round(UM.Theme.getSize("setting").height / 2) - height: UM.Theme.getSize("setting").height + id: removeButton + width: UM.Theme.getSize("setting").height + height: UM.Theme.getSize("setting").height + UM.Theme.getSize("narrow_margin").height onClicked: addedSettingsModel.setVisible(model.key, false) background: Item { - UM.RecolorImage + UM.ColorImage { anchors.verticalCenter: parent.verticalCenter width: parent.width height: width - sourceSize.height: width color: parent.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") source: UM.Theme.getIcon("Minus") } diff --git a/plugins/PerObjectSettingsTool/__init__.py b/plugins/PerObjectSettingsTool/__init__.py index d3c6d236ef..9c3e5f31de 100644 --- a/plugins/PerObjectSettingsTool/__init__.py +++ b/plugins/PerObjectSettingsTool/__init__.py @@ -3,7 +3,7 @@ from . import PerObjectSettingsTool from . import PerObjectSettingVisibilityHandler -from PyQt5.QtQml import qmlRegisterType +from PyQt6.QtQml import qmlRegisterType from UM.i18n import i18nCatalog i18n_catalog = i18nCatalog("cura") diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index 2a44d6840e..d6aacfc341 100644 --- a/plugins/PerObjectSettingsTool/plugin.json +++ b/plugins/PerObjectSettingsTool/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the Per Model Settings.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 755d815d0a..845ad45341 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -9,7 +9,7 @@ import pkgutil import sys from typing import Dict, Type, TYPE_CHECKING, List, Optional, cast -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot +from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot from UM.Application import Application from UM.Extension import Extension diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 8ce6e3f94f..4faf908881 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -143,14 +143,12 @@ UM.Dialog } } - UM.RecolorImage + UM.ColorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: parent.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("text_disabled") source: UM.Theme.getIcon("ChevronSingleDown") } @@ -175,14 +173,12 @@ UM.Dialog } } - UM.RecolorImage + UM.ColorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: upButton.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("text_disabled") source: UM.Theme.getIcon("ChevronSingleUp") } @@ -200,14 +196,12 @@ UM.Dialog onClicked: manager.removeScriptByIndex(index) } - UM.RecolorImage + UM.ColorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: UM.Theme.getColor("text") source: UM.Theme.getIcon("Cancel") } @@ -237,8 +231,8 @@ UM.Dialog onTriggered: manager.addScriptToList(modelData.toString()) } - onObjectAdded: scriptsMenu.insertItem(index, object) - onObjectRemoved: scriptsMenu.removeItem(object) + onObjectAdded: function(index, object) { scriptsMenu.insertItem(index, object)} + onObjectRemoved: function(index, object) { scriptsMenu.removeItem(object) } } } @@ -481,7 +475,7 @@ UM.Dialog } toolTipContentAlignment: UM.Enums.ContentAlignment.AlignLeft onClicked: dialog.show() - iconSource: "Script.svg" + iconSource: Qt.resolvedUrl("Script.svg") fixedWidthMode: false } diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index d5b6727962..9671b2e91d 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -2,7 +2,7 @@ "name": "Post Processing", "author": "Ultimaker", "version": "2.2.1", - "api": 7, + "api": 8, "description": "Extension that allows for user created scripts for post processing", "catalog": "cura" } \ No newline at end of file diff --git a/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py b/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py index 0ce6ac6eff..fef66915bf 100644 --- a/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py +++ b/plugins/PostProcessingPlugin/scripts/CreateThumbnail.py @@ -2,7 +2,7 @@ import base64 from UM.Logger import Logger from cura.Snapshot import Snapshot -from PyQt5.QtCore import QByteArray, QIODevice, QBuffer +from PyQt6.QtCore import QByteArray, QIODevice, QBuffer from ..Script import Script @@ -22,7 +22,7 @@ class CreateThumbnail(Script): Logger.log("d", "Encoding thumbnail image...") try: thumbnail_buffer = QBuffer() - thumbnail_buffer.open(QBuffer.ReadWrite) + thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite) thumbnail_image = snapshot thumbnail_image.save(thumbnail_buffer, "PNG") base64_bytes = base64.b64encode(thumbnail_buffer.data()) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index f0779df98f..d6bff1792e 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -91,13 +91,11 @@ Item height: parent.height width: visible ? (headerPadding * 3 + UM.Theme.getSize("button_icon").height + iconSize) : 0 - headerItem: UM.RecolorImage + headerItem: UM.ColorImage { id: menuIcon source: UM.Theme.getIcon("Folder", "medium") color: UM.Theme.getColor("icon") - - sourceSize.height: height } contentItem: Item @@ -111,13 +109,13 @@ Item //The column doesn't automatically listen to its children rect if the children change internally, so we need to explicitly update the size. onChildrenRectChanged: { - popup.height = childrenRect.height - popup.width = childrenRect.width + popup.implicitHeight = childrenRect.height + popup.implicitWidth = childrenRect.width } onPositioningComplete: { - popup.height = childrenRect.height - popup.width = childrenRect.width + popup.implicitHeight = childrenRect.height + popup.implicitWidth = childrenRect.width } Repeater @@ -177,7 +175,7 @@ Item contentItem: Item { - UM.RecolorImage + UM.ColorImage { id: buttonIcon source: UM.Theme.getIcon("Folder", "medium") @@ -185,8 +183,6 @@ Item width: UM.Theme.getSize("button_icon").width height: UM.Theme.getSize("button_icon").height color: UM.Theme.getColor("icon") - - sourceSize.height: height } } diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json index 603ed49359..39842c32f2 100644 --- a/plugins/PrepareStage/plugin.json +++ b/plugins/PrepareStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a prepare stage in Cura.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PreviewStage/plugin.json b/plugins/PreviewStage/plugin.json index f0341d20ea..7e91ec96c5 100644 --- a/plugins/PreviewStage/plugin.json +++ b/plugins/PreviewStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a preview stage in Cura.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py index d60e4b34f0..bb3f5e0019 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py @@ -21,7 +21,7 @@ class RemovableDrivePlugin(OutputDevicePlugin): super().__init__() self._update_thread = threading.Thread(target = self._updateThread) - self._update_thread.setDaemon(True) + self._update_thread.daemon = True self._check_updates = True diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index b1dae2d182..76dd485dfd 100644 --- a/plugins/RemovableDriveOutputDevice/plugin.json +++ b/plugins/RemovableDriveOutputDevice/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Provides removable drive hotplugging and writing support.", "version": "1.0.1", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/SentryLogger/plugin.json b/plugins/SentryLogger/plugin.json index e765e45e0f..2a55de2963 100644 --- a/plugins/SentryLogger/plugin.json +++ b/plugins/SentryLogger/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Logs certain events so that they can be used by the crash reporter", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 0a24ca9023..f08c72c1f7 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -266,7 +266,7 @@ Item anchors.bottom: parent.top anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height anchors.horizontalCenter: parent.horizontalCenter - target: Qt.point(parent.width / 2, parent.top) + target: Qt.point(parent.width / 2, 1) visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle // custom properties @@ -376,7 +376,7 @@ Item anchors.top: parent.bottom anchors.topMargin: UM.Theme.getSize("narrow_margin").height anchors.horizontalCenter: parent.horizontalCenter - target: Qt.point(parent.width / 2, parent.bottom) + target: Qt.point(parent.width / 2, -1) visible: sliderRoot.activeHandle == parent || sliderRoot.activeHandle == rangeHandle // custom properties diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index af6b538f26..532ef459c0 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -3,9 +3,9 @@ import sys -from PyQt5.QtCore import Qt -from PyQt5.QtGui import QOpenGLContext -from PyQt5.QtWidgets import QApplication +from PyQt6.QtCore import Qt +from PyQt6.QtGui import QOpenGLContext +from PyQt6.QtWidgets import QApplication from UM.Application import Application from UM.Event import Event, KeyEvent @@ -598,8 +598,8 @@ class SimulationView(CuraView): def event(self, event) -> bool: modifiers = QApplication.keyboardModifiers() - ctrl_is_active = modifiers & Qt.ControlModifier - shift_is_active = modifiers & Qt.ShiftModifier + ctrl_is_active = modifiers & Qt.KeyboardModifier.ControlModifier + shift_is_active = modifiers & Qt.KeyboardModifier.ShiftModifier if event.type == Event.KeyPressEvent and ctrl_is_active: amount = 10 if shift_is_active else 1 if event.key == KeyEvent.UpKey: diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 4fb238f6da..a82d1e3db9 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -75,7 +75,7 @@ Item UM.SimpleButton { id: playButton - iconSource: !isSimulationPlaying ? "./resources/Play.svg": "./resources/Pause.svg" + iconSource: Qt.resolvedUrl(!isSimulationPlaying ? "./resources/Play.svg": "./resources/Pause.svg") width: UM.Theme.getSize("small_button").width height: UM.Theme.getSize("small_button").height hoverColor: UM.Theme.getColor("slider_handle_active") diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index ba13a75ea7..a811fea5db 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -1,10 +1,9 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.4 +import QtQuick 2.15 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 import UM 1.5 as UM import Cura 1.0 as Cura @@ -137,7 +136,7 @@ Cura.ExpandableComponent model: layerViewTypes visible: !UM.SimulationView.compatibilityMode - onActivated: UM.Preferences.setValue("layerview/layer_view_type", index) + onActivated: (index) => {UM.Preferences.setValue("layerview/layer_view_type", index)} Component.onCompleted: { @@ -484,43 +483,28 @@ Cura.ExpandableComponent border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - LinearGradient + gradient: Gradient { - anchors + orientation: Gradient.Horizontal + GradientStop { - left: parent.left - leftMargin: UM.Theme.getSize("default_lining").width - right: parent.right - rightMargin: UM.Theme.getSize("default_lining").width - top: parent.top - topMargin: UM.Theme.getSize("default_lining").width - bottom: parent.bottom - bottomMargin: UM.Theme.getSize("default_lining").width + position: 0.000 + color: Qt.rgba(0, 0, 1, 1) } - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient + GradientStop { - GradientStop - { - position: 0.000 - color: Qt.rgba(0, 0, 1, 1) - } - GradientStop - { - position: 0.25 - color: Qt.rgba(0.25, 1, 0, 1) - } - GradientStop - { - position: 0.375 - color: Qt.rgba(0.375, 0.5, 0, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(1, 0.5, 0, 1) - } + position: 0.25 + color: Qt.rgba(0.25, 1, 0, 1) + } + GradientStop + { + position: 0.375 + color: Qt.rgba(0.375, 0.5, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(1, 0.5, 0, 1) } } } @@ -538,48 +522,33 @@ Cura.ExpandableComponent border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - LinearGradient + gradient: Gradient { - anchors + orientation: Gradient.Horizontal + GradientStop { - left: parent.left - leftMargin: UM.Theme.getSize("default_lining").width - right: parent.right - rightMargin: UM.Theme.getSize("default_lining").width - top: parent.top - topMargin: UM.Theme.getSize("default_lining").width - bottom: parent.bottom - bottomMargin: UM.Theme.getSize("default_lining").width + position: 0.000 + color: Qt.rgba(0, 0, 0.5, 1) } - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient + GradientStop { - GradientStop - { - position: 0.000 - color: Qt.rgba(0, 0, 0.5, 1) - } - GradientStop - { - position: 0.25 - color: Qt.rgba(0, 0.375, 0.75, 1) - } - GradientStop - { - position: 0.5 - color: Qt.rgba(0, 0.75, 0.5, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(1, 0.75, 0.25, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(1, 1, 0, 1) - } + position: 0.25 + color: Qt.rgba(0, 0.375, 0.75, 1) + } + GradientStop + { + position: 0.5 + color: Qt.rgba(0, 0.75, 0.5, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(1, 0.75, 0.25, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(1, 1, 0, 1) } } } @@ -597,68 +566,53 @@ Cura.ExpandableComponent border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - LinearGradient + gradient: Gradient { - anchors + orientation: Gradient.Horizontal + GradientStop { - left: parent.left - leftMargin: UM.Theme.getSize("default_lining").width - right: parent.right - rightMargin: UM.Theme.getSize("default_lining").width - top: parent.top - topMargin: UM.Theme.getSize("default_lining").width - bottom: parent.bottom - bottomMargin: UM.Theme.getSize("default_lining").width + position: 0.0 + color: Qt.rgba(0, 0, 0.5, 1) } - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient + GradientStop { - GradientStop - { - position: 0.0 - color: Qt.rgba(0, 0, 0.5, 1) - } - GradientStop - { - position: 0.125 - color: Qt.rgba(0, 0.0, 1.0, 1) - } - GradientStop - { - position: 0.25 - color: Qt.rgba(0, 0.5, 1.0, 1) - } - GradientStop - { - position: 0.375 - color: Qt.rgba(0.0, 1.0, 1.0, 1) - } - GradientStop - { - position: 0.5 - color: Qt.rgba(0.5, 1.0, 0.5, 1) - } - GradientStop - { - position: 0.625 - color: Qt.rgba(1.0, 1.0, 0.0, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(1.0, 0.5, 0, 1) - } - GradientStop - { - position: 0.875 - color: Qt.rgba(1.0, 0.0, 0, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0.5, 0, 0, 1) - } + position: 0.125 + color: Qt.rgba(0, 0.0, 1.0, 1) + } + GradientStop + { + position: 0.25 + color: Qt.rgba(0, 0.5, 1.0, 1) + } + GradientStop + { + position: 0.375 + color: Qt.rgba(0.0, 1.0, 1.0, 1) + } + GradientStop + { + position: 0.5 + color: Qt.rgba(0.5, 1.0, 0.5, 1) + } + GradientStop + { + position: 0.625 + color: Qt.rgba(1.0, 1.0, 0.0, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(1.0, 0.5, 0, 1) + } + GradientStop + { + position: 0.875 + color: Qt.rgba(1.0, 0.0, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0.5, 0, 0, 1) } } } diff --git a/plugins/SimulationView/SimulationViewProxy.py b/plugins/SimulationView/SimulationViewProxy.py index 7d78e93ca5..669f7fdbcc 100644 --- a/plugins/SimulationView/SimulationViewProxy.py +++ b/plugins/SimulationView/SimulationViewProxy.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import TYPE_CHECKING -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty +from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty from UM.FlameProfiler import pyqtSlot from UM.Application import Application diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py index 420ee60660..c3f2b1629c 100644 --- a/plugins/SimulationView/__init__.py +++ b/plugins/SimulationView/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtQml import qmlRegisterSingletonType +from PyQt6.QtQml import qmlRegisterSingletonType from UM.i18n import i18nCatalog from . import SimulationViewProxy, SimulationView @@ -24,5 +24,5 @@ def createSimulationViewProxy(engine, script_engine): def register(app): simulation_view = SimulationView.SimulationView() - qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy) + qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, simulation_view.getProxy, "SimulationView") return { "view": simulation_view} diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index 39c528f89d..7e7561e5c0 100644 --- a/plugins/SimulationView/plugin.json +++ b/plugins/SimulationView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the preview of sliced layerdata.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/SliceInfoPlugin/MoreInfoWindow.qml b/plugins/SliceInfoPlugin/MoreInfoWindow.qml index 3a6b6c8741..2f60089343 100644 --- a/plugins/SliceInfoPlugin/MoreInfoWindow.qml +++ b/plugins/SliceInfoPlugin/MoreInfoWindow.qml @@ -5,10 +5,9 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 -import UM 1.3 as UM +import UM 1.5 as UM import Cura 1.1 as Cura - Window { UM.I18nCatalog { id: catalog; name: "cura" } diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 0e8db0f88a..0bf76f8423 100755 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -7,8 +7,8 @@ import platform import time from typing import cast, Optional, Set, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, QObject -from PyQt5.QtNetwork import QNetworkRequest +from PyQt6.QtCore import pyqtSlot, QObject +from PyQt6.QtNetwork import QNetworkRequest from UM.Extension import Extension from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -20,7 +20,7 @@ from UM.Qt.Duration import DurationFormat from cura import ApplicationMetadata if TYPE_CHECKING: - from PyQt5.QtNetwork import QNetworkReply + from PyQt6.QtNetwork import QNetworkReply catalog = i18nCatalog("cura") @@ -289,7 +289,7 @@ class SliceInfo(QObject, Extension): Logger.logException("e", "Exception raised while sending slice info.") # But we should be notified about these problems of course. def _onRequestFinished(self, reply: "QNetworkReply") -> None: - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) if status_code == 200: Logger.log("i", "SliceInfo sent successfully") return diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index eba604c47f..cc3429d6a1 100644 --- a/plugins/SliceInfoPlugin/plugin.json +++ b/plugins/SliceInfoPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Submits anonymous slice info. Can be disabled through preferences.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index f12c1aae01..b7aeb90da4 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -6,8 +6,8 @@ from UM.View.View import View from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Selection import Selection from UM.Resources import Resources -from PyQt5.QtGui import QOpenGLContext, QDesktopServices, QImage -from PyQt5.QtCore import QSize, QUrl +from PyQt6.QtGui import QOpenGLContext, QDesktopServices, QImage +from PyQt6.QtCore import QSize, QUrl import numpy as np import time @@ -296,7 +296,7 @@ class SolidView(View): self._next_xray_checking_time = time.time() + self._xray_checking_update_time xray_img = self._xray_pass.getOutput() - xray_img = xray_img.convertToFormat(QImage.Format_RGB888) + xray_img = xray_img.convertToFormat(QImage.Format.Format_RGB888) # We can't just read the image since the pixels are aligned to internal memory positions. # xray_img.byteCount() != xray_img.width() * xray_img.height() * 3 diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index fa4180ff61..48ac75377f 100644 --- a/plugins/SolidView/plugin.json +++ b/plugins/SolidView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a normal solid mesh view.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py index b64a0f4eed..0a714396aa 100644 --- a/plugins/SupportEraser/SupportEraser.py +++ b/plugins/SupportEraser/SupportEraser.py @@ -1,8 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import Qt, QTimer -from PyQt5.QtWidgets import QApplication +from PyQt6.QtCore import Qt, QTimer +from PyQt6.QtWidgets import QApplication from UM.Application import Application from UM.Math.Vector import Vector @@ -31,7 +31,7 @@ import numpy class SupportEraser(Tool): def __init__(self): super().__init__() - self._shortcut_key = Qt.Key_E + self._shortcut_key = Qt.Key.Key_E self._controller = self.getController() self._selection_pass = None @@ -53,7 +53,7 @@ class SupportEraser(Tool): def event(self, event): super().event(event) modifiers = QApplication.keyboardModifiers() - ctrl_is_active = modifiers & Qt.ControlModifier + ctrl_is_active = modifiers & Qt.KeyboardModifier.ControlModifier if event.type == Event.MousePressEvent and MouseEvent.LeftButton in event.buttons and self._controller.getToolsEnabled(): if ctrl_is_active: diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index f6259ad70e..dc05ebc908 100644 --- a/plugins/SupportEraser/plugin.json +++ b/plugins/SupportEraser/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Creates an eraser mesh to block the printing of support in certain places", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/TrimeshReader/TrimeshReader.py b/plugins/TrimeshReader/TrimeshReader.py index 6aea321f15..9f13dd7872 100644 --- a/plugins/TrimeshReader/TrimeshReader.py +++ b/plugins/TrimeshReader/TrimeshReader.py @@ -1,5 +1,5 @@ -# Copyright (c) 2019 Ultimaker B.V., fieldOfView -# Cura is released under the terms of the LGPLv3 or higher. +# Copyright (c) 2019-2022 Ultimaker B.V., fieldOfView +# Cura is released under the terms of the LGPLv3 or higher. # The _toMeshData function is taken from the AMFReader class which was built by fieldOfView. @@ -29,14 +29,7 @@ class TrimeshReader(MeshReader): def __init__(self) -> None: super().__init__() - self._supported_extensions = [".ctm", ".dae", ".gltf", ".glb", ".ply", ".zae"] - MimeTypeDatabase.addMimeType( - MimeType( - name = "application/x-ctm", - comment = "Open Compressed Triangle Mesh", - suffixes = ["ctm"] - ) - ) + self._supported_extensions = [".dae", ".gltf", ".glb", ".ply", ".zae"] MimeTypeDatabase.addMimeType( MimeType( name = "model/vnd.collada+xml", @@ -164,4 +157,4 @@ class TrimeshReader(MeshReader): normals = calculateNormalsFromIndexedVertices(vertices, indices, face_count) mesh_data = MeshData(vertices = vertices, indices = indices, normals = normals, file_name = file_name) - return mesh_data \ No newline at end of file + return mesh_data diff --git a/plugins/TrimeshReader/plugin.json b/plugins/TrimeshReader/plugin.json index 7bf60eefc6..67d5a1c3e7 100644 --- a/plugins/TrimeshReader/plugin.json +++ b/plugins/TrimeshReader/plugin.json @@ -3,5 +3,5 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading model files.", - "api": 7 + "api": 8 } diff --git a/plugins/UFPReader/plugin.json b/plugins/UFPReader/plugin.json index cac7e86236..70b057f06f 100644 --- a/plugins/UFPReader/plugin.json +++ b/plugins/UFPReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading Ultimaker Format Packages.", - "supported_sdk_versions": ["7.9.0"], + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index 455a7c3c36..52dab1efc7 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -8,7 +8,7 @@ from Charon.OpenMode import OpenMode # To indicate that we want to write to UFP from Charon.filetypes.OpenPackagingConvention import OPCError from io import StringIO # For converting g-code to bytes. -from PyQt5.QtCore import QBuffer +from PyQt6.QtCore import QBuffer from UM.Logger import Logger from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement. @@ -83,7 +83,7 @@ class UFPWriter(MeshWriter): thumbnail = archive.getStream("/Metadata/thumbnail.png") thumbnail_buffer = QBuffer() - thumbnail_buffer.open(QBuffer.ReadWrite) + thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite) snapshot.save(thumbnail_buffer, "PNG") thumbnail.write(thumbnail_buffer.data()) diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index 6d27d250cf..0f2d3bc847 100644 --- a/plugins/UFPWriter/plugin.json +++ b/plugins/UFPWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for writing Ultimaker Format Packages.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index 8a7a0e5b43..46f0b8bace 100644 --- a/plugins/UM3NetworkPrinting/plugin.json +++ b/plugins/UM3NetworkPrinting/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Manages network connections to Ultimaker networked printers.", "version": "2.0.0", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml index 4458b48996..e95b9d7e0e 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml @@ -21,7 +21,7 @@ Button color: parent.enabled ? (parent.hovered ? UM.Theme.getColor("monitor_card_hover") : "transparent") : UM.Theme.getColor("monitor_icon_disabled") } - UM.RecolorImage + UM.ColorImage { id: icon anchors diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index e68f552f7b..7d34a182b3 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -1,14 +1,14 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import UM 1.5 as UM -import Cura 1.5 as Cura - -import QtQuick 2.2 +import QtQuick 2.15 import QtQuick.Controls 2.9 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 +import UM 1.5 as UM +import Cura 1.5 as Cura + Cura.MachineAction { id: base @@ -302,7 +302,7 @@ Cura.MachineAction { id: addressField width: parent.width - validator: RegExpValidator { regExp: /[a-zA-Z0-9\.\-\_]*/ } + validator: RegularExpressionValidator { regularExpression: /[a-zA-Z0-9\.\-\_]*/ } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/GenericPopUp.qml b/plugins/UM3NetworkPrinting/resources/qml/GenericPopUp.qml index 580338ae0c..47660fc7f3 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/GenericPopUp.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/GenericPopUp.qml @@ -3,7 +3,6 @@ import QtQuick 2.2 import QtQuick.Controls 2.0 -import QtGraphicalEffects 1.0 import UM 1.3 as UM /** @@ -60,16 +59,6 @@ Popup { anchors.fill: parent - DropShadow - { - anchors.fill: pointedRectangle - color: UM.Theme.getColor("monitor_shadow") - radius: UM.Theme.getSize("monitor_shadow_radius").width - source: pointedRectangle - transparentBorder: true - verticalOffset: 2 * screenScaleFactor - } - Item { id: pointedRectangle diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 60e3726a32..0bd57e76d8 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -45,7 +45,7 @@ Item radius: Math.floor(height / 2) } - UM.RecolorImage + UM.ColorImage { id: buildplateIcon anchors.centerIn: parent diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index a42dc0d12a..89c2edd9b5 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -3,7 +3,6 @@ import QtQuick 2.3 import QtQuick.Controls 2.0 -import QtGraphicalEffects 1.0 import UM 1.3 as UM Item @@ -39,24 +38,10 @@ Item } height: parent.height z: 10 - LinearGradient + Rectangle { anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(leftHint.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("monitor_stage_background") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("monitor_stage_background_fade") - } - } + color: UM.Theme.getColor("monitor_stage_background") } MouseArea { @@ -90,13 +75,11 @@ Item contentItem: Item { anchors.fill: parent - UM.RecolorImage + UM.ColorImage { anchors.centerIn: parent width: 18 // TODO: Theme! height: width // TODO: Theme! - sourceSize.width: width // TODO: Theme! - sourceSize.height: width // TODO: Theme! color: UM.Theme.getColor("text") source: UM.Theme.getIcon("ChevronSingleLeft") } @@ -169,13 +152,11 @@ Item contentItem: Item { anchors.fill: parent - UM.RecolorImage + UM.ColorImage { anchors.centerIn: parent width: 18 // TODO: Theme! height: width // TODO: Theme! - sourceSize.width: width // TODO: Theme! - sourceSize.height: width // TODO: Theme! color: UM.Theme.getColor("text") source: UM.Theme.getIcon("ChevronSingleRight") } @@ -194,24 +175,11 @@ Item height: centerSection.height z: 10 - LinearGradient + Rectangle { anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(rightHint.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("monitor_stage_background_fade") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("monitor_stage_background") - } - } + color: UM.Theme.getColor("monitor_stage_background_fade") + } MouseArea { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml index f22ac53298..0467e2b753 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml @@ -22,12 +22,12 @@ Item property int size: 32 * screenScaleFactor // TODO: Theme! // THe extruder icon source; NOTE: This shouldn't need to be changed - property string iconSource: "../svg/icons/Extruder.svg" + property string iconSource: Qt.resolvedUrl("../svg/icons/Extruder.svg") height: size width: size - UM.RecolorImage + UM.ColorImage { id: icon anchors.fill: parent diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index cf2239201f..18d3a44ef1 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -38,26 +38,21 @@ Item } - UM.RecolorImage + UM.ColorImage { id: ultiBotImage anchors.centerIn: printJobPreview color: UM.Theme.getColor("monitor_placeholder_image") height: printJobPreview.height - source: "../svg/ultibot.svg" - sourceSize - { - height: height - width: width - } + source: Qt.resolvedUrl("../svg/ultibot.svg") /* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or not in order to determine if we show the placeholder (ultibot) image instead. */ visible: printJob && previewImage.status == Image.Error width: printJobPreview.width } - UM.RecolorImage + UM.ColorImage { id: overlayIcon anchors.centerIn: printJobPreview @@ -90,11 +85,6 @@ Item } return "" } - sourceSize - { - height: height - width: width - } visible: source != "" width: 0.5 * printJobPreview.width } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index df701b1d1f..0069d017f6 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -147,7 +147,7 @@ Item color: UM.Theme.getColor("text_link") text: catalog.i18nc("@label link to Connect and Cloud interfaces", "Manage printer") } - UM.RecolorImage + UM.ColorImage { id: externalLinkIcon anchors @@ -252,7 +252,7 @@ Item bottom: parent.bottom bottomMargin: 20 * screenScaleFactor // TODO: Theme! } - iconSource: "../svg/icons/CameraPhoto.svg" + iconSource: Qt.resolvedUrl("../svg/icons/CameraPhoto.svg") enabled: !cloudConnection visible: printer } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml index 87b7cab83e..3fd500cfca 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -40,7 +40,7 @@ Item height: 18 * screenScaleFactor // TODO: Theme! width: childrenRect.width - UM.RecolorImage + UM.ColorImage { id: externalLinkIcon anchors.verticalCenter: manageQueueLabel.verticalCenter diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 7b8be4b2c2..470e57947e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -5,8 +5,8 @@ from json import JSONDecodeError from time import time from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast -from PyQt5.QtCore import QUrl -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt6.QtCore import QUrl +from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from UM.TaskManagement.HttpRequestManager import HttpRequestManager @@ -165,7 +165,7 @@ class CloudApiClient: request = QNetworkRequest(QUrl(path)) if content_type: - request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) access_token = self._account.accessToken if access_token: request.setRawHeader(b"Authorization", "Bearer {}".format(access_token).encode()) @@ -179,7 +179,7 @@ class CloudApiClient: :return: A tuple with a status code and a dictionary. """ - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() return status_code, json.loads(response) @@ -233,7 +233,7 @@ class CloudApiClient: self._anti_gc_callbacks.remove(parse) # Don't try to parse the reply if we didn't get one - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) is None: if on_error is not None: on_error() return diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 85e802215c..8c45ce537f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -5,9 +5,9 @@ from time import time import os from typing import cast, List, Optional, TYPE_CHECKING -from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest # Parse errors specific to print job uploading. +from PyQt6.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest # Parse errors specific to print job uploading. from UM import i18nCatalog from UM.Backend.Backend import BackendState @@ -272,7 +272,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice): """ Displays a message when an error occurs specific to uploading print job (i.e. queue is full). """ - error_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + error_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) if error_code == 409: PrintJobUploadQueueFullMessage().show() else: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 8eecafd49c..d88814818a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -4,8 +4,8 @@ import os from typing import Dict, List, Optional, Set -from PyQt5.QtNetwork import QNetworkReply -from PyQt5.QtWidgets import QMessageBox +from PyQt6.QtNetwork import QNetworkReply +from PyQt6.QtWidgets import QMessageBox from UM import i18nCatalog from UM.Logger import Logger # To log errors talking to the API. @@ -480,7 +480,7 @@ class CloudOutputDeviceManager: if remove_printers_ids == all_ids: question_content = self.i18n_catalog.i18nc("@label", "You are about to remove all printers from Cura. This action cannot be undone.\nAre you sure you want to continue?") result = QMessageBox.question(None, question_title, question_content) - if result == QMessageBox.No: + if result == QMessageBox.StandardButton.No: return for machine_cloud_id in self.reported_device_ids: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py index 5a3e2474a8..1881d90923 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # !/usr/bin/env python # -*- coding: utf-8 -*- -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply from typing import Callable, Any, Tuple, cast, Dict, Optional from UM.Logger import Logger @@ -106,9 +106,9 @@ class ToolPathUploader: """Checks whether a chunk of data was uploaded successfully, starting the next chunk if needed.""" Logger.log("i", "Finished callback %s %s", - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) + reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute), reply.url().toString()) - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) # type: Optional[int] + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) # type: Optional[int] if not status_code: Logger.log("e", "Reply contained no status code.") self._errorCallback(reply, None) diff --git a/plugins/UM3NetworkPrinting/src/Messages/CloudFlowMessage.py b/plugins/UM3NetworkPrinting/src/Messages/CloudFlowMessage.py index 34687339a9..05609070a2 100644 --- a/plugins/UM3NetworkPrinting/src/Messages/CloudFlowMessage.py +++ b/plugins/UM3NetworkPrinting/src/Messages/CloudFlowMessage.py @@ -2,8 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. import os -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QUrl +from PyQt6.QtGui import QDesktopServices from UM import i18nCatalog from UM.Message import Message diff --git a/plugins/UM3NetworkPrinting/src/Messages/NotClusterHostMessage.py b/plugins/UM3NetworkPrinting/src/Messages/NotClusterHostMessage.py index 059b81b39e..3b00f24672 100644 --- a/plugins/UM3NetworkPrinting/src/Messages/NotClusterHostMessage.py +++ b/plugins/UM3NetworkPrinting/src/Messages/NotClusterHostMessage.py @@ -2,8 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import TYPE_CHECKING -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QDesktopServices +from PyQt6.QtCore import QUrl +from PyQt6.QtGui import QDesktopServices from UM import i18nCatalog from UM.Message import Message diff --git a/plugins/UM3NetworkPrinting/src/Models/ConfigurationChangeModel.py b/plugins/UM3NetworkPrinting/src/Models/ConfigurationChangeModel.py index 58fae03679..6de08afb65 100644 --- a/plugins/UM3NetworkPrinting/src/Models/ConfigurationChangeModel.py +++ b/plugins/UM3NetworkPrinting/src/Models/ConfigurationChangeModel.py @@ -1,6 +1,6 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QObject +from PyQt6.QtCore import pyqtProperty, QObject BLOCKING_CHANGE_TYPES = [ diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterStatus.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterStatus.py index b5fae5d9c7..16b4b6d656 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterStatus.py @@ -3,7 +3,7 @@ from itertools import product from typing import List, Union, Dict, Optional, Any -from PyQt5.QtCore import QUrl +from PyQt6.QtCore import QUrl from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel from cura.PrinterOutput.PrinterOutputController import PrinterOutputController diff --git a/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py b/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py index b063a2bf5b..85651244f9 100644 --- a/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py +++ b/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py @@ -2,9 +2,9 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional -from PyQt5.QtCore import pyqtProperty, pyqtSignal -from PyQt5.QtGui import QImage -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from PyQt6.QtCore import pyqtProperty, pyqtSignal +from PyQt6.QtGui import QImage +from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest from UM.Logger import Logger from UM.TaskManagement.HttpRequestManager import HttpRequestManager @@ -42,6 +42,6 @@ class UM3PrintJobOutputModel(PrintJobOutputModel): def _onImageLoaded(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if not HttpRequestManager.replyIndicatesSuccess(reply, error): Logger.warning("Requesting preview image failed, response code {0} while trying to connect to {1}".format( - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())) + reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute), reply.url())) return self.updatePreviewImageData(reply.readAll()) diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index d1840bf90c..e0b156dc08 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -4,8 +4,8 @@ import json from json import JSONDecodeError from typing import Callable, List, Optional, Dict, Union, Any, Type, cast, TypeVar, Tuple -from PyQt5.QtCore import QUrl -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply +from PyQt6.QtCore import QUrl +from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply from UM.Logger import Logger @@ -118,9 +118,8 @@ class ClusterApiClient: """ url = QUrl("http://" + self._address + path) request = QNetworkRequest(url) - request.setAttribute(QNetworkRequest.FollowRedirectsAttribute, True) if content_type: - request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, content_type) return request @staticmethod @@ -130,7 +129,7 @@ class ClusterApiClient: :param reply: The reply from the server. :return: A tuple with a status code and a dictionary. """ - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() return status_code, json.loads(response) @@ -173,10 +172,10 @@ class ClusterApiClient: self._anti_gc_callbacks.remove(parse) # Don't try to parse the reply if we didn't get one - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) is None: return - if reply.error() > 0: + if reply.error() != QNetworkReply.NetworkError.NoError: self._on_error(reply.errorString()) return diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py index 9288b4c4b0..2d27b7c3be 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py @@ -3,9 +3,9 @@ import os from typing import Optional, Dict, List, Callable, Any -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject +from PyQt6.QtNetwork import QNetworkReply from UM.FileHandler.FileHandler import FileHandler from UM.i18n import i18nCatalog diff --git a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py index 877eaebcb7..32e20892ac 100644 --- a/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/Network/SendMaterialJob.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import os from typing import Dict, TYPE_CHECKING, Set, List -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest from UM.Job import Job from UM.Logger import Logger @@ -152,7 +152,7 @@ class SendMaterialJob(Job): def _sendingFinished(self, reply: QNetworkReply) -> None: """Check a reply from an upload to the printer and log an error when the call failed""" - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: + if reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) != 200: Logger.log("w", "Error while syncing material: %s", reply.errorString()) return body = reply.readAll().data().decode('utf8') diff --git a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterAction.py b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterAction.py index 772a9d1973..12b0fa56bf 100644 --- a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterAction.py +++ b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterAction.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Optional, cast -from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QObject +from PyQt6.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QObject from UM import i18nCatalog from cura.CuraApplication import CuraApplication diff --git a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py index c9231d71ee..769e92610a 100644 --- a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py @@ -4,7 +4,7 @@ import os from time import time from typing import List, Optional, Dict -from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, pyqtSlot, QUrl +from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject, pyqtSlot, QUrl from UM.Logger import Logger from UM.Qt.Duration import Duration, DurationFormat diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 1e9b46cb1c..02e3cc137d 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -7,7 +7,7 @@ import serial.tools.list_ports from os import environ from re import search -from PyQt5.QtCore import QObject, pyqtSignal +from PyQt6.QtCore import QObject, pyqtSignal from UM.Signal import Signal, signalemitter from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin @@ -30,16 +30,17 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): def __init__(self, application, parent = None): if USBPrinterOutputDeviceManager.__instance is not None: raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) - USBPrinterOutputDeviceManager.__instance = self super().__init__(parent = parent) + USBPrinterOutputDeviceManager.__instance = self + self._application = application self._serial_port_list = [] self._usb_output_devices = {} self._usb_output_devices_model = None self._update_thread = threading.Thread(target = self._updateThread) - self._update_thread.setDaemon(True) + self._update_thread.daemon = True self._check_updates = True diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 21e5fd4160..fd4b600c95 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -2,7 +2,7 @@ "name": "USB printing", "author": "Ultimaker B.V.", "version": "1.0.2", - "api": 7, + "api": 8, "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" } diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 62eab75986..0703731d64 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -3,7 +3,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from cura.MachineAction import MachineAction -from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty +from PyQt6.QtCore import pyqtSlot, pyqtSignal, pyqtProperty from UM.i18n import i18nCatalog from UM.Application import Application diff --git a/plugins/UltimakerMachineActions/plugin.json b/plugins/UltimakerMachineActions/plugin.json index c216c74f8f..82e4a21408 100644 --- a/plugins/UltimakerMachineActions/plugin.json +++ b/plugins/UltimakerMachineActions/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index 7f9b57c6e5..57c5c1c734 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index e179fdc051..8a51bff7d5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index efee6fb666..e5383be218 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index 03e19bb61e..18cbf2432e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index f2984809ac..65f1451118 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index 2404b3e474..a006e731aa 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index 6326ea2657..7703c5550b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index a25bd36f5a..822f36e8a6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json index 345578ff11..9dcdbc90de 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json index 4ac9de08c5..a0dea89942 100644 --- a/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json index 1b0ee2b162..0b18d3da1c 100644 --- a/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json b/plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json index 4ac4f264c8..e9abe62fe5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.11 to Cura 4.12.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json b/plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json index df3c53b5df..a95bbd1c3e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.13 to Cura 5.0.", - "api": "7.9.0", + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json b/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json index e8b3e3c846..6b48b3277d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.1 to Cura 4.2.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json b/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json index ce0a7aee60..2183444f76 100644 --- a/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.2 to Cura 4.3.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json b/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json index bd6658ff3c..e9c29631c5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.3 to Cura 4.4.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade44to45/VersionUpgrade44to45.py b/plugins/VersionUpgrade/VersionUpgrade44to45/VersionUpgrade44to45.py index 00faa216eb..bc372c1428 100644 --- a/plugins/VersionUpgrade/VersionUpgrade44to45/VersionUpgrade44to45.py +++ b/plugins/VersionUpgrade/VersionUpgrade44to45/VersionUpgrade44to45.py @@ -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 configparser @@ -33,18 +33,20 @@ class VersionUpgrade44to45(VersionUpgrade): In this case the plug-in will also check for stacks that need to be deleted. """ + super().__init__() # Only delete hidden stacks when upgrading from version 4.4. Not 4.3 or 4.5, just when you're starting out from 4.4. # If you're starting from an earlier version, you can't have had the bug that produces too many hidden stacks (https://github.com/Ultimaker/Cura/issues/6731). # If you're starting from a later version, the bug was already fixed. data_storage_root = os.path.dirname(Resources.getDataStoragePath()) - folders = set(os.listdir(data_storage_root)) # All version folders. - folders = set(filter(lambda p: re.fullmatch(r"\d+\.\d+", p), folders)) # Only folders with a correct version number as name. - folders.difference_update({os.path.basename(Resources.getDataStoragePath())}) # Remove current version from candidates (since the folder was just copied). - if folders: - latest_version = max(folders, key = Version) # Sort them by semantic version numbering. - if latest_version == "4.4": - self.removeHiddenStacks() + if os.path.exists(data_storage_root): + folders = set(os.listdir(data_storage_root)) # All version folders. + folders = set(filter(lambda p: re.fullmatch(r"\d+\.\d+", p), folders)) # Only folders with a correct version number as name. + folders.difference_update({os.path.basename(Resources.getDataStoragePath())}) # Remove current version from candidates (since the folder was just copied). + if folders: + latest_version = max(folders, key = Version) # Sort them by semantic version numbering. + if latest_version == "4.4": + self.removeHiddenStacks() def removeHiddenStacks(self) -> None: """ diff --git a/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json b/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json index a0ea2b1f1e..d571b3f7dc 100644 --- a/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.4 to Cura 4.5.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json b/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json index 8a82a74487..32ce86ab8d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.5 to Cura 4.6.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json b/plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json index 39947ee226..46b159ffde 100644 --- a/plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade462to47/plugin.json b/plugins/VersionUpgrade/VersionUpgrade462to47/plugin.json index 5168912651..822be1fd2b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade462to47/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade462to47/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.6.2 to Cura 4.7.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json b/plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json index 908db388b9..94135ec600 100644 --- a/plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.7 to Cura 4.8.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json b/plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json index 19de9f1e03..50f50b65eb 100644 --- a/plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.8 to Cura 4.9.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade49to410/plugin.json b/plugins/VersionUpgrade/VersionUpgrade49to410/plugin.json index 9a36c5989c..9304ae7e18 100644 --- a/plugins/VersionUpgrade/VersionUpgrade49to410/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade49to410/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.9 to Cura 4.10.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index 0ceaf4327c..6e56adfe75 100644 --- a/plugins/X3DReader/plugin.json +++ b/plugins/X3DReader/plugin.json @@ -3,6 +3,6 @@ "author": "Seva Alekseyev, Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for reading X3D files.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/XRayView/XRayView.py b/plugins/XRayView/XRayView.py index be4fe5ea76..5af7b17652 100644 --- a/plugins/XRayView/XRayView.py +++ b/plugins/XRayView/XRayView.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path -from PyQt5.QtGui import QOpenGLContext, QImage +from PyQt6.QtGui import QOpenGLContext, QImage from UM.Application import Application from UM.Logger import Logger diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 11a2556339..80ea1f388d 100644 --- a/plugins/XRayView/plugin.json +++ b/plugins/XRayView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the X-Ray view.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/plugins/XmlMaterialProfile/PluginInfo.py b/plugins/XmlMaterialProfile/PluginInfo.py index 4b78a47a5e..b88bea21eb 100644 --- a/plugins/XmlMaterialProfile/PluginInfo.py +++ b/plugins/XmlMaterialProfile/PluginInfo.py @@ -5,9 +5,9 @@ class PluginInfo(PluginObject): __instance = None # type: PluginInfo def __init__(self, *args, **kwags): - super().__init__(*args, **kwags) if PluginInfo.__instance is not None: raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__) + super().__init__(*args, **kwags) PluginInfo.__instance = self @classmethod diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index 75a42b8edc..9609387946 100644 --- a/plugins/XmlMaterialProfile/plugin.json +++ b/plugins/XmlMaterialProfile/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides capabilities to read and write XML-based material profiles.", - "api": 7, + "api": 8, "i18n-catalog": "cura" } diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 501445f9d8..6ec6d5a7b9 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -6,7 +6,7 @@ "display_name": "3MF Reader", "description": "Provides support for reading 3MF files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -23,7 +23,7 @@ "display_name": "3MF Writer", "description": "Provides support for writing 3MF files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -40,7 +40,7 @@ "display_name": "AMF Reader", "description": "Provides support for reading AMF files.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -57,7 +57,7 @@ "display_name": "Cura Backups", "description": "Backup and restore your configuration.", "package_version": "1.2.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -74,7 +74,7 @@ "display_name": "CuraEngine Backend", "description": "Provides the link to the CuraEngine slicing backend.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -91,7 +91,7 @@ "display_name": "Cura Profile Reader", "description": "Provides support for importing Cura profiles.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -108,7 +108,7 @@ "display_name": "Cura Profile Writer", "description": "Provides support for exporting Cura profiles.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -125,7 +125,7 @@ "display_name": "Ultimaker Digital Library", "description": "Connects to the Digital Library, allowing Cura to open files from and save files to the Digital Library.", "package_version": "1.1.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -142,7 +142,7 @@ "display_name": "Firmware Update Checker", "description": "Checks for firmware updates.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -159,7 +159,7 @@ "display_name": "Firmware Updater", "description": "Provides a machine actions for updating firmware.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -176,7 +176,7 @@ "display_name": "Compressed G-code Reader", "description": "Reads g-code from a compressed archive.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -193,7 +193,7 @@ "display_name": "Compressed G-code Writer", "description": "Writes g-code to a compressed archive.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -210,7 +210,7 @@ "display_name": "G-Code Profile Reader", "description": "Provides support for importing profiles from g-code files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -227,7 +227,7 @@ "display_name": "G-Code Reader", "description": "Allows loading and displaying G-code files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "VictorLarchenko", @@ -244,7 +244,7 @@ "display_name": "G-Code Writer", "description": "Writes g-code to a file.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -261,7 +261,7 @@ "display_name": "Image Reader", "description": "Enables ability to generate printable geometry from 2D image files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -278,7 +278,7 @@ "display_name": "Legacy Cura Profile Reader", "description": "Provides support for importing profiles from legacy Cura versions.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -295,7 +295,7 @@ "display_name": "Machine Settings Action", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -312,7 +312,7 @@ "display_name": "Model Checker", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -329,7 +329,7 @@ "display_name": "Monitor Stage", "description": "Provides a monitor stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -346,7 +346,7 @@ "display_name": "Per-Object Settings Tool", "description": "Provides the per-model settings.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -363,7 +363,7 @@ "display_name": "Post Processing", "description": "Extension that allows for user created scripts for post processing.", "package_version": "2.2.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -380,7 +380,7 @@ "display_name": "Prepare Stage", "description": "Provides a prepare stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -397,7 +397,7 @@ "display_name": "Preview Stage", "description": "Provides a preview stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -414,7 +414,7 @@ "display_name": "Removable Drive Output Device", "description": "Provides removable drive hotplugging and writing support.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -431,7 +431,7 @@ "display_name": "Sentry Logger", "description": "Logs certain events so that they can be used by the crash reporter", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -448,7 +448,7 @@ "display_name": "Simulation View", "description": "Provides the Simulation view.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -465,7 +465,7 @@ "display_name": "Slice Info", "description": "Submits anonymous slice info. Can be disabled through preferences.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -482,7 +482,7 @@ "display_name": "Solid View", "description": "Provides a normal solid mesh view.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -499,7 +499,7 @@ "display_name": "Support Eraser Tool", "description": "Creates an eraser mesh to block the printing of support in certain places.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -516,7 +516,7 @@ "display_name": "Trimesh Reader", "description": "Provides support for reading model files.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -533,7 +533,7 @@ "display_name": "Marketplace", "description": "Find, manage and install new Cura packages.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -550,7 +550,7 @@ "display_name": "UFP Reader", "description": "Provides support for reading Ultimaker Format Packages.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -567,7 +567,7 @@ "display_name": "UFP Writer", "description": "Provides support for writing Ultimaker Format Packages.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -584,7 +584,7 @@ "display_name": "Ultimaker Machine Actions", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -601,7 +601,7 @@ "display_name": "UM3 Network Printing", "description": "Manages network connections to Ultimaker 3 printers.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -618,7 +618,7 @@ "display_name": "USB Printing", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "package_version": "1.0.2", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -635,7 +635,7 @@ "display_name": "Version Upgrade 2.1 to 2.2", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -652,7 +652,7 @@ "display_name": "Version Upgrade 2.2 to 2.4", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -669,7 +669,7 @@ "display_name": "Version Upgrade 2.5 to 2.6", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -686,7 +686,7 @@ "display_name": "Version Upgrade 2.6 to 2.7", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -703,7 +703,7 @@ "display_name": "Version Upgrade 2.7 to 3.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -720,7 +720,7 @@ "display_name": "Version Upgrade 3.0 to 3.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -737,7 +737,7 @@ "display_name": "Version Upgrade 3.2 to 3.3", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -754,7 +754,7 @@ "display_name": "Version Upgrade 3.3 to 3.4", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -771,7 +771,7 @@ "display_name": "Version Upgrade 3.4 to 3.5", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -788,7 +788,7 @@ "display_name": "Version Upgrade 3.5 to 4.0", "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -805,7 +805,7 @@ "display_name": "Version Upgrade 4.0 to 4.1", "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -822,7 +822,7 @@ "display_name": "Version Upgrade 4.1 to 4.2", "description": "Upgrades configurations from Cura 4.1 to Cura 4.2.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -839,7 +839,7 @@ "display_name": "Version Upgrade 4.2 to 4.3", "description": "Upgrades configurations from Cura 4.2 to Cura 4.3.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -856,7 +856,7 @@ "display_name": "Version Upgrade 4.3 to 4.4", "description": "Upgrades configurations from Cura 4.3 to Cura 4.4.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -873,7 +873,7 @@ "display_name": "Version Upgrade 4.4 to 4.5", "description": "Upgrades configurations from Cura 4.4 to Cura 4.5.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -890,7 +890,7 @@ "display_name": "Version Upgrade 4.5 to 4.6", "description": "Upgrades configurations from Cura 4.5 to Cura 4.6.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -907,7 +907,7 @@ "display_name": "Version Upgrade 4.6.0 to 4.6.2", "description": "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -924,7 +924,7 @@ "display_name": "Version Upgrade 4.6.2 to 4.7", "description": "Upgrades configurations from Cura 4.6.2 to Cura 4.7.", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -941,7 +941,7 @@ "display_name": "Version Upgrade 4.7.0 to 4.8.0", "description": "Upgrades configurations from Cura 4.7.0 to Cura 4.8.0", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -958,7 +958,7 @@ "display_name": "Version Upgrade 4.8.0 to 4.9.0", "description": "Upgrades configurations from Cura 4.8.0 to Cura 4.9.0", "package_version": "1.0.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -975,8 +975,7 @@ "display_name": "Version Upgrade 4.9 to 4.10", "description": "Upgrades configurations from Cura 4.9 to Cura 4.10", "package_version": "1.0.0", - "sdk_version": 7, - "sdk_version_semver": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -1011,7 +1010,7 @@ "display_name": "X3D Reader", "description": "Provides support for reading X3D files.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "SevaAlekseyev", @@ -1028,7 +1027,7 @@ "display_name": "XML Material Profiles", "description": "Provides capabilities to read and write XML-based material profiles.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -1045,7 +1044,7 @@ "display_name": "X-Ray View", "description": "Provides the X-Ray view.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -1062,7 +1061,7 @@ "display_name": "Generic ABS", "description": "The generic ABS profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1080,7 +1079,7 @@ "display_name": "Generic BAM", "description": "The generic BAM profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1098,7 +1097,7 @@ "display_name": "Generic CFF CPE", "description": "The generic CFF CPE profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1116,7 +1115,7 @@ "display_name": "Generic CFF PA", "description": "The generic CFF PA profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1134,7 +1133,7 @@ "display_name": "Generic CPE", "description": "The generic CPE profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1152,7 +1151,7 @@ "display_name": "Generic CPE+", "description": "The generic CPE+ profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1170,7 +1169,7 @@ "display_name": "Generic GFF CPE", "description": "The generic GFF CPE profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1188,7 +1187,7 @@ "display_name": "Generic GFF PA", "description": "The generic GFF PA profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1206,7 +1205,7 @@ "display_name": "Generic HIPS", "description": "The generic HIPS profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1224,7 +1223,7 @@ "display_name": "Generic Nylon", "description": "The generic Nylon profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1242,7 +1241,7 @@ "display_name": "Generic PC", "description": "The generic PC profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1260,7 +1259,7 @@ "display_name": "Generic PETG", "description": "The generic PETG profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1278,7 +1277,7 @@ "display_name": "Generic PLA", "description": "The generic PLA profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1296,7 +1295,7 @@ "display_name": "Generic PP", "description": "The generic PP profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1314,7 +1313,7 @@ "display_name": "Generic PVA", "description": "The generic PVA profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1332,7 +1331,7 @@ "display_name": "Generic Tough PLA", "description": "The generic Tough PLA profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1350,7 +1349,7 @@ "display_name": "Generic TPU", "description": "The generic TPU profile which other profiles can be based upon.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1368,7 +1367,7 @@ "display_name": "Dagoma Chromatik PLA", "description": "Filament testĂ© et approuvĂ© pour les imprimantes 3D Dagoma. Chromatik est l'idĂ©al pour dĂ©buter et suivre les tutoriels premiers pas. Il vous offre qualitĂ© et rĂ©sistance pour chacune de vos impressions.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://dagoma.fr/boutique/filaments.html", "author": { "author_id": "Dagoma", @@ -1385,7 +1384,7 @@ "display_name": "FABtotum ABS", "description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so it’s compatible with all versions of the FABtotum Personal fabricator.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40", "author": { "author_id": "FABtotum", @@ -1402,7 +1401,7 @@ "display_name": "FABtotum Nylon", "description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53", "author": { "author_id": "FABtotum", @@ -1419,7 +1418,7 @@ "display_name": "FABtotum PLA", "description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starch’s sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotum’s one is between 185° and 195°.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39", "author": { "author_id": "FABtotum", @@ -1436,7 +1435,7 @@ "display_name": "FABtotum TPU Shore 98A", "description": "", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66", "author": { "author_id": "FABtotum", @@ -1453,7 +1452,7 @@ "display_name": "Fiberlogy HD PLA", "description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS – better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/", "author": { "author_id": "Fiberlogy", @@ -1470,7 +1469,7 @@ "display_name": "Filo3D PLA", "description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://dagoma.fr", "author": { "author_id": "Dagoma", @@ -1487,7 +1486,7 @@ "display_name": "IMADE3D JellyBOX PETG", "description": "", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1504,7 +1503,7 @@ "display_name": "IMADE3D JellyBOX PLA", "description": "", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1521,7 +1520,7 @@ "display_name": "Octofiber PLA", "description": "PLA material from Octofiber.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://nl.octofiber.com/3d-printing-filament/pla.html", "author": { "author_id": "Octofiber", @@ -1538,7 +1537,7 @@ "display_name": "PolyFlexâ„¢ PLA", "description": "PolyFlexâ„¢ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlexâ„¢ opens up a completely new realm of applications.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://www.polymaker.com/shop/polyflex/", "author": { "author_id": "Polymaker", @@ -1555,7 +1554,7 @@ "display_name": "PolyMaxâ„¢ PLA", "description": "PolyMaxâ„¢ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMaxâ„¢ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://www.polymaker.com/shop/polymax/", "author": { "author_id": "Polymaker", @@ -1572,7 +1571,7 @@ "display_name": "PolyPlusâ„¢ PLA True Colour", "description": "PolyPlusâ„¢ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Freeâ„¢ technology that ensures consistent extrusion and prevents jams.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://www.polymaker.com/shop/polyplus-true-colour/", "author": { "author_id": "Polymaker", @@ -1589,7 +1588,7 @@ "display_name": "PolyWoodâ„¢ PLA", "description": "PolyWoodâ„¢ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Freeâ„¢ printing experience.", "package_version": "1.0.1", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "http://www.polymaker.com/shop/polywood/", "author": { "author_id": "Polymaker", @@ -1606,7 +1605,7 @@ "display_name": "Ultimaker ABS", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1625,7 +1624,7 @@ "display_name": "Ultimaker Breakaway", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/breakaway", "author": { "author_id": "UltimakerPackages", @@ -1644,7 +1643,7 @@ "display_name": "Ultimaker CPE", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1663,7 +1662,7 @@ "display_name": "Ultimaker CPE+", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/cpe", "author": { "author_id": "UltimakerPackages", @@ -1682,7 +1681,7 @@ "display_name": "Ultimaker Nylon", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1701,7 +1700,7 @@ "display_name": "Ultimaker PC", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/pc", "author": { "author_id": "UltimakerPackages", @@ -1720,7 +1719,7 @@ "display_name": "Ultimaker PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1739,7 +1738,7 @@ "display_name": "Ultimaker PP", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/pp", "author": { "author_id": "UltimakerPackages", @@ -1758,7 +1757,7 @@ "display_name": "Ultimaker PVA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1777,7 +1776,7 @@ "display_name": "Ultimaker TPU 95A", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/tpu-95a", "author": { "author_id": "UltimakerPackages", @@ -1796,7 +1795,7 @@ "display_name": "Ultimaker Tough PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://ultimaker.com/products/materials/tough-pla", "author": { "author_id": "UltimakerPackages", @@ -1815,7 +1814,7 @@ "display_name": "Vertex Delta ABS", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1832,7 +1831,7 @@ "display_name": "Vertex Delta PET", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1849,7 +1848,7 @@ "display_name": "Vertex Delta PLA", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1866,7 +1865,7 @@ "display_name": "Vertex Delta TPU", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.4.0", - "sdk_version": "7.9.0", + "sdk_version": "8.0.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index d738c5c474..84a0caede1 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -10,7 +10,7 @@ import Cura 1.1 as Cura Item { property var profile: Cura.API.account.userProfile - property var loggedIn: Cura.API.account.isLoggedIn + property bool loggedIn: Cura.API.account.isLoggedIn height: signInButton.visible ? signInButton.height : accountWidget.height width: signInButton.visible ? signInButton.width : accountWidget.width @@ -81,6 +81,7 @@ Item source: (loggedIn && profile["profile_image_url"]) ? profile["profile_image_url"] : "" outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining") + maskColor: UM.Theme.getColor("main_window_header_background") } contentItem: Item diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 58c39e5065..6a60e993d8 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -3,7 +3,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 import UM 1.4 as UM @@ -16,44 +15,46 @@ Item property alias source: profileImage.source property alias outlineColor: profileImageOutline.color + + // This should be set to the color behind the image + // It fills the space around a rectangular avatar to make the image under it look circular + property alias maskColor: profileImageMask.color property bool hasAvatar: source != "" + Rectangle + { + id: profileImageBackground + anchors.fill: parent + radius: width + color: "white" + } + Image { id: profileImage anchors.fill: parent fillMode: Image.PreserveAspectCrop - visible: false + visible: hasAvatar mipmap: true } - Rectangle + UM.ColorImage { + // This image is a rectangle with a hole in the middle. + // Since we don't have access to proper masking in QT6 yet this is used as a primitive masking replacement id: profileImageMask anchors.fill: parent - radius: width - color: hasAvatar ? "white" : "transparent" + source: UM.Theme.getIcon("CircleMask") } - OpacityMask - { - anchors.fill: parent - source: profileImage - maskSource: profileImageMask - visible: hasAvatar - cached: true - } - - UM.RecolorImage + UM.ColorImage { + // This creates the circle outline around the image id: profileImageOutline - anchors.centerIn: parent - // Make it a bit bigger than it has to, otherwise it sometimes shows a white border. - width: parent.width + 2 - height: parent.height + 2 + anchors.fill: parent + anchors.margins: .25 visible: hasAvatar source: UM.Theme.getIcon("CircleOutline") - sourceSize: Qt.size(parent.width, parent.height) color: UM.Theme.getColor("account_widget_outline_active") } } diff --git a/resources/qml/Account/SyncState.qml b/resources/qml/Account/SyncState.qml index e5731ff9d7..b558714376 100644 --- a/resources/qml/Account/SyncState.qml +++ b/resources/qml/Account/SyncState.qml @@ -13,37 +13,44 @@ Row // Sync state icon + message height: childrenRect.height spacing: UM.Theme.getSize("narrow_margin").height + // These are the enums from cura/API/account.py + // somehow exposing these enums from python to QML doesn't work properly anymore + property var _Cura_AccountSyncState_SYNCING: 0 + property var _Cura_AccountSyncState_SUCCESS: 1 + property var _Cura_AccountSyncState_ERROR: 2 + property var _Cura_AccountSyncState_IDLE: 3 + states: [ State { name: "idle" - when: syncState == Cura.AccountSyncState.IDLE + when: syncState == _Cura_AccountSyncState_IDLE PropertyChanges { target: icon; source: UM.Theme.getIcon("ArrowDoubleCircleRight")} }, State { name: "syncing" - when: syncState == Cura.AccountSyncState.SYNCING + when: syncState == _Cura_AccountSyncState_SYNCING PropertyChanges { target: icon; source: UM.Theme.getIcon("ArrowDoubleCircleRight") } PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Checking...")} }, State { name: "up_to_date" - when: syncState == Cura.AccountSyncState.SUCCESS + when: syncState == _Cura_AccountSyncState_SUCCESS PropertyChanges { target: icon; source: UM.Theme.getIcon("CheckCircle") } PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Account synced")} }, State { name: "error" - when: syncState == Cura.AccountSyncState.ERROR + when: syncState == _Cura_AccountSyncState_ERROR PropertyChanges { target: icon; source: UM.Theme.getIcon("Warning") } PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Something went wrong...")} } ] - UM.RecolorImage + UM.ColorImage { id: icon width: 20 * screenScaleFactor diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index be858df993..c8642681ae 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -30,6 +30,7 @@ Column height: UM.Theme.getSize("main_window_header").height source: profile["profile_image_url"] ? profile["profile_image_url"] : "" + maskColor: UM.Theme.getColor("main_background") outlineColor: UM.Theme.getColor("main_background") } Rectangle @@ -45,7 +46,7 @@ Column { id: initialLabel anchors.centerIn: parent - text: profile["username"].charAt(0).toUpperCase() + text: profile.username.charAt(0).toUpperCase() font: UM.Theme.getFont("large_bold") horizontalAlignment: Text.AlignHCenter } diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index fece44d287..21cdda0627 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -3,7 +3,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 // For the dropshadow import UM 1.5 as UM import Cura 1.0 as Cura @@ -47,7 +46,7 @@ Button leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("action_button").height + implicitHeight: UM.Theme.getSize("action_button").height hoverEnabled: true onHoveredChanged: @@ -63,14 +62,12 @@ Button spacing: UM.Theme.getSize("narrow_margin").width height: button.height //Left side icon. Only displayed if !isIconOnRightSide. - UM.RecolorImage + UM.ColorImage { id: buttonIconLeft source: "" height: visible ? button.iconSize : 0 width: visible ? height : 0 - sourceSize.width: width - sourceSize.height: height color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor) : button.textDisabledColor visible: source != "" && !button.isIconOnRightSide anchors.verticalCenter: parent.verticalCenter @@ -110,14 +107,12 @@ Button } //Right side icon. Only displayed if isIconOnRightSide. - UM.RecolorImage + UM.ColorImage { id: buttonIconRight source: buttonIconLeft.source height: visible ? button.iconSize : 0 width: visible ? height : 0 - sourceSize.width: width - sourceSize.height: height color: buttonIconLeft.color visible: source != "" && button.isIconOnRightSide anchors.verticalCenter: buttonIconLeft.verticalCenter @@ -160,6 +155,7 @@ Button height: parent.height visible: false + running: visible RotationAnimator { diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index d9923ce4e8..377bc3e979 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -7,7 +7,7 @@ import QtQuick.Controls 2.1 import UM 1.1 as UM import Cura 1.0 as Cura -UM.RecolorImage +UM.ColorImage { id: widget @@ -17,6 +17,8 @@ UM.RecolorImage color: UM.Theme.getColor("icon") + property var printMaterialCosts: PrintInformation.materialCosts + MouseArea { anchors.fill: parent @@ -37,8 +39,6 @@ UM.RecolorImage opacity: opened ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - contentWidth: printJobInformation.width - contentHeight: printJobInformation.implicitHeight contentItem: PrintJobInformation { diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 868f23d242..58bf736575 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -119,6 +119,7 @@ Column text: widget.waitingForSliceToStart ? catalog.i18nc("@button", "Processing"): catalog.i18nc("@button", "Slice") tooltip: catalog.i18nc("@label", "Start the slicing process") + hoverEnabled: !widget.waitingForSliceToStart enabled: widget.backendState != UM.Backend.Error && !widget.waitingForSliceToStart visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error onClicked: { diff --git a/resources/qml/ApplicationSwitcher/ApplicationButton.qml b/resources/qml/ApplicationSwitcher/ApplicationButton.qml index 182b80811b..3d67889220 100644 --- a/resources/qml/ApplicationSwitcher/ApplicationButton.qml +++ b/resources/qml/ApplicationSwitcher/ApplicationButton.qml @@ -45,7 +45,7 @@ Button anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - UM.RecolorImage + UM.ColorImage { id: applicationIcon anchors.horizontalCenter: parent.horizontalCenter @@ -54,7 +54,7 @@ Button width: UM.Theme.getSize("application_switcher_icon").width height: width - UM.RecolorImage + UM.ColorImage { id: externalLinkIndicatorIcon visible: base.isExternalLink diff --git a/resources/qml/ApplicationSwitcher/ApplicationSwitcher.qml b/resources/qml/ApplicationSwitcher/ApplicationSwitcher.qml index a8ea2312a5..69852270c1 100644 --- a/resources/qml/ApplicationSwitcher/ApplicationSwitcher.qml +++ b/resources/qml/ApplicationSwitcher/ApplicationSwitcher.qml @@ -31,7 +31,7 @@ Item Behavior on opacity { NumberAnimation { duration: 100; } } } - UM.RecolorImage + UM.ColorImage { anchors.fill: parent color: UM.Theme.getColor("primary_text") diff --git a/resources/qml/CategoryButton.qml b/resources/qml/CategoryButton.qml index b5d8bb2b2d..049ca032f0 100644 --- a/resources/qml/CategoryButton.qml +++ b/resources/qml/CategoryButton.qml @@ -78,7 +78,7 @@ Button id: content //spacing: UM.Theme.getSize("narrow_margin").width - UM.RecolorImage + UM.ColorImage { id: icon source: "" @@ -88,8 +88,6 @@ Button width: visible ? UM.Theme.getSize("section_icon").width: 0 height: UM.Theme.getSize("section_icon").height anchors.leftMargin: base.indented ? UM.Theme.getSize("default_margin").width: 0 - sourceSize.width: width - sourceSize.height: width } UM.Label @@ -106,15 +104,13 @@ Button color: UM.Theme.getColor("setting_category_text") } - UM.RecolorImage + UM.ColorImage { id: categoryArrow anchors.right: parent.right width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height anchors.verticalCenter: parent.verticalCenter - sourceSize.width: width - sourceSize.height: height color: UM.Theme.getColor("setting_control_button") source: expanded ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft") } diff --git a/resources/qml/ColorDialog.qml b/resources/qml/ColorDialog.qml index 6877ef34b6..49adcf0cca 100644 --- a/resources/qml/ColorDialog.qml +++ b/resources/qml/ColorDialog.qml @@ -1,4 +1,7 @@ -import QtQuick 2.10 +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Window 2.1 import QtQuick.Layouts 1.1 @@ -26,7 +29,7 @@ UM.Dialog // the size of the dialog ourselves. // Ugly workaround for windows having overlapping elements due to incorrect dialog width minimumWidth: content.width + (Qt.platform.os == "windows" ? 4 * margin : 2 * margin) - minimumHeight: content.height + buttonRow.height + (Qt.platform.os == "windows" ? 5 * margin : 3 * margin) + minimumHeight: content.height + buttonArea.height + (Qt.platform.os == "windows" ? 5 * margin : 3 * margin) property alias color: colorInput.text property var swatchColors: [ @@ -77,7 +80,7 @@ UM.Dialog implicitHeight: UM.Theme.getSize("medium_button_icon").height radius: width / 2 - UM.RecolorImage + UM.ColorImage { anchors.fill: parent visible: swatchColor == base.color @@ -104,7 +107,7 @@ UM.Dialog text: catalog.i18nc("@label", "Hex") } - TextField + Cura.TextField { id: colorInput Layout.fillWidth: true @@ -116,7 +119,7 @@ UM.Dialog text = `#${text}`; } } - validator: RegExpValidator { regExp: /^#([a-fA-F0-9]{0,6})$/ } + validator: RegularExpressionValidator { regularExpression: /^#([a-fA-F0-9]{0,6})$/ } } Rectangle diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 371754e9e0..3b06af677d 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -3,8 +3,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.15 -import QtQuick.Dialogs 1.2 -import QtGraphicalEffects 1.0 +import QtQuick.Dialogs + import UM 1.5 as UM import Cura 1.1 as Cura @@ -49,6 +49,22 @@ UM.MainWindow tooltip.hide(); } + MouseArea + { + // Hack introduced when switching to qt6 + // We used to be able to let the main window's default handlers control this, but something seems to be changed + // for qt6 in the ordering. TODO; We should find out what changed and have a less hacky fix for that. + enabled: parent.visible + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.AllButtons + onPositionChanged: (mouse) => {base.mouseMoved(mouse);} + onPressed: (mouse) => { base.mousePressed(mouse);} + onReleased: (mouse) => { base.mouseReleased(mouse);} + onWheel: (wheel) => {base.wheel(wheel)} + + } + Rectangle { id: greyOutBackground @@ -147,7 +163,7 @@ UM.MainWindow anchors.fill: parent //DeleteSelection on the keypress backspace event - Keys.onPressed: + Keys.onPressed: (event) => { if (event.key == Qt.Key_Backspace) { @@ -171,29 +187,10 @@ UM.MainWindow } height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height - LinearGradient + Rectangle { anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } + color: UM.Theme.getColor("main_window_header_background") } // This is a placeholder for adding a pattern in the header @@ -237,7 +234,7 @@ UM.MainWindow { // The drop area is here to handle files being dropped onto Cura. anchors.fill: parent - onDropped: + onDropped: (drop) => { if (drop.urls.length > 0) { @@ -246,7 +243,7 @@ UM.MainWindow for (var i = 0; i < drop.urls.length; i++) { var filename = drop.urls[i]; - if (filename.toLowerCase().endsWith(".curapackage")) + if (filename.toString().toLowerCase().endsWith(".curapackage")) { // Try to install plugin & close. CuraApplication.installPackageViaDragAndDrop(filename); @@ -574,7 +571,7 @@ UM.MainWindow id: contextMenu } - onPreClosing: + onPreClosing: (close) => { close.accepted = CuraApplication.getIsAllChecksPassed(); if (!close.accepted) @@ -636,24 +633,19 @@ UM.MainWindow //: File open dialog title title: catalog.i18nc("@title:window","Open file(s)") modality: Qt.WindowModal - selectMultiple: true + fileMode: FileDialog.OpenFiles nameFilters: UM.MeshFileHandler.supportedReadFileTypes; - folder: - { - //Because several implementations of the file dialog only update the folder when it is explicitly set. - folder = CuraApplication.getDefaultPath("dialog_load_path"); - return CuraApplication.getDefaultPath("dialog_load_path"); - } + currentFolder: CuraApplication.getDefaultPath("dialog_load_path") onAccepted: { // Because several implementations of the file dialog only update the folder // when it is explicitly set. - var f = folder; - folder = f; + var f = currentFolder; + currentFolder = f; - CuraApplication.setDefaultPath("dialog_load_path", folder); + CuraApplication.setDefaultPath("dialog_load_path", currentFolder); - handleOpenFileUrls(fileUrls); + handleOpenFileUrls(selectedFiles); } // Yeah... I know... it is a mess to put all those things here. @@ -745,7 +737,7 @@ UM.MainWindow { id: packageInstallDialog title: catalog.i18nc("@window:title", "Install Package") - standardButtons: StandardButton.Ok + standardButtons: Dialog.Ok } Cura.MessageDialog diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index e6d15af2d9..738c72f52d 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -137,7 +137,7 @@ UM.Dialog projectsModel.append({ name: "libArcus", description: catalog.i18nc("@label", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" }); projectsModel.append({ name: "Python", description: catalog.i18nc("@label", "Programming language"), license: "Python", url: "http://python.org/" }); - projectsModel.append({ name: "Qt5", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" }); + projectsModel.append({ name: "Qt6", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" }); projectsModel.append({ name: "PyQt", description: catalog.i18nc("@label", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" }); projectsModel.append({ name: "SIP", description: catalog.i18nc("@label", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" }); projectsModel.append({ name: "Protobuf", description: catalog.i18nc("@label", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" }); diff --git a/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml b/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml index bcace5a8b1..0579cb3c30 100644 --- a/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml +++ b/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml @@ -1,11 +1,10 @@ //Copyright (c) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import Qt.labs.qmlmodels 1.0 import QtQuick 2.1 import QtQuick.Controls 2.15 -import UM 1.5 as UM +import UM 1.6 as UM import Cura 1.6 as Cura UM.Dialog @@ -77,11 +76,9 @@ UM.Dialog Cura.MachineManager.activeQualityDisplayNameMap["main"], catalog.i18nc("@title:column", "Current changes") ] - model: TableModel + model: UM.TableModel { - TableModelColumn { display: "label" } - TableModelColumn { display: "original_value" } - TableModelColumn { display: "user_value" } + headers: ["label", "original_value", "user_value"] rows: userChangesModel.items } sectionRole: "category" diff --git a/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml b/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml index 18891cebee..c33cce1505 100644 --- a/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml +++ b/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml @@ -40,7 +40,7 @@ UM.Dialog } } - onAccepted: loadModelFiles(base.fileUrls) + onAccepted: loadModelFiles(base.selectedFiles) UM.Label { diff --git a/resources/qml/EmptyViewMenuComponent.qml b/resources/qml/EmptyViewMenuComponent.qml index 10a50ea023..61d821ccb6 100644 --- a/resources/qml/EmptyViewMenuComponent.qml +++ b/resources/qml/EmptyViewMenuComponent.qml @@ -3,7 +3,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 // For the dropshadow + import UM 1.2 as UM @@ -12,6 +12,8 @@ Rectangle { color: UM.Theme.getColor("disabled") + /* + TODO: Reimplement later DropShadow { id: shadow @@ -24,5 +26,5 @@ Rectangle color: UM.Theme.getColor("action_button_shadow") // Should always be drawn behind the background. z: parent.z - 1 - } + }*/ } diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index e5eab3bd93..9cc84e18dc 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -155,7 +155,7 @@ Item } } - UM.RecolorImage + UM.ColorImage { id: collapseButton anchors @@ -249,13 +249,13 @@ Item } property var clickPos: Qt.point(0, 0) property bool dragging: false - onPressed: + onPressed: (mouse) => { clickPos = Qt.point(mouse.x, mouse.y); dragging = true } - onPositionChanged: + onPositionChanged: (mouse) => { if(dragging) { @@ -266,10 +266,8 @@ Item } } } - onReleased: - { - dragging = false - } + onReleased: dragging = false + onDoubleClicked: { @@ -305,14 +303,6 @@ Item padding: UM.Theme.getSize("default_margin").width contentItem: Item {} - - onContentItemChanged: - { - // Since we want the size of the content to be set by the size of the content, - // we need to do it like this. - content.width = contentItem.width + 2 * content.padding - content.height = contentItem.height + 2 * content.padding - } } } @@ -322,10 +312,8 @@ Item { // Since it could be that the content is dynamically populated, we should also take these changes into account. target: content.contentItem - function onWidthChanged() { content.width = content.contentItem.width + 2 * content.padding } function onHeightChanged() { - content.height = content.contentItem.height + 2 * content.padding contentContainer.height = contentHeader.height + content.height } } diff --git a/resources/qml/ExpandableComponentHeader.qml b/resources/qml/ExpandableComponentHeader.qml index 7176f0978b..de60faba2f 100644 --- a/resources/qml/ExpandableComponentHeader.qml +++ b/resources/qml/ExpandableComponentHeader.qml @@ -51,10 +51,9 @@ Cura.RoundedRectangle verticalCenter: parent.verticalCenter } - contentItem: UM.RecolorImage + contentItem: UM.ColorImage { anchors.fill: parent - sourceSize.width: width color: closeButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") source: UM.Theme.getIcon("Cancel") } diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index 219608a762..967ef09767 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -27,6 +27,12 @@ Item // The contentItem holds the QML item that is shown when the "open" button is pressed property alias contentItem: content.contentItem + // If the contentItem is a Layout (eg Column) you must use these to set the popup size otherwise you end up with a + // binding loop between the popup and the contentItem + // ImplicitWidth/ImplicitHeight can be used instead in the contentItem if it is not a Layout. + property alias contentWidth: content.width + property alias contentHeight: content.height + property color contentBackgroundColor: UM.Theme.getColor("action_button") property color headerBackgroundColor: UM.Theme.getColor("action_button") @@ -168,7 +174,7 @@ Item anchors.bottom: parent.bottom } - UM.RecolorImage + UM.ColorImage { id: collapseButton anchors @@ -211,23 +217,5 @@ Item } contentItem: Item {} - - onContentItemChanged: - { - // Since we want the size of the content to be set by the size of the content, - // we need to do it like this. - content.width = contentItem.width + 2 * content.padding - content.height = contentItem.height + 2 * content.padding - } - } - - // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item. - // Apparently the order in which these are handled matters and so the height is correctly updated if this is here. - Connections - { - // Since it could be that the content is dynamically populated, we should also take these changes into account. - target: content.contentItem - function onWidthChanged() { content.width = content.contentItem.width + 2 * content.padding } - function onHeightChanged() { content.height = content.contentItem.height + 2 * content.padding } } } diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index b9141a449a..fb3269ca78 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -15,7 +15,7 @@ Item property color materialColor property alias textColor: extruderNumberText.color property bool extruderEnabled: true - property alias iconSize: mainIcon.sourceSize + property var iconSize property string iconVariant: "medium" Item @@ -24,19 +24,21 @@ Item anchors.fill: parent layer.enabled: true // Prevent weird opacity effects. - UM.RecolorImage + UM.ColorImage { anchors.fill: parent - sourceSize: mainIcon.sourceSize + width: mainIcon.width + height: mainIcon.height source: UM.Theme.getIcon("ExtruderColor", iconVariant) color: materialColor } - UM.RecolorImage + UM.ColorImage { id: mainIcon anchors.fill: parent - sourceSize: UM.Theme.getSize("extruder_icon") + width: UM.Theme.getSize("extruder_icon").width + height: UM.Theme.getSize("extruder_icon").height source: UM.Theme.getIcon("Extruder", iconVariant) color: extruderNumberText.color diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index d7b4adfa8c..cc0834d7ea 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -35,7 +35,7 @@ Item implicitWidth: icon.width + 100 implicitHeight: icon.height - UM.RecolorImage + UM.ColorImage { id: icon width: UM.Theme.getSize("section_icon").width diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 1a5f40466a..d2ca855e8b 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -1,7 +1,7 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.15 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.1 @@ -97,8 +97,8 @@ Item cursorPosition = 0 } - validator: RegExpValidator { - regExp: /^[^\\\/\*\?\|\[\]]*$/ + validator: RegularExpressionValidator { + regularExpression: /^[^\\\/\*\?\|\[\]]*$/ } color: UM.Theme.getColor("text_scene") background: Item {} diff --git a/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml b/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml index 51c9730052..b5147d63db 100644 --- a/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml +++ b/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml @@ -1,7 +1,7 @@ // Copyright (c) 2020 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.10 +import QtQuick 2.15 import QtQuick.Controls 2.3 import UM 1.5 as UM @@ -172,9 +172,9 @@ UM.TooltipArea return "^%0\\d{0,%1}[.,]?\\d{0,%2}$".arg(minus).arg(digits).arg(numericTextFieldWithUnit.decimals) } } - validator: RegExpValidator + validator: RegularExpressionValidator { - regExp: new RegExp(textFieldWithUnit.validatorString) + regularExpression: new RegExp(textFieldWithUnit.validatorString) } //Enforce actual minimum and maximum values. diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 49d6710606..e8917517dd 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -88,7 +88,7 @@ Button visible: !configurationItem.isValidMaterial - UM.RecolorImage + UM.ColorImage { id: icon anchors.verticalCenter: unknownMaterialMessage.verticalCenter diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 58d96f8028..8d4fc9c2b5 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -57,7 +57,7 @@ Cura.ExpandablePopup property var extruderStack: activeMachine ? activeMachine.extruderList[model.index]: null property bool valueWarning: !Cura.ExtruderManager.getExtruderHasQualityForMaterial(extruderStack) - property bool valueError: activeMachine ? Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible", "") != "True" : false + property bool valueError: activeMachine ? Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible") != "True" : false // Extruder icon. Shows extruder index and has the same color as the active material. Cura.ExtruderIcon @@ -109,7 +109,7 @@ Cura.ExpandablePopup } // Warning icon that indicates if no qualities are available for the variant/material combination for this extruder - UM.RecolorImage + UM.ColorImage { id: badge anchors @@ -257,11 +257,10 @@ Cura.ExpandablePopup } } + contentWidth: UM.Theme.getSize("configuration_selector").width contentItem: Column { id: popupItem - width: UM.Theme.getSize("configuration_selector").width - height: implicitHeight // Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. padding: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 4b2b84d39e..7223706e63 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -202,7 +202,7 @@ Item return paddedWidth - textWidth - UM.Theme.getSize("print_setup_big_item").height * 0.5 - UM.Theme.getSize("default_margin").width } } - property string instructionLink: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "instruction_link", ""): "" + property string instructionLink: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "instruction_link"): "" Row { @@ -269,7 +269,7 @@ Item { id: materialSelection - property bool valueError: Cura.MachineManager.activeStack !== null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") !== "True" : true + property bool valueError: Cura.MachineManager.activeStack !== null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible") !== "True" : true property bool valueWarning: !Cura.MachineManager.isActiveQualitySupported text: Cura.MachineManager.activeStack !== null ? Cura.MachineManager.activeStack.material.name : "" @@ -304,7 +304,7 @@ Item height: UM.Theme.getSize("small_button").height width: UM.Theme.getSize("small_button").width anchors.centerIn: parent - background: UM.RecolorImage + background: UM.ColorImage { source: UM.Theme.getIcon("Guide") color: instructionButton.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("icon") @@ -369,15 +369,13 @@ Item width: selectors.controlWidth height: childrenRect.height - UM.RecolorImage + UM.ColorImage { id: warningImage anchors.left: parent.left source: UM.Theme.getIcon("Warning") width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - sourceSize.width: width - sourceSize.height: height color: UM.Theme.getColor("material_compatibility_warning") visible: !Cura.MachineManager.isCurrentSetupSupported || warnings.buildplateCompatibilityError || warnings.buildplateCompatibilityWarning } diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 1bba79e2c1..65f3409c8a 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -45,8 +45,8 @@ Cura.Menu shortcut: "Ctrl+" + (model.index + 1) } // Add it to the fifth position (and above) as we want it to be added after the extruder header. - onObjectAdded: base.insertItem(index + 5, object) - onObjectRemoved: base.removeItem(object) + onObjectAdded: function(index, object) { base.insertItem(index + 5, object) } + onObjectRemoved: function(index, object) { base.removeItem(object) } } // Global actions @@ -134,6 +134,7 @@ Cura.Menu from: 1 to: 99 width: 2 * UM.Theme.getSize("button").width + value: 1 } } } diff --git a/resources/qml/Menus/ExtensionMenu.qml b/resources/qml/Menus/ExtensionMenu.qml index 3c2d1a79c7..919e094e5e 100644 --- a/resources/qml/Menus/ExtensionMenu.qml +++ b/resources/qml/Menus/ExtensionMenu.qml @@ -53,12 +53,12 @@ Cura.Menu sourceComponent: modelText.trim() == "" ? extensionsMenuSeparator : extensionsMenuItem } - onObjectAdded: sub_menu.insertItem(index, object.item) - onObjectRemoved: sub_menu.removeItem(object.item) + onObjectAdded: function(index, object) { sub_menu.insertItem(index, object.item)} + onObjectRemoved: function(index, object) { sub_menu.removeItem(object.item)} } } - onObjectAdded: extensionMenu.insertMenu(index, object) - onObjectRemoved: extensionMenu.removeMenu(object) + onObjectAdded: function(index, object) { extensionMenu.insertMenu(index, object) } + onObjectRemoved: function(index, object) { extensionMenu.removeMenu(object)} } } \ No newline at end of file diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index 338ada60fc..0884053ef3 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -33,6 +33,7 @@ Cura.Menu id: openFilesMenu shouldBeVisible: base.fileProviderModel.count > 1 + enabled: shouldBeVisible } RecentFilesMenu { } diff --git a/resources/qml/Menus/MaterialBrandMenu.qml b/resources/qml/Menus/MaterialBrandMenu.qml new file mode 100644 index 0000000000..e256e75904 --- /dev/null +++ b/resources/qml/Menus/MaterialBrandMenu.qml @@ -0,0 +1,345 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 2.7 + +import UM 1.5 as UM +import Cura 1.7 as Cura + +/* This element is a workaround for MacOS, where it can crash in Qt6 when nested menus are closed. +Instead we'll use a pop-up which doesn't seem to have that problem. */ + +Cura.MenuItem +{ + id: materialBrandMenu + overrideShowArrow: true + + property var materialTypesModel + text: materialTypesModel.name + + contentItem: MouseArea + { + hoverEnabled: true + + RowLayout + { + spacing: 0 + opacity: materialBrandMenu.enabled ? 1 : 0.5 + + Item + { + // Spacer + width: UM.Theme.getSize("default_margin").width + } + + UM.Label + { + text: replaceText(materialBrandMenu.text) + Layout.fillWidth: true + Layout.fillHeight:true + elide: Label.ElideRight + wrapMode: Text.NoWrap + } + + Item + { + Layout.fillWidth: true + } + + Item + { + // Right side margin + width: UM.Theme.getSize("default_margin").width + } + } + + onEntered: showTimer.restartTimer() + onExited: hideTimer.restartTimer() + } + + Timer + { + id: showTimer + interval: 250 + function restartTimer() + { + restart(); + running = Qt.binding(function() { return materialBrandMenu.enabled && materialBrandMenu.contentItem.containsMouse; }); + hideTimer.running = false; + } + onTriggered: menuPopup.open() + } + Timer + { + id: hideTimer + interval: 250 + function restartTimer() //Restart but re-evaluate the running property then. + { + restart(); + running = Qt.binding(function() { return materialBrandMenu.enabled && !materialBrandMenu.contentItem.containsMouse && !menuPopup.itemHovered > 0; }); + showTimer.running = false; + } + onTriggered: menuPopup.close() + } + + Popup + { + id: menuPopup + width: materialTypesList.width + padding * 2 + height: materialTypesList.height + padding * 2 + + property var flipped: false + + x: parent.width - UM.Theme.getSize("default_lining").width + y: { + // Checks if popup is more than halfway down the screen AND further than 400 down (this avoids popup going off the top of screen) + // If it is then the popup will push up instead of down + // This fixes the popups appearing bellow the bottom of the screen. + + if (materialBrandMenu.parent.height / 2 < parent.y && parent.y > 400) { + flipped = true + return -UM.Theme.getSize("default_lining").width - height + UM.Theme.getSize("menu").height + } + flipped = false + return -UM.Theme.getSize("default_lining").width + } + + padding: background.border.width + // Nasty hack to ensure that we can keep track if the popup contains the mouse. + // Since we also want a hover for the sub items (and these events are sent async) + // We have to keep a count of itemHovered (instead of just a bool) + property int itemHovered: 0 + MouseArea + { + id: submenuArea + anchors.fill: parent + + hoverEnabled: true + onEntered: hideTimer.restartTimer() + } + + background: Rectangle + { + color: UM.Theme.getColor("main_background") + border.color: UM.Theme.getColor("lining") + border.width: UM.Theme.getSize("default_lining").width + } + + Column + { + id: materialTypesList + spacing: 0 + + property var brandMaterials: materialTypesModel.material_types + + Repeater + { + model: parent.brandMaterials + + //Use a MouseArea and Rectangle, not a button, because the button grabs mouse events which makes the parent pop-up think it's no longer being hovered. + //With a custom MouseArea, we can prevent the events from being accepted. + delegate: Rectangle + { + id: brandMaterialBase + height: UM.Theme.getSize("menu").height + width: UM.Theme.getSize("menu").width + + color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1") + + property var isFlipped: menuPopup.flipped + + RowLayout + { + spacing: 0 + opacity: materialBrandMenu.enabled ? 1 : 0.5 + height: parent.height + width: parent.width + + Item + { + // Spacer + width: UM.Theme.getSize("default_margin").width + } + + UM.Label + { + text: model.name + Layout.fillWidth: true + Layout.fillHeight: true + elide: Label.ElideRight + wrapMode: Text.NoWrap + } + + Item + { + Layout.fillWidth: true + } + + UM.ColorImage + { + height: UM.Theme.getSize("default_arrow").height + width: UM.Theme.getSize("default_arrow").width + color: UM.Theme.getColor("setting_control_text") + source: UM.Theme.getIcon("ChevronSingleRight") + } + + Item + { + // Right side margin + width: UM.Theme.getSize("default_margin").width + } + } + + MouseArea + { + id: materialTypeButton + anchors.fill: parent + + hoverEnabled: true + acceptedButtons: Qt.NoButton + + onEntered: + { + menuPopup.itemHovered += 1; + showSubTimer.restartTimer(); + } + onExited: + { + menuPopup.itemHovered -= 1; + hideSubTimer.restartTimer(); + } + } + Timer + { + id: showSubTimer + interval: 250 + function restartTimer() + { + restart(); + running = Qt.binding(function() { return materialTypeButton.containsMouse; }); + hideSubTimer.running = false; + } + onTriggered: colorPopup.open() + } + Timer + { + id: hideSubTimer + interval: 250 + function restartTimer() //Restart but re-evaluate the running property then. + { + restart(); + running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; }); + showSubTimer.running = false; + } + onTriggered: colorPopup.close() + } + + Popup + { + id: colorPopup + width: materialColorsList.width + padding * 2 + height: materialColorsList.height + padding * 2 + x: parent.width + y: { + // If flipped the popup should push up rather than down from the parent + if (brandMaterialBase.isFlipped) { + return -height + UM.Theme.getSize("menu").height + UM.Theme.getSize("default_lining").width + } + return -UM.Theme.getSize("default_lining").width + } + + property int itemHovered: 0 + padding: background.border.width + + background: Rectangle + { + color: UM.Theme.getColor("main_background") + border.color: UM.Theme.getColor("lining") + border.width: UM.Theme.getSize("default_lining").width + } + + Column + { + id: materialColorsList + property var brandColors: model.colors + spacing: 0 + + Repeater + { + model: parent.brandColors + + delegate: Rectangle + { + height: UM.Theme.getSize("menu").height + width: UM.Theme.getSize("menu").width + + color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1") + + Item + { + opacity: materialBrandMenu.enabled ? 1 : 0.5 + anchors.fill: parent + + //Checkmark, if the material is selected. + UM.ColorImage + { + id: checkmark + visible: model.id === materialMenu.activeMaterialId + height: UM.Theme.getSize("default_arrow").height + width: height + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter + source: UM.Theme.getIcon("Check", "low") + color: UM.Theme.getColor("setting_control_text") + } + + UM.Label + { + text: model.name + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_arrow").height + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + elide: Label.ElideRight + wrapMode: Text.NoWrap + } + } + + MouseArea + { + id: materialColorButton + anchors.fill: parent + + hoverEnabled: true + onClicked: + { + Cura.MachineManager.setMaterial(extruderIndex, model.container_node); + menuPopup.close(); + colorPopup.close(); + materialMenu.close(); + } + onEntered: + { + menuPopup.itemHovered += 1; + colorPopup.itemHovered += 1; + } + onExited: + { + menuPopup.itemHovered -= 1; + colorPopup.itemHovered -= 1; + } + } + } + } + } + } + } + } + } + } +} diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index e6d7fbc3e9..cee28cee6a 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -66,8 +66,8 @@ Cura.Menu checked: model.root_material_id === materialMenu.currentRootMaterialId onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) } - onObjectAdded: materialMenu.insertItem(index + 1, object) - onObjectRemoved: materialMenu.removeItem(index) + onObjectAdded: function(index, object) { materialMenu.insertItem(index + 1, object) } + onObjectRemoved: function(index, object) { materialMenu.removeItem(index) } } Cura.MenuSeparator { visible: favoriteMaterialsModel.items.length > 0} @@ -88,8 +88,8 @@ Cura.Menu checked: model.root_material_id === materialMenu.currentRootMaterialId onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) } - onObjectAdded: genericMenu.insertItem(index, object) - onObjectRemoved: genericMenu.removeItem(index) + onObjectAdded: function(index, object) { genericMenu.insertItem(index, object)} + onObjectRemoved: function(index, object) {genericMenu.removeItem(index) } } } @@ -98,45 +98,12 @@ Cura.Menu Instantiator { model: brandModel - Cura.Menu + delegate: Cura.MaterialBrandMenu { - id: brandMenu - title: brandName - property string brandName: model.name - property var brandMaterials: model.material_types - - Instantiator - { - model: brandMaterials - delegate: Cura.Menu - { - id: brandMaterialsMenu - title: materialName - property string materialName: model.name - property var brandMaterialColors: model.colors - - Instantiator - { - model: brandMaterialColors - delegate: Cura.MenuItem - { - text: model.name - checkable: true - enabled: isActiveExtruderEnabled - checked: model.id === materialMenu.activeMaterialId - - onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) - } - onObjectAdded: brandMaterialsMenu.insertItem(index, object) - onObjectRemoved: brandMaterialsMenu.removeItem(object) - } - } - onObjectAdded: brandMenu.insertMenu(index, object) - onObjectRemoved: brandMenu.removeMenu(object) - } + materialTypesModel: model } - onObjectAdded: materialMenu.insertMenu(index + 4, object) - onObjectRemoved: materialMenu.removeMenu(object) + onObjectAdded: function(index, object) { materialMenu.insertItem(index + 4, object)} + onObjectRemoved: function(index, object) { materialMenu.removeItem(index) } } Cura.MenuSeparator {} diff --git a/resources/qml/Menus/NozzleMenu.qml b/resources/qml/Menus/NozzleMenu.qml index b320a02a5e..f286410a11 100644 --- a/resources/qml/Menus/NozzleMenu.qml +++ b/resources/qml/Menus/NozzleMenu.qml @@ -49,8 +49,8 @@ Cura.Menu onTriggered: Cura.MachineManager.setVariant(nozzleMenu.extruderIndex, model.container_node) } - onObjectAdded: nozzleMenu.insertItem(index, object) - onObjectRemoved: nozzleMenu.removeItem(object) + onObjectAdded: function(index, object) { nozzleMenu.insertItem(index, object) } + onObjectRemoved: function(index, object) {nozzleMenu.removeItem(object)} } } diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index de5ab00c76..ca838b5cb3 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -34,8 +34,8 @@ Cura.Menu } shortcut: model.shortcut } - onObjectAdded: openFilesMenu.insertItem(index, object) + onObjectAdded: function(index, object) { openFilesMenu.insertItem(index, object)} - onObjectRemoved: openFilesMenu.removeItem(object) + onObjectRemoved: function(index, object) { openFilesMenu.removeItem(object) } } } diff --git a/resources/qml/Menus/PrinterMenu.qml b/resources/qml/Menus/PrinterMenu.qml index 9f0e0575b0..7cea1de52e 100644 --- a/resources/qml/Menus/PrinterMenu.qml +++ b/resources/qml/Menus/PrinterMenu.qml @@ -38,8 +38,8 @@ Cura.Menu checked: Cura.MachineManager.activeMachineNetworkGroupName == connectGroupName onTriggered: Cura.MachineManager.setActiveMachine(model.id) } - onObjectAdded: menu.insertItem(2, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: function(index, object) { menu.insertItem(2, object)} + onObjectRemoved: function(index, object) { menu.removeItem(object)} } Cura.MenuSeparator { visible: networKPrinterInstantiator.count > 0 } @@ -66,8 +66,8 @@ Cura.Menu onTriggered: Cura.MachineManager.setActiveMachine(model.id) } // A bit hackish, but we have 2 items at the end, put them before that - onObjectAdded: menu.insertItem(menu.count - 2, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: function(index, object) { menu.insertItem(menu.count - 2, object) } + onObjectRemoved: function(index, object) { menu.removeItem(object) } } Cura.MenuSeparator { visible: localPrinterInstantiator.count > 0 } diff --git a/resources/qml/Menus/PrinterTypeMenu.qml b/resources/qml/Menus/PrinterTypeMenu.qml index 17986ac1b8..8691bf7ecc 100644 --- a/resources/qml/Menus/PrinterTypeMenu.qml +++ b/resources/qml/Menus/PrinterTypeMenu.qml @@ -28,7 +28,7 @@ Cura.Menu Cura.MachineManager.switchPrinterType(modelData.machine_type) } } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: function(index, object) { return menu.insertItem(index, object); } + onObjectRemoved: function(index, object) { return menu.removeItem(object); } } } diff --git a/resources/qml/Menus/RecentFilesMenu.qml b/resources/qml/Menus/RecentFilesMenu.qml index 8f4f328927..19ff681219 100644 --- a/resources/qml/Menus/RecentFilesMenu.qml +++ b/resources/qml/Menus/RecentFilesMenu.qml @@ -29,7 +29,7 @@ Cura.Menu } onTriggered: CuraApplication.readLocalFile(modelData) } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: (index, object) => menu.insertItem(index, object) + onObjectRemoved: (object) => menu.removeItem(object) } } diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml index 16d54382d1..2140d5e0ef 100644 --- a/resources/qml/Menus/SaveProjectMenu.qml +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -43,8 +43,8 @@ Cura.Menu shortcut: model.shortcut enabled: saveProjectMenu.shouldBeVisible } - onObjectAdded: saveProjectMenu.insertItem(index, object) - onObjectRemoved: saveProjectMenu.removeItem(object) + onObjectAdded: function(index, object) { saveProjectMenu.insertItem(index, object)} + onObjectRemoved: function(index, object) { saveProjectMenu.removeItem(object)} } WorkspaceSummaryDialog diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 08d74a74ce..57298030c4 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -29,14 +29,11 @@ Cura.Menu checkable: true checked: modelData.presetId == settingVisibilityPresetsModel.activePreset ActionGroup.group: group - onTriggered: - { - settingVisibilityPresetsModel.setActivePreset(modelData.presetId); - } + onTriggered: settingVisibilityPresetsModel.setActivePreset(modelData.presetId) } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: function(index, object) { menu.insertItem(index, object) } + onObjectRemoved: function(index, object) { menu.removeItem(object)} } Cura.MenuSeparator {} diff --git a/resources/qml/Menus/SettingsMenu.qml b/resources/qml/Menus/SettingsMenu.qml index 25bee871f5..ea5de6c66f 100644 --- a/resources/qml/Menus/SettingsMenu.qml +++ b/resources/qml/Menus/SettingsMenu.qml @@ -67,8 +67,8 @@ Cura.Menu height: visible ? implicitHeight: 0 } } - onObjectAdded: base.insertMenu(index, object) - onObjectRemoved: base.removeMenu(object) + onObjectAdded: function(index, object) { base.insertMenu(index, object) } + onObjectRemoved: function(index, object) { base.removeMenu(object);} } Cura.MenuSeparator { } diff --git a/resources/qml/ObjectItemButton.qml b/resources/qml/ObjectItemButton.qml index c3066b800d..407a0298f4 100644 --- a/resources/qml/ObjectItemButton.qml +++ b/resources/qml/ObjectItemButton.qml @@ -146,7 +146,7 @@ Button labelText: perObjectSettingsCount.toString() } - UM.RecolorImage + UM.ColorImage { id: meshTypeIcon anchors diff --git a/resources/qml/ObjectSelector.qml b/resources/qml/ObjectSelector.qml index db68ff6c03..fdb4697a7e 100644 --- a/resources/qml/ObjectSelector.qml +++ b/resources/qml/ObjectSelector.qml @@ -1,3 +1,4 @@ + // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. @@ -40,12 +41,11 @@ Item width: parent.width height: label.height - UM.RecolorImage + UM.ColorImage { id: openCloseIcon width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width anchors.left: parent.left color: openCloseButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") source: objectSelector.opened ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleUp") diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index c4200e2351..ca8019ea75 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -224,7 +224,7 @@ UM.PreferencesPage textRole: "text" model: languageList implicitWidth: UM.Theme.getSize("combobox").width - implicitHeight: currencyField.height + height: currencyField.height function setCurrentIndex() { var code = UM.Preferences.getValue("general/language"); @@ -264,6 +264,7 @@ UM.PreferencesPage selectByMouse: true text: UM.Preferences.getValue("cura/currency") implicitWidth: UM.Theme.getSize("combobox").width + implicitHeight: UM.Theme.getSize("setting_control").height onTextChanged: UM.Preferences.setValue("cura/currency", text) } @@ -293,7 +294,7 @@ UM.PreferencesPage model: themeList textRole: "text" implicitWidth: UM.Theme.getSize("combobox").width - implicitHeight: currencyField.height + height: currencyField.height currentIndex: { diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index 9f7eeccc54..6f404ad738 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 import UM 1.5 as UM diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index bb40187825..913e213dae 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -2,8 +2,8 @@ // Uranium is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 +import QtQuick.Dialogs import UM 1.2 as UM import Cura 1.0 as Cura diff --git a/resources/qml/Preferences/Materials/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml index 2774c2a204..22f8091314 100644 --- a/resources/qml/Preferences/Materials/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -3,7 +3,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.15 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs import UM 1.5 as UM import Cura 1.5 as Cura @@ -226,12 +226,12 @@ UM.ManagementPage { id: importMaterialDialog title: catalog.i18nc("@title:window", "Import Material") - selectExisting: true + fileMode: FileDialog.OpenFile nameFilters: Cura.ContainerManager.getContainerNameFilters("material") - folder: CuraApplication.getDefaultPath("dialog_material_path") + currentFolder: CuraApplication.getDefaultPath("dialog_material_path") onAccepted: { - const result = Cura.ContainerManager.importMaterialContainer(fileUrl); + const result = Cura.ContainerManager.importMaterialContainer(selectedFile); const messageDialog = Qt.createQmlObject("import Cura 1.5 as Cura; Cura.MessageDialog { onClosed: destroy() }", base); messageDialog.standardButtons = Dialog.Ok; @@ -239,14 +239,14 @@ UM.ManagementPage switch (result.status) { case "success": - messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag !", "Successfully imported material %1").arg(fileUrl); + messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag !", "Successfully imported material %1").arg(selectedFile); break; default: - messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags or !", "Could not import material %1: %2").arg(fileUrl).arg(result.message); + messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags or !", "Could not import material %1: %2").arg(selectedFile).arg(result.message); break; } messageDialog.open(); - CuraApplication.setDefaultPath("dialog_material_path", folder); + CuraApplication.setDefaultPath("dialog_material_path", currentFolder); } } @@ -254,12 +254,14 @@ UM.ManagementPage { id: exportMaterialDialog title: catalog.i18nc("@title:window", "Export Material") - selectExisting: false + fileMode: FileDialog.SaveFile nameFilters: Cura.ContainerManager.getContainerNameFilters("material") - folder: CuraApplication.getDefaultPath("dialog_material_path") + currentFolder: CuraApplication.getDefaultPath("dialog_material_path") onAccepted: { - const result = Cura.ContainerManager.exportContainer(base.currentItem.root_material_id, selectedNameFilter, fileUrl); + const nameFilterString = selectedNameFilter.index >= 0 ? nameFilters[selectedNameFilter.index] : nameFilters[0]; + + const result = Cura.ContainerManager.exportContainer(base.currentItem.root_material_id, nameFilterString, selectedFile); const messageDialog = Qt.createQmlObject("import Cura 1.5 as Cura; Cura.MessageDialog { onClosed: destroy() }", base); messageDialog.title = catalog.i18nc("@title:window", "Export Material"); @@ -267,7 +269,7 @@ UM.ManagementPage switch (result.status) { case "error": - messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags and !", "Failed to export material to %1: %2").arg(fileUrl).arg(result.message); + messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tags and !", "Failed to export material to %1: %2").arg(selectedFile).arg(result.message); break; case "success": messageDialog.text = catalog.i18nc("@info:status Don't translate the XML tag !", "Successfully exported material to %1").arg(result.path); @@ -275,7 +277,7 @@ UM.ManagementPage } messageDialog.open(); - CuraApplication.setDefaultPath("dialog_material_path", folder); + CuraApplication.setDefaultPath("dialog_material_path", currentFolder); } } } diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index f124736fd0..8292fbe6da 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -106,7 +106,7 @@ Rectangle anchors.right: materialSlot.right visible: false - UM.RecolorImage + UM.ColorImage { id: favoriteIndicator anchors.centerIn: parent diff --git a/resources/qml/Preferences/Materials/MaterialsSyncDialog.qml b/resources/qml/Preferences/Materials/MaterialsSyncDialog.qml index e592be6d5d..3e8fdd7397 100644 --- a/resources/qml/Preferences/Materials/MaterialsSyncDialog.qml +++ b/resources/qml/Preferences/Materials/MaterialsSyncDialog.qml @@ -3,9 +3,10 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs import QtQuick.Layouts 1.15 import QtQuick.Window 2.1 + import Cura 1.1 as Cura import UM 1.5 as UM @@ -297,7 +298,7 @@ Window iconSize: UM.Theme.getSize("machine_selector_icon").width //Printer status badge (always cloud, but whether it's online or offline). - UM.RecolorImage + UM.ColorImage { width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height @@ -325,7 +326,7 @@ Window } } - UM.RecolorImage + UM.ColorImage { id: printerSpinner width: UM.Theme.getSize("section_icon").width @@ -402,7 +403,7 @@ Window UM.Label { Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter text: catalog.i18nc("@text Asking the user whether printers are missing in a list.", "Printers missing?") + "\n" + catalog.i18nc("@text", "Make sure all your printers are turned ON and connected to Digital Factory.") @@ -413,7 +414,7 @@ Window Cura.SecondaryButton { id: refreshListButton - Layout.alignment: Qt.AlignVCenter + Layout.alignment: Qt.AlignmentFlag.AlignVCenter text: catalog.i18nc("@button", "Refresh List") iconSource: UM.Theme.getIcon("ArrowDoubleCircleRight") onClicked: Cura.API.account.sync(true) @@ -490,7 +491,7 @@ Window visible: !syncButton.visible - UM.RecolorImage + UM.ColorImage { id: syncingIcon height: UM.Theme.getSize("action_button_icon").height @@ -700,7 +701,7 @@ Window { if(!materialsSyncDialog.hasExportedUsb) { - exportUsbDialog.folder = syncModel.getPreferredExportAllPath(); + exportUsbDialog.currentFolder = syncModel.getPreferredExportAllPath(); exportUsbDialog.open(); } else @@ -731,13 +732,13 @@ Window property variant exportUsbDialog: FileDialog { title: catalog.i18nc("@title:window", "Export All Materials") - selectExisting: false nameFilters: ["Material archives (*.umm)", "All files (*)"] + fileMode: FileDialog.SaveFile onAccepted: { - syncModel.exportAll(fileUrl); + syncModel.exportAll(selectedFile); CuraApplication.setDefaultPath("dialog_material_path", folder); materialsSyncDialog.hasExportedUsb = true; } } -} \ No newline at end of file +} diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index d781218027..2e6a255014 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -2,7 +2,7 @@ // Uranium is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.4 import QtQuick.Layouts 1.3 import UM 1.5 as UM diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index 2cbadc6bd6..a172949812 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -3,8 +3,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.15 -import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.3 +import QtQuick.Dialogs import UM 1.5 as UM import Cura 1.0 as Cura @@ -206,7 +206,8 @@ Item { height: informationPage.rowHeight width: informationPage.columnWidth - verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") + verticalAlignment: Qt.AlignVCenter + text: catalog.i18nc("@label", "Color") } Row @@ -573,11 +574,11 @@ Item elide: Text.ElideRight verticalAlignment: Qt.AlignVCenter } - Cura.SpinBox + Cura.NumericTextFieldWithUnit { id: spinBox anchors.left: label.right - value: + valueText: { // In case the setting is not in the material... if (!isNaN(parseFloat(materialPropertyProvider.properties.value))) @@ -597,11 +598,11 @@ Item return 0; } width: settingsPage.columnWidth - suffix: " " + model.unit - to: 99999 + maximum: 99999 + unitText: model.unit decimals: model.unit == "mm" ? 2 : 0 - onEditingFinished: materialPropertyProvider.setPropertyValue("value", value) + editingFinishedFunction: materialPropertyProvider.setPropertyValue("value", value) } UM.ContainerPropertyProvider diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 9ea23194e7..2a7c50fe59 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -4,7 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.3 -import QtQuick.Dialogs 1.2 +import QtQuick.Dialogs import UM 1.5 as UM import Cura 1.6 as Cura @@ -349,13 +349,18 @@ UM.ManagementPage { id: exportDialog title: catalog.i18nc("@title:window", "Export Profile") - selectExisting: false + fileMode: FileDialog.SaveFile nameFilters: base.qualityManagementModel.getFileNameFilters("profile_writer") - folder: CuraApplication.getDefaultPath("dialog_profile_path") + currentFolder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { + + // If nameFilters contains only 1 item, the index of selectedNameFilter will always be -1 + // This fetches the nameFilter at index selectedNameFilter.index if it is positive + const nameFilterString = selectedNameFilter.index >= 0 ? nameFilters[selectedNameFilter.index] : nameFilters[0]; + var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group, - fileUrl, selectedNameFilter); + selectedFile, nameFilterString); if (result && result.status == "error") { @@ -365,7 +370,7 @@ UM.ManagementPage } // else pop-up Message thing from python code - CuraApplication.setDefaultPath("dialog_profile_path", folder); + CuraApplication.setDefaultPath("dialog_profile_path", currentFolder); } } @@ -385,7 +390,7 @@ UM.ManagementPage title: catalog.i18nc("@title:window", "Confirm Remove") text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItemName) - standardButtons: StandardButton.Yes | StandardButton.No + standardButtons: Dialog.Yes | Dialog.No modal: true onAccepted: @@ -414,12 +419,12 @@ UM.ManagementPage { id: importDialog title: catalog.i18nc("@title:window", "Import Profile") - selectExisting: true + fileMode: FileDialog.OpenFile nameFilters: base.qualityManagementModel.getFileNameFilters("profile_reader") - folder: CuraApplication.getDefaultPath("dialog_profile_path") + currentFolder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { - var result = Cura.ContainerManager.importProfile(fileUrl); + var result = Cura.ContainerManager.importProfile(selectedFile); messageDialog.title = catalog.i18nc("@title:window", "Import Profile") messageDialog.text = result.message; messageDialog.open(); diff --git a/resources/qml/Preferences/SettingVisibilityItem.qml b/resources/qml/Preferences/SettingVisibilityItem.qml index b7edd54c35..8905c15124 100644 --- a/resources/qml/Preferences/SettingVisibilityItem.qml +++ b/resources/qml/Preferences/SettingVisibilityItem.qml @@ -74,7 +74,7 @@ Item } } - UM.RecolorImage + UM.ColorImage { anchors.centerIn: parent width: Math.round(check.height * 0.75) | 0 diff --git a/resources/qml/PrintSetupHeaderButton.qml b/resources/qml/PrintSetupHeaderButton.qml index a4f95148da..0a6c5c363a 100644 --- a/resources/qml/PrintSetupHeaderButton.qml +++ b/resources/qml/PrintSetupHeaderButton.qml @@ -89,7 +89,7 @@ ToolButton { id: background - UM.RecolorImage + UM.ColorImage { id: downArrow anchors.verticalCenter: parent.verticalCenter @@ -97,7 +97,6 @@ ToolButton anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.height: width color: base.enabled ? UM.Theme.getColor("setting_control_button") : UM.Theme.getColor("setting_category_disabled_text") source: UM.Theme.getIcon("ChevronSingleDown") } diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 2ca4a9f9bf..41bf28be15 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -159,7 +159,7 @@ Item } onExited: base.hideTooltip() } - UM.RecolorImage + UM.ColorImage { id: downArrow diff --git a/resources/qml/PrintSetupSelector/Custom/MenuButton.qml b/resources/qml/PrintSetupSelector/Custom/MenuButton.qml index 112edbbf77..6663ff9991 100644 --- a/resources/qml/PrintSetupSelector/Custom/MenuButton.qml +++ b/resources/qml/PrintSetupSelector/Custom/MenuButton.qml @@ -44,7 +44,7 @@ Button { height: button.height width: button.width - UM.RecolorImage + UM.ColorImage { id: check height: UM.Theme.getSize("default_arrow").height diff --git a/resources/qml/PrintSetupSelector/NoIntentIcon.qml b/resources/qml/PrintSetupSelector/NoIntentIcon.qml index 6fc883fdef..2dc422f6d6 100644 --- a/resources/qml/PrintSetupSelector/NoIntentIcon.qml +++ b/resources/qml/PrintSetupSelector/NoIntentIcon.qml @@ -16,7 +16,7 @@ Item implicitWidth: UM.Theme.getSize("section_icon").width implicitHeight: UM.Theme.getSize("section_icon").height - UM.RecolorImage + UM.ColorImage { source: UM.Theme.getIcon("Information") color: UM.Theme.getColor("icon") diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 414c349bb6..16dabea7b8 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -12,7 +12,6 @@ Cura.ExpandableComponent id: printSetupSelector dragPreferencesNamePrefix: "view/settings" - property bool preSlicedData: PrintInformation !== null && PrintInformation.preSliced contentPadding: UM.Theme.getSize("default_lining").width diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 046efe8b5b..e53aa693e1 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -15,10 +15,8 @@ Item id: content property int absoluteMinimumHeight: 200 * screenScaleFactor - - width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width - height: contents.height + buttonRow.height - + implicitWidth: UM.Theme.getSize("print_setup_widget").width + implicitHeight: contents.height + buttonRow.height enum Mode { Recommended = 0, @@ -227,7 +225,7 @@ Item color: UM.Theme.getColor("lining") } - UM.RecolorImage + UM.ColorImage { width: UM.Theme.getSize("drag_icon").width height: UM.Theme.getSize("drag_icon").height diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 434945e172..625629c810 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -101,7 +101,8 @@ Item { id: backgroundLine height: UM.Theme.getSize("print_setup_slider_groove").height - width: infillSlider.width - UM.Theme.getSize("print_setup_slider_handle").width + width: parent.width - UM.Theme.getSize("print_setup_slider_handle").width + implicitWidth: width anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") @@ -120,8 +121,10 @@ Item anchors.verticalCenter: parent.verticalCenter // Do not use Math.round otherwise the tickmarks won't be aligned - x: ((handleButton.width / 2) - (backgroundLine.implicitWidth / 2) + (index * ((repeater.width - handleButton.width) / (repeater.count-1)))) - radius: Math.round(backgroundLine.implicitWidth / 2) + // (space between steps) * index of step + x: (backgroundLine.width / (repeater.count - 1)) * index + + radius: Math.round(implicitWidth / 2) visible: (index % 10) == 0 // Only show steps of 10% UM.Label @@ -172,7 +175,8 @@ Item // same operation const active_mode = UM.Preferences.getValue("cura/active_mode") - if (active_mode == 0 || active_mode == "simple") + if (visible // Workaround: 'visible' is checked because on startup in Windows it spuriously gets an 'onValueChanged' with value '0' if this isn't checked. + && (active_mode == 0 || active_mode == "simple")) { Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 49be0fae1c..0b683f0ddf 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -154,7 +154,7 @@ Item when: supportExtruderCombobox.model.count > 0 } - indicator: UM.RecolorImage + indicator: UM.ColorImage { id: downArrow x: supportExtruderCombobox.width - width - supportExtruderCombobox.rightPadding @@ -163,8 +163,6 @@ Item source: UM.Theme.getIcon("ChevronSingleDown") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width + 5 * screenScaleFactor - sourceSize.height: width + 5 * screenScaleFactor color: UM.Theme.getColor("setting_control_button") } diff --git a/resources/qml/PrintSetupTooltip.qml b/resources/qml/PrintSetupTooltip.qml index 29fe7d6508..e4d322f9d4 100644 --- a/resources/qml/PrintSetupTooltip.qml +++ b/resources/qml/PrintSetupTooltip.qml @@ -17,10 +17,12 @@ UM.PointingRectangle arrowSize: UM.Theme.getSize("default_arrow").width opacity: 0 + // This should be disabled when invisible, otherwise it will catch mouse events. + enabled: opacity > 0 Behavior on opacity { - NumberAnimation { duration: 100; } + NumberAnimation { duration: 200; } } property alias text: label.text @@ -59,16 +61,19 @@ UM.PointingRectangle base.opacity = 0; } - MouseArea + ScrollView { - enabled: parent.opacity > 0 - visible: enabled - anchors.fill: parent - acceptedButtons: Qt.NoButton - hoverEnabled: true + id: textScroll + width: parent.width + height: Math.min(label.height + UM.Theme.getSize("tooltip_margins").height, base.parent.height) + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + hoverEnabled: parent.opacity > 0 onHoveredChanged: { - if(containsMouse && base.opacity > 0) + if(hovered && base.opacity > 0) { base.show(Qt.point(target.x - 1, target.y - UM.Theme.getSize("tooltip_arrow_margins").height / 2)); //Same arrow position as before. } @@ -78,27 +83,16 @@ UM.PointingRectangle } } - ScrollView + UM.Label { - id: textScroll - width: parent.width - height: Math.min(label.height, base.parent.height) + id: label + x: UM.Theme.getSize("tooltip_margins").width + y: UM.Theme.getSize("tooltip_margins").height + width: textScroll.width - 2 * UM.Theme.getSize("tooltip_margins").width - ScrollBar.horizontal: ScrollBar { - active: false //Only allow vertical scrolling. We should grow vertically only, but due to how the label is positioned it allocates space in the ScrollView horizontally. - } - - UM.Label - { - id: label - x: UM.Theme.getSize("tooltip_margins").width - y: UM.Theme.getSize("tooltip_margins").height - width: base.width - UM.Theme.getSize("tooltip_margins").width * 2 - - wrapMode: Text.Wrap - textFormat: Text.RichText - color: UM.Theme.getColor("tooltip_text") - } + wrapMode: Text.Wrap + textFormat: Text.RichText + color: UM.Theme.getColor("tooltip_text") } } } diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index bea7c3bdd8..e567d5d4cb 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -1,7 +1,7 @@ //Copyright (c) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.15 import QtQuick.Controls 2.1 import UM 1.5 as UM @@ -206,7 +206,7 @@ Item selectByMouse: true maximumLength: 5 enabled: parent.enabled - validator: RegExpValidator { regExp: /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } //Floating point regex. + validator: RegularExpressionValidator { regularExpression: /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } //Floating point regex. anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width anchors.right: unit.left diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 288e9cab3f..8a1d13524f 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -1,7 +1,7 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.10 +import QtQuick 2.15 import QtQuick.Controls 2.4 import UM 1.5 as UM @@ -186,7 +186,7 @@ Item selectByMouse: true maximumLength: 5 enabled: parent.enabled - validator: RegExpValidator { regExp: /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } //Floating point regex. + validator: RegularExpressionValidator { regularExpression: /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } //Floating point regex. anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width anchors.right: unit.left diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 12f495f7a8..77cd2be409 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -108,7 +108,7 @@ Cura.ExpandablePopup iconColor: UM.Theme.getColor("machine_selector_printer_icon") iconSize: source != "" ? UM.Theme.getSize("machine_selector_icon").width: 0 - UM.RecolorImage + UM.ColorImage { id: connectionStatusImage anchors @@ -192,9 +192,8 @@ Cura.ExpandablePopup contentItem: Item { id: popup - width: UM.Theme.getSize("machine_selector_widget_content").width - height: Math.min(machineSelectorList.contentHeight + separator.height + buttonRow.height, UM.Theme.getSize("machine_selector_widget_content").height) //Maximum height is the theme entry. - + implicitWidth: UM.Theme.getSize("machine_selector_widget_content").width + implicitHeight: Math.min(machineSelectorList.contentHeight + separator.height + buttonRow.height, UM.Theme.getSize("machine_selector_widget_content").height) //Maximum height is the theme entry. MachineSelectorList { id: machineSelectorList diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index 74c833f691..cd1f3cc574 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -40,7 +40,7 @@ Button contentItem: Item { - width: machineSelectorButton.width - machineSelectorButton.leftPadding + width: machineSelectorButton.width - machineSelectorButton.leftPadding - machineSelectorButton.rightPadding height: UM.Theme.getSize("action_button").height UM.Label diff --git a/resources/qml/ProfileOverview.qml b/resources/qml/ProfileOverview.qml index e1009cd010..3508a85193 100644 --- a/resources/qml/ProfileOverview.qml +++ b/resources/qml/ProfileOverview.qml @@ -1,11 +1,10 @@ //Copyright (c) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import Qt.labs.qmlmodels 1.0 import QtQuick 2.7 import QtQuick.Controls 2.15 -import UM 1.5 as UM +import UM 1.6 as UM import Cura 1.6 as Cura Cura.TableView @@ -39,13 +38,11 @@ Cura.TableView catalog.i18nc("@title:column", "Current"), catalog.i18nc("@title:column Unit of measurement", "Unit") ] - model: TableModel + + model: UM.TableModel { id: tableModel - TableModelColumn { display: "label" } - TableModelColumn { display: "profile_value" } - TableModelColumn { display: "user_value" } - TableModelColumn { display: "unit" } + headers: ["label", "profile_value", "user_value", "unit"] rows: modelRows } sectionRole: "category" diff --git a/resources/qml/SearchBar.qml b/resources/qml/SearchBar.qml index 4d9c003653..af5ad7cc35 100644 --- a/resources/qml/SearchBar.qml +++ b/resources/qml/SearchBar.qml @@ -17,7 +17,7 @@ Cura.TextField placeholderText: catalog.i18nc("@placeholder", "Search") font.italic: true - UM.RecolorImage + UM.ColorImage { id: searchIcon diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index 4987eedb83..215d2be1be 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -143,13 +143,12 @@ SettingItem return UM.Theme.getColor("checkbox") } - UM.RecolorImage + UM.ColorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter height: UM.Theme.getSize("checkbox_mark").height width: UM.Theme.getSize("checkbox_mark").width - sourceSize.height: width color: !enabled ? UM.Theme.getColor("checkbox_mark_disabled") : UM.Theme.getColor("checkbox_mark"); source: UM.Theme.getIcon("Check", "low") opacity: control.checked ? 1 : 0 diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index c3bc472fbe..697acdec0b 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -98,7 +98,7 @@ SettingItem when: control.model.items.length > 0 } - indicator: UM.RecolorImage + indicator: UM.ColorImage { id: downArrow x: control.width - width - control.rightPadding @@ -107,8 +107,6 @@ SettingItem source: UM.Theme.getIcon("ChevronSingleDown") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width + 5 * screenScaleFactor - sourceSize.height: width + 5 * screenScaleFactor color: UM.Theme.getColor("setting_control_button"); } @@ -156,7 +154,7 @@ SettingItem background: Rectangle { id: swatch - height: Math.round(parent.height / 2) + height: UM.Theme.getSize("standard_arrow").width width: height radius: Math.round(width / 2) anchors.right: parent.right diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 6787855775..e975f1a2b9 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -62,13 +62,19 @@ Item var affected_by_list = "" for (var i in affected_by) { - affected_by_list += "
  • %1
  • \n".arg(affected_by[i].label) + if(affected_by[i].label != "") + { + affected_by_list += "
  • %1
  • \n".arg(affected_by[i].label) + } } var affects_list = "" for (var i in affects) { - affects_list += "
  • %1
  • \n".arg(affects[i].label) + if(affects[i].label != "") + { + affects_list += "
  • %1
  • \n".arg(affects[i].label) + } } var tooltip = "%1\n

    %2

    ".arg(definition.label).arg(definition.description) diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index 2a4db2ab31..e02fe88e34 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -99,7 +99,7 @@ SettingItem value: control.currentText != "" ? control.model.getItem(control.currentIndex).color : "transparent" } - indicator: UM.RecolorImage + indicator: UM.ColorImage { id: downArrow x: control.width - width - control.rightPadding @@ -108,8 +108,6 @@ SettingItem source: UM.Theme.getIcon("ChevronSingleDown") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width + 5 * screenScaleFactor - sourceSize.height: width + 5 * screenScaleFactor color: UM.Theme.getColor("setting_control_button") } @@ -159,7 +157,7 @@ SettingItem background: Rectangle { id: swatch - height: Math.round(parent.height / 2) + height: UM.Theme.getSize("standard_arrow").width width: height radius: Math.round(width / 2) anchors.right: parent.right diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index 95976ad58e..5b051178b1 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -1,8 +1,8 @@ // Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 -import QtQuick.Controls 2.0 +import QtQuick 2.15 +import QtQuick.Controls 2.15 import UM 1.5 as UM @@ -154,7 +154,7 @@ SettingItem // should be done as little as possible) clip: definition.type == "str" || definition.type == "[int]" - validator: RegExpValidator { regExp: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : (definition.type == "float") ? /^-?[0-9]{0,9}[.,]?[0-9]{0,3}$/ : /^.*$/ } // definition.type property from parent loader used to disallow fractional number entry + validator: RegularExpressionValidator { regularExpression: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : (definition.type == "float") ? /^-?[0-9]{0,9}[.,]?[0-9]{0,3}$/ : /^.*$/ } // definition.type property from parent loader used to disallow fractional number entry Binding { @@ -162,6 +162,13 @@ SettingItem property: "text" value: { + if (input.activeFocus) + { + // In QT6 using "when: !activeFocus" causes the value to be null when activeFocus becomes True + // Since we want the value to stay the same when giving focus to the TextInput this is being used + // in place of "when: !activeFocus" + return input.text + } // Stacklevels // 0: user -> unsaved change // 1: quality changes -> saved change @@ -181,7 +188,6 @@ SettingItem return propertyProvider.properties.value } } - when: !input.activeFocus } MouseArea diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 40e9e19b01..3ac6acbc33 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -43,6 +43,7 @@ Item height: parent.height anchors.left: parent.left anchors.right: parent.right + topPadding: height / 4 leftPadding: searchIcon.width + UM.Theme.getSize("default_margin").width * 2 placeholderText: catalog.i18nc("@label:textbox", "Search settings") font.italic: true @@ -50,7 +51,7 @@ Item property var expandedCategories property bool lastFindingSettings: false - UM.RecolorImage + UM.ColorImage { id: searchIcon @@ -60,7 +61,7 @@ Item left: parent.left leftMargin: UM.Theme.getSize("default_margin").width } - source: UM.Theme.getIcon("search") + source: UM.Theme.getIcon("Magnifier") height: UM.Theme.getSize("small_button_icon").height width: height color: UM.Theme.getColor("text") @@ -171,13 +172,13 @@ Item { anchors.fill: contents acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true + onWheel: (wheel) => { wheel.accepted = true } } ListView { id: contents - maximumFlickVelocity: 1000 + maximumFlickVelocity: 1000 * screenScaleFactor anchors { top: filterContainer.bottom @@ -194,7 +195,7 @@ Item onPositionChanged: { // This removes focus from items when scrolling. // This fixes comboboxes staying open and scrolling container - if (!activeFocus) { + if (!activeFocus && !filter.activeFocus) { forceActiveFocus(); } } @@ -226,9 +227,7 @@ Item id: delegate width: contents.width - (scrollBar.width + UM.Theme.getSize("narrow_margin").width) - Behavior on height { NumberAnimation { duration: 100 } } opacity: enabled ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } enabled: provider.properties.enabled === "True" property var definition: model @@ -350,10 +349,7 @@ Item function onFocusReceived() { contents.indexWithFocus = index - animateContentY.from = contents.contentY contents.positionViewAtIndex(index, ListView.Contain) - animateContentY.to = contents.contentY - animateContentY.running = true } function onSetActiveFocusToNextSetting(forward) { @@ -385,35 +381,6 @@ Item } } - NumberAnimation { - id: animateContentY - target: contents - property: "contentY" - duration: 50 - } - - add: Transition { - SequentialAnimation { - NumberAnimation { properties: "height"; from: 0; duration: 100 } - NumberAnimation { properties: "opacity"; from: 0; duration: 100 } - } - } - remove: Transition { - SequentialAnimation { - NumberAnimation { properties: "opacity"; to: 0; duration: 100 } - NumberAnimation { properties: "height"; to: 0; duration: 100 } - } - } - addDisplaced: Transition { - NumberAnimation { properties: "x,y"; duration: 100 } - } - removeDisplaced: Transition { - SequentialAnimation { - PauseAnimation { duration: 100; } - NumberAnimation { properties: "x,y"; duration: 100 } - } - } - Cura.Menu { id: contextMenu diff --git a/resources/qml/SpinBox.qml b/resources/qml/SpinBox.qml index 5f809a434a..038125111c 100644 --- a/resources/qml/SpinBox.qml +++ b/resources/qml/SpinBox.qml @@ -1,7 +1,7 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.15 import QtQuick.Controls 2.15 import UM 1.5 as UM @@ -28,14 +28,13 @@ Item property bool editable: true - property var validator: RegExpValidator + property var validator: RegularExpressionValidator { - regExp: new RegExp("^" + prefix + "([0-9]+[.|,]?[0-9]*)?" + suffix + "$") + regularExpression: new RegExp("^" + prefix + "([0-9]+[.|,]?[0-9]*)?" + suffix + "$") } signal editingFinished() - implicitWidth: spinBox.implicitWidth - implicitHeight: spinBox.implicitHeight + height: UM.Theme.getSize("setting_control").height SpinBox { @@ -44,8 +43,8 @@ Item editable: base.editable topPadding: 0 bottomPadding: 0 - padding: UM.Theme.getSize("narrow_margin").width - + leftPadding: down.indicator.width + UM.Theme.getSize("narrow_margin").width + rightPadding: up.indicator.width + UM.Theme.getSize("narrow_margin").width // The stepSize of the SpinBox is intentionally set to be always `1` // As SpinBoxes can only contain integer values the `base.stepSize` is concidered the precision/resolution // increasing the spinBox.value by one increases the actual/real value of the component by `base.stepSize` @@ -110,7 +109,7 @@ Item color: spinBox.down.pressed ? spinBox.palette.mid : UM.Theme.getColor("detail_background") } - UM.RecolorImage + UM.ColorImage { anchors.centerIn: parent height: parent.height / 2.5 @@ -130,7 +129,7 @@ Item color: spinBox.up.pressed ? spinBox.palette.mid : UM.Theme.getColor("detail_background") } - UM.RecolorImage + UM.ColorImage { anchors.centerIn: parent height: parent.height / 2.5 diff --git a/resources/qml/TableView.qml b/resources/qml/TableView.qml index 5e0b863f5a..444d310511 100644 --- a/resources/qml/TableView.qml +++ b/resources/qml/TableView.qml @@ -1,7 +1,6 @@ //Copyright (C) 2022 Ultimaker B.V. //Cura is released under the terms of the LGPLv3 or higher. -import Qt.labs.qmlmodels 1.0 import QtQuick 2.15 import QtQuick.Controls 2.15 @@ -56,8 +55,9 @@ Item font: UM.Theme.getFont("medium_bold") elide: Text.ElideRight } - Item //Resize handle. + Item { + //Resize handle. anchors { right: parent.right diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 32de5b76ca..9c1abd31b7 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -63,13 +63,10 @@ Item isTopElement: toolsModel.getItem(0).id == model.id isBottomElement: toolsModel.getItem(toolsModel.count - 1).id == model.id - toolItem: UM.RecolorImage + toolItem: UM.ColorImage { source: UM.Theme.getIcon(model.icon) != "" ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon color: UM.Theme.getColor("icon") - - sourceSize.height: Math.round(UM.Theme.getSize("button").height / 2) - sourceSize.width: Math.round(UM.Theme.getSize("button").width / 2) } onCheckedChanged: @@ -160,7 +157,7 @@ Item anchors.topMargin: base.activeY z: buttons.z - 1 - target: Qt.point(parent.right, base.activeY + Math.round(UM.Theme.getSize("button").height/2)) + target: Qt.point(-1, base.activeY + Math.round(UM.Theme.getSize("button").height / 2)) arrowSize: UM.Theme.getSize("default_arrow").width width: diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 452cc5a09a..e76e5dbb67 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -69,17 +69,10 @@ Cura.ExpandablePopup } } + contentWidth: viewSelector.width - 2 * viewSelector.contentPadding contentItem: Column { id: viewSelectorPopup - width: viewSelector.width - 2 * viewSelector.contentPadding - - // For some reason the height/width of the column gets set to 0 if this is not set... - Component.onCompleted: - { - height = implicitHeight - width = viewSelector.width - 2 * viewSelector.contentPadding - } Repeater { diff --git a/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml index d2d48267de..127fca5fa6 100644 --- a/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml +++ b/resources/qml/WelcomePages/AddLocalPrinterScrollView.qml @@ -1,8 +1,8 @@ // 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 2.15 +import QtQuick.Controls 2.14 import UM 1.5 as UM import Cura 1.1 as Cura @@ -115,14 +115,12 @@ Item width: childrenRect.width height: UM.Theme.getSize("action_button").height - UM.RecolorImage + UM.ColorImage { id: arrow anchors.left: parent.left width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: UM.Theme.getColor("text") source: base.currentSection == section ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleRight") } @@ -235,9 +233,9 @@ Item placeholderText: catalog.i18nc("@text", "Please name your printer") maximumLength: 40 width: parent.width - (printerNameLabel.width + (3 * UM.Theme.getSize("default_margin").width)) - validator: RegExpValidator + validator: RegularExpressionValidator { - regExp: printerNameTextField.machineNameValidator.machineNameRegex + regularExpression: printerNameTextField.machineNameValidator.machineNameRegex } property var machineNameValidator: Cura.MachineNameValidator { } } diff --git a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml index 64f194dd56..badd87326b 100644 --- a/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml +++ b/resources/qml/WelcomePages/AddNetworkPrinterScrollView.qml @@ -200,7 +200,7 @@ Item height: troubleshootingLinkIcon.height width: troubleshootingLinkIcon.width + troubleshootingLabel.width + UM.Theme.getSize("thin_margin").width - UM.RecolorImage + UM.ColorImage { id: troubleshootingLinkIcon anchors.right: troubleshootingLabel.left @@ -208,7 +208,6 @@ Item anchors.verticalCenter: parent.verticalCenter height: troubleshootingLabel.height width: height - sourceSize.height: width color: UM.Theme.getColor("text_link") source: UM.Theme.getIcon("LinkExternal") } diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 2207bd1708..1adaa09a49 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -1,7 +1,7 @@ // Copyright (c) 2019 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.10 +import QtQuick 2.15 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 @@ -123,9 +123,9 @@ Item onInvalidInputDetected: invalidInputLabel.visible = true - validator: RegExpValidator + validator: RegularExpressionValidator { - regExp: /([a-fA-F0-9.:]+)?/ + regularExpression: /([a-fA-F0-9.:]+)?/ } onTextEdited: invalidInputLabel.visible = false diff --git a/resources/qml/WelcomePages/DropDownHeader.qml b/resources/qml/WelcomePages/DropDownHeader.qml index a4b416b093..b91f3383be 100644 --- a/resources/qml/WelcomePages/DropDownHeader.qml +++ b/resources/qml/WelcomePages/DropDownHeader.qml @@ -57,7 +57,7 @@ Cura.RoundedRectangle color: base.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") } - UM.RecolorImage + UM.ColorImage { id: rightIcon anchors.right: parent.right diff --git a/resources/qml/WelcomePages/WelcomeDialogItem.qml b/resources/qml/WelcomePages/WelcomeDialogItem.qml index 2d01642ada..d98610d7d8 100644 --- a/resources/qml/WelcomePages/WelcomeDialogItem.qml +++ b/resources/qml/WelcomePages/WelcomeDialogItem.qml @@ -4,7 +4,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 -import QtGraphicalEffects 1.0 // For the DropShadow import UM 1.3 as UM import Cura 1.1 as Cura @@ -45,7 +44,8 @@ Item } // Drop shadow around the panel - DropShadow + // TODO: Maybe re-implement this some other way. + /*DropShadow { id: shadow radius: UM.Theme.getSize("first_run_shadow_radius").width @@ -55,7 +55,7 @@ Item verticalOffset: shadowOffset color: UM.Theme.getColor("first_run_shadow") transparentBorder: true - } + }*/ // Close this dialog when there's no more page to show Connections diff --git a/resources/qml/WelcomePages/WhatsNewContent.qml b/resources/qml/WelcomePages/WhatsNewContent.qml index c56f8880ed..2ac3935ece 100644 --- a/resources/qml/WelcomePages/WhatsNewContent.qml +++ b/resources/qml/WelcomePages/WhatsNewContent.qml @@ -4,7 +4,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 -import QtGraphicalEffects 1.12 // For the DropShadow import UM 1.3 as UM import Cura 1.1 as Cura @@ -67,7 +66,7 @@ Item Rectangle { - Layout.alignment: Qt.AlignHCenter + Layout.alignment: Qt.AlignmentFlag.AlignHCenter color: UM.Theme.getColor("viewport_overlay") width: whatsNewViewport.width height: whatsNewViewport.height @@ -92,13 +91,6 @@ Item source: manager.getSubpageImageSource(index) } - DropShadow { - anchors.fill: subpageImage - radius: UM.Theme.getSize("monitor_shadow_radius").width - color: UM.Theme.getColor("first_run_shadow") - source: subpageImage - } - Cura.ScrollableTextArea { id: subpageText diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml index 698eb4ec10..2a3716c3af 100644 --- a/resources/qml/Widgets/ComboBox.qml +++ b/resources/qml/Widgets/ComboBox.qml @@ -39,7 +39,7 @@ ComboBox background: UM.UnderlineBackground{} - indicator: UM.RecolorImage + indicator: UM.ColorImage { id: downArrow x: control.width - width - control.rightPadding @@ -48,8 +48,6 @@ ComboBox source: UM.Theme.getIcon("ChevronSingleDown") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width + 5 * screenScaleFactor - sourceSize.height: width + 5 * screenScaleFactor color: UM.Theme.getColor("setting_control_button") } diff --git a/resources/qml/Widgets/MenuItem.qml b/resources/qml/Widgets/MenuItem.qml index 36098e1197..c2929e512e 100644 --- a/resources/qml/Widgets/MenuItem.qml +++ b/resources/qml/Widgets/MenuItem.qml @@ -15,11 +15,12 @@ UM.MenuItem implicitHeight: UM.Theme.getSize("menu").height + UM.Theme.getSize("narrow_margin").height implicitWidth: UM.Theme.getSize("menu").width - opacity: enabled ? 1.0 : 0.5 - arrow: UM.RecolorImage + property bool overrideShowArrow: false + + arrow: UM.ColorImage { - visible: menuItem.subMenu + visible: menuItem.subMenu || overrideShowArrow height: UM.Theme.getSize("default_arrow").height width: height anchors.verticalCenter: parent.verticalCenter @@ -29,7 +30,7 @@ UM.MenuItem color: UM.Theme.getColor("setting_control_text") } - indicator: UM.RecolorImage + indicator: UM.ColorImage { id: check visible: menuItem.checkable && menuItem.checked diff --git a/resources/qml/Widgets/TextField.qml b/resources/qml/Widgets/TextField.qml index b708446d65..d035883154 100644 --- a/resources/qml/Widgets/TextField.qml +++ b/resources/qml/Widgets/TextField.qml @@ -23,6 +23,8 @@ TextField selectByMouse: true font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_field_text") + selectedTextColor: UM.Theme.getColor("text_field_text") + placeholderTextColor: UM.Theme.getColor("text_field_text_disabled") renderType: Text.NativeRendering selectionColor: UM.Theme.getColor("text_selection") leftPadding: iconLeft.visible ? iconLeft.width + UM.Theme.getSize("default_margin").width * 2 : UM.Theme.getSize("thin_margin").width @@ -53,7 +55,7 @@ TextField { id: backgroundRectangle //Optional icon added on the left hand side. - UM.RecolorImage + UM.ColorImage { id: iconLeft diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index 91d54ef17a..98cc638295 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -1,3 +1,70 @@ +[5.0] +Beta +Watch the launch event to learn more about Ultimaker Cura 5.0 beta. + +* New slicing engine +Following special beta releases to test the Arachne engine, we are pleased to announce our new slicing engine is here in Ultimaker Cura! This all-new engine uses variable line widths when preparing files for printing, meaning you can now print thin and intricate parts more accurately and with greater strength. + +* Renewed the Ultimaker Cura Marketplace +We have streamlined the workflow for accessing the Ultimaker Marketplace inside of Ultimaker Cura. The UI has been improved and it’s now easier and faster to find and install plugins and material profiles. + +* Improved print profiles for Ultimaker printers +The new slicing engine in Ultimaker Cura 5.0 beta has helped us to improve our print profiles. This means that users of Ultimaker printers can achieve speed increases of up to 20%. + +* Upgrade from Qt5 to Qt6 +We now support Apple M1 chips + +* Other new features and improvements: +- New Cura icon +- New Cura splash screen +- Updated the digital build plates for Ultimaker printers +- Introduce Minimum Wall Line Width, contributed by BagelOrb +- Settings for metal printing implemented +- Shrinkage compensation is now available for PLA, tPLA and PETG +- Improved default Line Widths for Spiralize +- Decrease resolution to remove some buffer underruns +- Removed the setting Center Last from Wall Ordering +- Incomplete languages are now shown in the language drop-down menu + +* Bug fixes: +- Added the Scale Fan Speed From 0 to 1 setting for printers that interpreted fan speed as percentages +- Fixed a bug with extra travel moves increased the printing time, contributed by BagelOrb +- Fixed a bug where Monotonic Ironing breaks Ironing, contributed by BagelOrb +- Changed the priority of CuraEngine +- Fixed a bug where increasing Filter distances creates extremely wide lines, contributed by BagelOrb +- Fixed double scroll bar, contributed by fieldOfView +- Fixed a bug where maximum resolution/deviation was not applied to surface mode, contributed by BagelOrb +- Fixed a bug where the seam placement was uneven +- Fixed a bug where Top Surface Skin Layers didn't work +- Fixed a bug where Speed in the flow setting were not respected +- Fixed a bug with unnecessary retracted travel moves +- Fixed a bug where the Ironing Inset didn't work +- Fixed a bug where Support Layers were missing +- Improved the visibility of the checkboxes +- Fixed a crash if Randomize Infill Start was used +- Fixed a bug where Combing was in the wrong part with dual extrusion +- Fixed a crash with Bridging and Top Surface Skin Layers +- Fixed a bug where modifier meshes didn't work in one-at-a-time mode +- Fixed a bug where Tree Support Branches where not being generated +- Fixed a bug where less support was generated +- Changed the possibility for 100% Infill Bottom Layer for Spiralize, contributed by BagelOrb +- Fixed disallowed areas for Brim gap, contributed by BagelOrb + +* Printer definitions, profiles and materials: +- Added Atom 3 and Atom 3 Lite printer definitions, contributed by Daniel-Kurth +- Added Layer One Black PLA, Dark Grey PLA and White PLA, contributed by Daniel-Kurth +- Added FLSUN Q5 printer definition, contributed by kreuzhofer +- Added Creatlity CR100 printer definition, contributed by bodyboarder2528 +- Added Mixware Hyper-S printer definition, contributed by mixware011 +- Added Creality Sermoon D1 printer definition, contributed by felixstif +- Added Volumic SH65, Stream30Pro MK3 and Stream30Ultra SC2 printer definitions, contributed by VOLUMIC +- Updated Eryone Thinker and ER20 profiles, contributed by Eryone +- Updated Atom 2 profile, contributed by lin-ycv +- Added Hellbot Hidra and Magna series printer definitions, contributed by DevelopmentHellbot +- Updated Snapmaker 2 End-Gcode, contributed by Rolzad73 +- Updated the Tinyboy Fabricator printer definitions, contributed by reibuehl +- Updated the Creality Ender 5 printer profile, contributed by Rakhmanov + [4.13.1] * Bug fixes - Fixed a bug where tree support could go through the model diff --git a/resources/themes/cura-light/icons/default/BlockGrid.svg b/resources/themes/cura-light/icons/default/BlockGrid.svg index 207171b8f7..3fb368e7c8 100644 --- a/resources/themes/cura-light/icons/default/BlockGrid.svg +++ b/resources/themes/cura-light/icons/default/BlockGrid.svg @@ -1,3 +1,3 @@ - - + + diff --git a/resources/themes/cura-light/icons/default/CircleMask.svg b/resources/themes/cura-light/icons/default/CircleMask.svg new file mode 100644 index 0000000000..d3a2e9a81c --- /dev/null +++ b/resources/themes/cura-light/icons/default/CircleMask.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/themes/cura-light/icons/default/ExtruderColor.svg b/resources/themes/cura-light/icons/default/ExtruderColor.svg index ff202e7296..8c6063ce13 100644 --- a/resources/themes/cura-light/icons/default/ExtruderColor.svg +++ b/resources/themes/cura-light/icons/default/ExtruderColor.svg @@ -2,9 +2,6 @@ - diff --git a/resources/themes/cura-light/icons/default/Settings.svg b/resources/themes/cura-light/icons/default/Settings.svg index feb0ab0cc8..204f2a5b6f 100644 --- a/resources/themes/cura-light/icons/default/Settings.svg +++ b/resources/themes/cura-light/icons/default/Settings.svg @@ -1,3 +1,3 @@ - + diff --git a/resources/themes/cura-light/icons/low/Check.svg b/resources/themes/cura-light/icons/low/Check.svg index 9630b70052..022df8c696 100644 --- a/resources/themes/cura-light/icons/low/Check.svg +++ b/resources/themes/cura-light/icons/low/Check.svg @@ -1,3 +1,3 @@ - - + + diff --git a/resources/themes/cura-light/icons/low/CheckBoxFill.svg b/resources/themes/cura-light/icons/low/CheckBoxFill.svg index bfbb6d87f2..acaf3ed5aa 100644 --- a/resources/themes/cura-light/icons/low/CheckBoxFill.svg +++ b/resources/themes/cura-light/icons/low/CheckBoxFill.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/resources/themes/cura-light/icons/medium/ExtruderColor.svg b/resources/themes/cura-light/icons/medium/ExtruderColor.svg index cd4452b246..b438cf805b 100644 --- a/resources/themes/cura-light/icons/medium/ExtruderColor.svg +++ b/resources/themes/cura-light/icons/medium/ExtruderColor.svg @@ -2,9 +2,7 @@ - + diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 94372d415e..86d9766d1b 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -6,166 +6,166 @@ "fonts": { "large": { "size": 1.35, - "weight": 40, + "weight": 400, "family": "Noto Sans" }, "large_ja_JP": { "size": 1.35, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "large_zh_CN": { "size": 1.35, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "large_zh_TW": { "size": 1.35, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "large_bold": { "size": 1.35, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "huge": { "size": 1.8, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "huge_bold": { "size": 1.8, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "medium": { "size": 1.16, - "weight": 40, + "weight": 400, "family": "Noto Sans" }, "medium_ja_JP": { "size": 1.16, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "medium_zh_CN": { "size": 1.16, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "medium_zh_TW": { "size": 1.16, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "medium_bold": { "size": 1.16, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "default": { "size": 0.95, - "weight": 40, + "weight": 400, "family": "Noto Sans" }, "default_ja_JP": { "size": 1.0, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "default_zh_CN": { "size": 1.0, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "default_zh_TW": { "size": 1.0, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "default_bold": { "size": 0.95, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "default_bold_ja_JP": { "size": 1.0, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "default_bold_zh_CN": { "size": 1.0, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "default_bold_zh_TW": { "size": 1.0, - "weight": 63, + "weight": 600, "family": "Noto Sans" }, "default_italic": { "size": 0.95, - "weight": 40, + "weight": 400, "italic": true, "family": "Noto Sans" }, "default_italic_ja_JP": { "size": 1.0, - "weight": 50, + "weight": 400, "italic": true, "family": "Noto Sans" }, "default_italic_zh_CN": { "size": 1.0, - "weight": 50, + "weight": 400, "italic": true, "family": "Noto Sans" }, "default_italic_zh_TW": { "size": 1.0, - "weight": 50, + "weight": 400, "italic": true, "family": "Noto Sans" }, "small": { "size": 0.9, - "weight": 40, + "weight": 400, "family": "Noto Sans" }, "small_ja_JP": { "size": 0.9, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "small_zh_CN": { "size": 0.9, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "small_zh_TW": { "size": 0.9, - "weight": 50, + "weight": 400, "family": "Noto Sans" }, "small_emphasis": { "size": 0.9, - "weight": 100, + "weight": 700, "family": "Noto Sans" }, "small_emphasis_ja_JP": { "size": 0.9, - "weight": 100, + "weight": 700, "family": "Noto Sans" }, "small_emphasis_zh_CN": { "size": 0.9, - "weight": 100, + "weight": 700, "family": "Noto Sans" }, "small_emphasis_zh_TW": { "size": 0.9, - "weight": 100, + "weight": 700, "family": "Noto Sans" } }, diff --git a/tests/PrinterOutput/TestNetworkedPrinterOutputDevice.py b/tests/PrinterOutput/TestNetworkedPrinterOutputDevice.py index 2a5cc8a2d5..984051d589 100644 --- a/tests/PrinterOutput/TestNetworkedPrinterOutputDevice.py +++ b/tests/PrinterOutput/TestNetworkedPrinterOutputDevice.py @@ -4,8 +4,8 @@ import time from unittest.mock import MagicMock, patch -from PyQt5.QtNetwork import QNetworkAccessManager -from PyQt5.QtCore import QUrl +from PyQt6.QtNetwork import QNetworkAccessManager +from PyQt6.QtCore import QUrl from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputDevice import ConnectionState @@ -43,7 +43,7 @@ def test_post(): # Create a fake reply (we can't use a QReply, since those are abstract C++) reply = MagicMock() - reply.operation = MagicMock(return_value=QNetworkAccessManager.PostOperation) + reply.operation = MagicMock(return_value=QNetworkAccessManager.Operation.PostOperation) reply.url = MagicMock(return_value=QUrl("127.0.0.1")) mocked_network_manager.post = MagicMock(return_value = reply) @@ -65,7 +65,7 @@ def test_get(): # Create a fake reply (we can't use a QReply, since those are abstract C++) reply = MagicMock() - reply.operation = MagicMock(return_value=QNetworkAccessManager.PostOperation) + reply.operation = MagicMock(return_value=QNetworkAccessManager.Operation.PostOperation) reply.url = MagicMock(return_value=QUrl("127.0.0.1")) mocked_network_manager.get = MagicMock(return_value=reply) @@ -87,7 +87,7 @@ def test_delete(): # Create a fake reply (we can't use a QReply, since those are abstract C++) reply = MagicMock() - reply.operation = MagicMock(return_value=QNetworkAccessManager.PostOperation) + reply.operation = MagicMock(return_value=QNetworkAccessManager.Operation.PostOperation) reply.url = MagicMock(return_value=QUrl("127.0.0.1")) mocked_network_manager.deleteResource = MagicMock(return_value=reply) @@ -109,7 +109,7 @@ def test_put(): # Create a fake reply (we can't use a QReply, since those are abstract C++) reply = MagicMock() - reply.operation = MagicMock(return_value=QNetworkAccessManager.PostOperation) + reply.operation = MagicMock(return_value=QNetworkAccessManager.Operation.PostOperation) reply.url = MagicMock(return_value=QUrl("127.0.0.1")) mocked_network_manager.put = MagicMock(return_value = reply) diff --git a/tests/Settings/TestContainerManager.py b/tests/Settings/TestContainerManager.py index 19ade68f68..36409ba414 100644 --- a/tests/Settings/TestContainerManager.py +++ b/tests/Settings/TestContainerManager.py @@ -1,7 +1,7 @@ from unittest import TestCase from unittest.mock import MagicMock -from PyQt5.QtCore import QUrl +from PyQt6.QtCore import QUrl from unittest.mock import patch from UM.MimeTypeDatabase import MimeTypeDatabase from cura.Settings.ContainerManager import ContainerManager diff --git a/tests/TestOAuth2.py b/tests/TestOAuth2.py index 7d0a4bc5c4..09fa555af4 100644 --- a/tests/TestOAuth2.py +++ b/tests/TestOAuth2.py @@ -4,8 +4,8 @@ from datetime import datetime from unittest.mock import MagicMock, Mock, patch -from PyQt5.QtGui import QDesktopServices -from PyQt5.QtNetwork import QNetworkReply +from PyQt6.QtGui import QDesktopServices +from PyQt6.QtNetwork import QNetworkReply from UM.Preferences import Preferences from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers, TOKEN_TIMESTAMP_FORMAT