diff options
24 files changed, 535 insertions, 184 deletions
diff --git a/.github/workflows/conan-package-create.yml b/.github/workflows/conan-package-create.yml index 3e5f3dbe1c..4af608b7ac 100644 --- a/.github/workflows/conan-package-create.yml +++ b/.github/workflows/conan-package-create.yml @@ -109,7 +109,7 @@ jobs: sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - name: Install GCC-12 on ubuntu-22.04 - if: ${{ matrix.os == 'ubuntu-22.04' }} + if: ${{ startsWith(inputs.runs_on, 'ubuntu-22.04') }} run: | sudo apt install g++-12 gcc-12 -y sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 @@ -1,64 +1,96 @@ -# Cura - -<p align="center"> - <a href="https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml" alt="Unit Tests"> - <img src="https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml/badge.svg" /></a> - <a href="https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml" alt="Unit Tests"> - <img src="https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml/badge.svg" /></a> - <a href="https://github.com/Ultimaker/Cura/issues" alt="Open Issues"> - <img src="https://img.shields.io/github/issues/ultimaker/cura" /></a> - <a href="https://github.com/Ultimaker/Cura/issues?q=is%3Aissue+is%3Aclosed" alt="Closed Issues"> - <img src="https://img.shields.io/github/issues-closed/ultimaker/cura?color=g" /></a> - <a href="https://github.com/Ultimaker/Cura/pulls" alt="Pull Requests"> - <img src="https://img.shields.io/github/issues-pr/ultimaker/cura" /></a> - <a href="https://github.com/Ultimaker/Cura/graphs/contributors" alt="Contributors"> - <img src="https://img.shields.io/github/contributors/ultimaker/cura" /></a> - <a href="https://github.com/Ultimaker/Cura" alt="Repo Size"> - <img src="https://img.shields.io/github/repo-size/ultimaker/cura?style=flat" /></a> - <a href="https://github.com/Ultimaker/Cura/blob/master/LICENSE" alt="License"> - <img src="https://img.shields.io/github/license/ultimaker/cura?style=flat" /></a> -</p> - -Ultimaker Cura is a state-of-the-art slicer application to prepare your 3D models for printing with a 3D printer. With hundreds of settings -and hundreds of community-managed print profiles, Ultimaker Cura is sure to lead your next project to a success. - -![Screenshot](cura-logo.PNG) - -## Logging Issues - -For crashes and similar issues, please attach the following information: - -* (On Windows) The log as produced by dxdiag (start -> run -> dxdiag -> save output) -* The Cura GUI log file, located at - * `%APPDATA%\cura\<Cura version>\cura.log` (Windows), or usually `C:\Users\<your username>\AppData\Roaming\cura\<Cura version>\cura.log` - * `$HOME/Library/Application Support/cura/<Cura version>/cura.log` (OSX) - * `$HOME/.local/share/cura/<Cura version>/cura.log` (Ubuntu/Linux) - -If the Cura user interface still starts, you can also reach this directory from the application menu in Help -> Show settings folder. -An alternative is to install the [ExtensiveSupportLogging plugin](https://marketplace.ultimaker.com/app/cura/plugins/UltimakerPackages/ExtensiveSupportLogging) -this creates a zip folder of the relevant log files. If you're experiencing performance issues, we might ask you to connect the CPU profiler -in this plugin and attach the collected data to your support ticket. - -## Running from Source -Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source) for details about running Cura from source. - -## Plugins -Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Plugin-Directory) for details about creating and using plugins. - -## Supported printers -Please check our [Wiki page](https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura) for guidelines about adding support -for new machines. - -## Configuring Cura -Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Cura-Settings) about configuration options for developers. - -## Translating Cura -Please check out [Wiki page](https://github.com/Ultimaker/Cura/wiki/Translating-Cura) about how to translate Cura into other languages. - -## License -![License](https://img.shields.io/github/license/ultimaker/cura?style=flat) -Cura is released under terms of the LGPLv3 or higher. A copy of this license should be included with the software. Terms of the license can be found in the LICENSE file. Or at -http://www.gnu.org/licenses/lgpl.html - -> But in general it boils down to: -> **You need to share the source of any Cura modifications** + +<br> + +<div align = center> + +[![Badge Issues]][Issues] +[![Badge PullRequests]][PullRequests] +[![Badge Closed]][Closed] + +[![Badge Size]][#] +[![Badge License]][License] +[![Badge Contributors]][Contributors] + +[![Badge Test]][Test] +[![Badge Conan]][Conan] + +<br> +<br> + +![Logo] + +# Ultimaker Cura + +*State-of-the-art slicer app to prepare* <br> +*your 3D models for your 3D printer.* + +*With hundreds of settings & community-managed print profiles,* <br> +*Ultimaker Cura is sure to lead your next project to a success.* + +<br> +<br> + +[![Button Building]][Building] +[![Button Plugins]][Plugins] +[![Button Machines]][Machines] + +[![Button Report]][Report] +[![Button Settings]][Settings] +[![Button Localize]][Localize] + +<br> +<br> + +<picture> + <source media="(prefers-color-scheme: light)" srcset="./cura-logo.PNG"> + <source media="(prefers-color-scheme: dark)" srcset="./cura-logo-dark.PNG"> + <img alt="Shows cura open on the preview screen with a large benchy model in the center." src="./cura-logo.PNG"> +</picture> + +</div> + +<br> + + +<!-----------------------------------------------------------------------------> + +[Contributors]: https://github.com/Ultimaker/Cura/graphs/contributors +[PullRequests]: https://github.com/Ultimaker/Cura/pulls +[Machines]: https://github.com/Ultimaker/Cura/wiki/Adding-new-machine-profiles-to-Cura +[Building]: https://github.com/Ultimaker/Cura/wiki/Running-Cura-from-Source +[Localize]: https://github.com/Ultimaker/Cura/wiki/Translating-Cura +[Settings]: https://github.com/Ultimaker/Cura/wiki/Cura-Settings +[Plugins]: https://github.com/Ultimaker/Cura/wiki/Plugin-Directory +[Closed]: https://github.com/Ultimaker/Cura/issues?q=is%3Aissue+is%3Aclosed +[Issues]: https://github.com/Ultimaker/Cura/issues +[Conan]: https://github.com/Ultimaker/Cura/actions/workflows/conan-package.yml +[Test]: https://github.com/Ultimaker/Cura/actions/workflows/unit-test.yml + +[License]: LICENSE +[Report]: docs/Report.md +[Logo]: resources/images/cura-icon.png +[#]: # + + +<!---------------------------------[ Badges ]----------------------------------> + +[Badge Contributors]: https://img.shields.io/github/contributors/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=db5e8a&color=ab4a6c&logo=GitHub +[Badge PullRequests]: https://img.shields.io/github/issues-pr/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=bb9f3e&color=937d31&logo=GitExtensions +[Badge License]: https://img.shields.io/badge/License-LGPL3-336887.svg?style=for-the-badge&labelColor=458cb5&logoColor=white&logo=GNU +[Badge Closed]: https://img.shields.io/github/issues-closed/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=629944&color=446a30&logo=AddThis +[Badge Issues]: https://img.shields.io/github/issues/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=c34360&color=933349&logo=AdBlock +[Badge Conan]: https://img.shields.io/github/workflow/status/Ultimaker/Cura/conan-package?style=for-the-badge&logoColor=white&labelColor=6185aa&color=4c6987&logo=Conan&label=Conan%20Package +[Badge Test]: https://img.shields.io/github/workflow/status/Ultimaker/Cura/unit-test?style=for-the-badge&logoColor=white&labelColor=4a999d&color=346c6e&logo=Codacy&label=Unit%20Test +[Badge Size]: https://img.shields.io/github/repo-size/ultimaker/cura?style=for-the-badge&logoColor=white&labelColor=715a97&color=584674&logo=GoogleAnalytics + + +<!---------------------------------[ Buttons ]---------------------------------> + +[Button Localize]: https://img.shields.io/badge/Help_Localize-e2467d?style=for-the-badge&logoColor=white&logo=GoogleTranslate +[Button Machines]: https://img.shields.io/badge/Adding_Machines-yellow?style=for-the-badge&logoColor=white&logo=CloudFoundry +[Button Settings]: https://img.shields.io/badge/Configuration-00979D?style=for-the-badge&logoColor=white&logo=CodeReview +[Button Building]: https://img.shields.io/badge/Building_Cura-blue?style=for-the-badge&logoColor=white&logo=GitBook +[Button Plugins]: https://img.shields.io/badge/Plugin_Usage-569A31?style=for-the-badge&logoColor=white&logo=ROS +[Button Report]: https://img.shields.io/badge/Report_Issues-C9284D?style=for-the-badge&logoColor=white&logo=Cliqz + + diff --git a/cura-logo-dark.PNG b/cura-logo-dark.PNG Binary files differnew file mode 100644 index 0000000000..c176eda379 --- /dev/null +++ b/cura-logo-dark.PNG diff --git a/cura-logo.PNG b/cura-logo.PNG Binary files differindex 52da8203a8..972dcd7535 100644 --- a/cura-logo.PNG +++ b/cura-logo.PNG diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7fa33d32f3..2fddc51558 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -115,6 +115,7 @@ from . import CuraActions from . import PlatformPhysics from . import PrintJobPreviewImageProvider from .AutoSave import AutoSave +from .Machines.Models.MachineListModel import MachineListModel from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel from .Machines.Models.IntentSelectionModel import IntentSelectionModel from .SingleInstance import SingleInstance @@ -145,8 +146,6 @@ class CuraApplication(QtApplication): DefinitionChangesContainer = Resources.UserType + 10 SettingVisibilityPreset = Resources.UserType + 11 IntentInstanceContainer = Resources.UserType + 12 - AbstractMachineStack = Resources.UserType + 13 - pyqtEnum(ResourceTypes) @@ -425,7 +424,6 @@ class CuraApplication(QtApplication): Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility") Resources.addStorageType(self.ResourceTypes.IntentInstanceContainer, "intent") - Resources.addStorageType(self.ResourceTypes.AbstractMachineStack, "abstract_machine_instances") self._container_registry.addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") self._container_registry.addResourceType(self.ResourceTypes.QualityChangesInstanceContainer, "quality_changes") @@ -436,7 +434,6 @@ class CuraApplication(QtApplication): self._container_registry.addResourceType(self.ResourceTypes.MachineStack, "machine") self._container_registry.addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") self._container_registry.addResourceType(self.ResourceTypes.IntentInstanceContainer, "intent") - self._container_registry.addResourceType(self.ResourceTypes.AbstractMachineStack, "abstract_machine") Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.Firmware, "firmware") @@ -485,7 +482,6 @@ class CuraApplication(QtApplication): ("variant", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.VariantInstanceContainer, "application/x-uranium-instancecontainer"), ("setting_visibility", SettingVisibilityPresetsModel.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.SettingVisibilityPreset, "application/x-uranium-preferences"), ("machine", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"), - ("abstract_machine", 1): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer"), ("extruder", 2): (Resources.DefinitionContainers, "application/x-uranium-definitioncontainer") } ) @@ -1194,6 +1190,7 @@ class CuraApplication(QtApplication): qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel") + qmlRegisterType(MachineListModel, "Cura", 1, 0, "MachineListModel") self.processEvents() qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 2cb333d157..b113bd6dd7 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -45,11 +45,7 @@ class MachineErrorChecker(QObject): self._start_time = 0. # measure checking time - # This timer delays the starting of error check so we can react less frequently if the user is frequently - # changing settings. - self._error_check_timer = QTimer(self) - self._error_check_timer.setInterval(100) - self._error_check_timer.setSingleShot(True) + self._setCheckTimer() self._keys_to_check = set() # type: Set[str] @@ -66,6 +62,18 @@ class MachineErrorChecker(QObject): self._onMachineChanged() + def _setCheckTimer(self) -> None: + """A QTimer to regulate error check frequency + + This timer delays the starting of error check + so we can react less frequently if the user is frequently + changing settings. + """ + + self._error_check_timer = QTimer(self) + self._error_check_timer.setInterval(100) + self._error_check_timer.setSingleShot(True) + def _onMachineChanged(self) -> None: if self._global_stack: self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged) diff --git a/cura/Machines/Models/MachineListModel.py b/cura/Machines/Models/MachineListModel.py new file mode 100644 index 0000000000..a758060384 --- /dev/null +++ b/cura/Machines/Models/MachineListModel.py @@ -0,0 +1,99 @@ +# Copyright (c) 2022 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +# The MachineListModel is used to display the connected printers in the interface. Both the abstract machines and all +# online cloud connected printers are represented within this ListModel. Additional information such as the number of +# connected printers for each printer type is gathered. + +from PyQt6.QtCore import Qt, QTimer + +from UM.Qt.ListModel import ListModel +from UM.Settings.ContainerStack import ContainerStack +from UM.i18n import i18nCatalog +from UM.Util import parseBool + +from cura.Settings.CuraContainerRegistry import CuraContainerRegistry +from cura.Settings.GlobalStack import GlobalStack + + +class MachineListModel(ListModel): + NameRole = Qt.ItemDataRole.UserRole + 1 + IdRole = Qt.ItemDataRole.UserRole + 2 + HasRemoteConnectionRole = Qt.ItemDataRole.UserRole + 3 + MetaDataRole = Qt.ItemDataRole.UserRole + 4 + IsOnlineRole = Qt.ItemDataRole.UserRole + 5 + MachineCountRole = Qt.ItemDataRole.UserRole + 6 + IsAbstractMachine = Qt.ItemDataRole.UserRole + 7 + + def __init__(self, parent=None) -> None: + super().__init__(parent) + + self._catalog = i18nCatalog("cura") + + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection") + self.addRoleName(self.MetaDataRole, "metadata") + self.addRoleName(self.IsOnlineRole, "isOnline") + self.addRoleName(self.MachineCountRole, "machineCount") + self.addRoleName(self.IsAbstractMachine, "isAbstractMachine") + + self._change_timer = QTimer() + self._change_timer.setInterval(200) + self._change_timer.setSingleShot(True) + self._change_timer.timeout.connect(self._update) + + # Listen to changes + CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) + self._updateDelayed() + + def _onContainerChanged(self, container) -> None: + """Handler for container added/removed events from registry""" + + # We only need to update when the added / removed container GlobalStack + if isinstance(container, GlobalStack): + self._updateDelayed() + + def _updateDelayed(self) -> None: + self._change_timer.start() + + def _update(self) -> None: + self.setItems([]) # Clear items + + other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") + + abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True") + abstract_machine_stacks.sort(key = lambda machine: machine.getName(), reverse = True) + + for abstract_machine in abstract_machine_stacks: + definition_id = abstract_machine.definition.getId() + from cura.CuraApplication import CuraApplication + machines_manager = CuraApplication.getInstance().getMachineManager() + online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True) + + # Create a list item for abstract machine + self.addItem(abstract_machine, len(online_machine_stacks)) + other_machine_stacks.remove(abstract_machine) + + # Create list of machines that are children of the abstract machine + for stack in online_machine_stacks: + self.addItem(stack) + # Remove this machine from the other stack list + other_machine_stacks.remove(stack) + + for stack in other_machine_stacks: + self.addItem(stack) + + def addItem(self, container_stack: ContainerStack, machine_count: int = 0) -> None: + if parseBool(container_stack.getMetaDataEntry("hidden", False)): + return + + self.appendItem({"name": container_stack.getName(), + "id": container_stack.getId(), + "metadata": container_stack.getMetaData().copy(), + "isOnline": parseBool(container_stack.getMetaDataEntry("is_online", False)), + "isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)), + "machineCount": machine_count, + }) diff --git a/cura/PrinterOutput/Peripheral.py b/cura/PrinterOutput/Peripheral.py index 27d127832b..4613506978 100644 --- a/cura/PrinterOutput/Peripheral.py +++ b/cura/PrinterOutput/Peripheral.py @@ -1,20 +1,19 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from dataclasses import dataclass +@dataclass class Peripheral: """Data class that represents a peripheral for a printer. Output device plug-ins may specify that the printer has a certain set of peripherals. This set is then possibly shown in the interface of the monitor stage. - """ - - def __init__(self, peripheral_type: str, name: str) -> None: - """Constructs the peripheral. - :param peripheral_type: A unique ID for the type of peripheral. - :param name: A human-readable name for the peripheral. - """ - self.type = peripheral_type - self.name = name + Args: + type (string): A unique ID for the type of peripheral. + name (string): A human-readable name for the peripheral. + """ + type: str + name: str diff --git a/cura/Scene/CuraSceneController.py b/cura/Scene/CuraSceneController.py index 1d604b74b6..54f1cc56f2 100644 --- a/cura/Scene/CuraSceneController.py +++ b/cura/Scene/CuraSceneController.py @@ -139,7 +139,7 @@ class CuraSceneController(QObject): def setActiveBuildPlate(self, nr): if nr == self._active_build_plate: return - Logger.log("d", "Select build plate: %s" % nr) + Logger.debug(f"Selected build plate: {nr}") self._active_build_plate = nr Selection.clear() diff --git a/cura/Settings/AbstractMachine.py b/cura/Settings/AbstractMachine.py deleted file mode 100644 index a89201a294..0000000000 --- a/cura/Settings/AbstractMachine.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import List - -from UM.Settings.ContainerStack import ContainerStack -from cura.PrinterOutput.PrinterOutputDevice import ConnectionType -from cura.Settings.GlobalStack import GlobalStack -from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase -from UM.Settings.ContainerRegistry import ContainerRegistry - - -class AbstractMachine(GlobalStack): - """ Represents a group of machines of the same type. This allows the user to select settings before selecting a printer. """ - - def __init__(self, container_id: str) -> None: - super().__init__(container_id) - self.setMetaDataEntry("type", "abstract_machine") - - def getMachines(self) -> List[ContainerStack]: - from cura.CuraApplication import CuraApplication - - application = CuraApplication.getInstance() - registry = application.getContainerRegistry() - - printer_type = self.definition.getId() - return [machine for machine in registry.findContainerStacks(type="machine") if machine.definition.id == printer_type and ConnectionType.CloudConnection in machine.configuredConnectionTypes] - - -## private: -_abstract_machine_mime = MimeType( - name = "application/x-cura-abstract-machine", - comment = "Cura Abstract Machine", - suffixes = ["global.cfg"] -) - -MimeTypeDatabase.addMimeType(_abstract_machine_mime) -ContainerRegistry.addContainerTypeByName(AbstractMachine, "abstract_machine", _abstract_machine_mime.name) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 5348deb4bd..a8a1d780ea 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -427,4 +427,4 @@ class _ContainerIndexes: } # Reverse lookup: type -> index - TypeIndexMap = dict([(v, k) for k, v in IndexTypeMap.items()]) + TypeIndexMap = {v: k for k, v in IndexTypeMap.items()} diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 7eff275457..5a745f8f0a 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -9,7 +9,6 @@ from UM.Settings.Interfaces import DefinitionContainerInterface from UM.Settings.InstanceContainer import InstanceContainer from cura.Machines.ContainerTree import ContainerTree -from .AbstractMachine import AbstractMachine from .GlobalStack import GlobalStack from .ExtruderStack import ExtruderStack @@ -268,21 +267,21 @@ class CuraStackBuilder: return definition_changes_container @classmethod - def createAbstractMachine(cls, definition_id: str) -> Optional[AbstractMachine]: + def createAbstractMachine(cls, definition_id: str) -> Optional[GlobalStack]: """Create a new instance of an abstract machine. :param definition_id: The ID of the machine definition to use. :return: The new Abstract Machine or None if an error occurred. """ - abstract_machine_id = definition_id + "_abstract_machine" + abstract_machine_id = f"{definition_id}_abstract_machine" from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() registry = application.getContainerRegistry() container_tree = ContainerTree.getInstance() - if registry.findContainerStacks(type = "abstract_machine", id = abstract_machine_id): + if registry.findContainerStacks(is_abstract_machine = "True", id = abstract_machine_id): # This abstract machine already exists return None @@ -296,7 +295,9 @@ class CuraStackBuilder: machine_node = container_tree.machines[machine_definition.getId()] name = machine_definition.getName() - stack = AbstractMachine(abstract_machine_id) + stack = GlobalStack(abstract_machine_id) + stack.setMetaDataEntry("is_abstract_machine", True) + stack.setMetaDataEntry("is_online", True) stack.setDefinition(machine_definition) cls.createUserContainer( name, diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index f0a6946f88..3c13f236ab 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2022 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from collections import defaultdict @@ -8,10 +8,9 @@ import uuid from PyQt6.QtCore import pyqtProperty, pyqtSlot, pyqtSignal -from UM.Decorators import deprecated, override +from UM.Decorators import override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase from UM.Settings.ContainerStack import ContainerStack -from UM.Settings.SettingInstance import InstanceState from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.Interfaces import PropertyEvaluationContext from UM.Logger import Logger @@ -344,14 +343,12 @@ class GlobalStack(CuraContainerStack): def getName(self) -> str: return self._metadata.get("group_name", self._metadata.get("name", "")) - def setName(self, name: "str") -> None: + def setName(self, name: str) -> None: super().setName(name) nameChanged = pyqtSignal() name = pyqtProperty(str, fget=getName, fset=setName, notify=nameChanged) - - ## private: global_stack_mime = MimeType( name = "application/x-cura-globalstack", diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 64d34d6c3e..389c5ded75 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -19,6 +19,7 @@ from UM.Logger import Logger from UM.Message import Message from UM.Settings.SettingFunction import SettingFunction +from UM.Settings.ContainerStack import ContainerStack from UM.Signal import postponeSignals, CompressTechnique import cura.CuraApplication # Imported like this to prevent circular references. @@ -186,6 +187,32 @@ class MachineManager(QObject): self.outputDevicesChanged.emit() + def getMachinesWithDefinition(self, definition_id: str, online_only=False) -> List[ContainerStack]: + """ Fetches all container stacks that match definition_id. + + :param definition_id: The id of the machine definition. + :return: A list of Containers that match definition_id + """ + from cura.CuraApplication import CuraApplication # In function to avoid circular import + application = CuraApplication.getInstance() + registry = application.getContainerRegistry() + + machines = registry.findContainerStacks(type="machine") + # Filter machines that match definition + machines = filter(lambda machine: machine.definition.id == definition_id, machines) + # Filter only LAN and Cloud printers + machines = filter(lambda machine: ConnectionType.CloudConnection in machine.configuredConnectionTypes or + ConnectionType.NetworkConnection in machine.configuredConnectionTypes, + machines) + if online_only: + # LAN printers can have is_online = False but should still be included, + # their online status is only checked when they are the active printer. + machines = filter(lambda machine: parseBool(machine.getMetaDataEntry("is_online", False) or + ConnectionType.NetworkConnection in machine.configuredConnectionTypes), + machines) + + return list(machines) + @pyqtProperty(QObject, notify = currentConfigurationChanged) def currentConfiguration(self) -> PrinterConfigurationModel: return self._current_printer_configuration diff --git a/cura/TaskManagement/OnExitCallbackManager.py b/cura/TaskManagement/OnExitCallbackManager.py index 7894931e9c..54121a2960 100644 --- a/cura/TaskManagement/OnExitCallbackManager.py +++ b/cura/TaskManagement/OnExitCallbackManager.py @@ -10,10 +10,13 @@ if TYPE_CHECKING: # -# This class manages a all registered upon-exit checks that need to be perform when the application tries to exit. -# For example, to show a confirmation dialog when there is USB printing in progress, etc. All callbacks will be called -# in the order of when they got registered. If all callbacks "passes", that is, for example, if the user clicks "yes" -# on the exit confirmation dialog or nothing that's blocking the exit, then the application will quit after that. +# This class manages all registered upon-exit checks +# that need to be performed when the application tries to exit. +# For example, show a confirmation dialog when there is USB printing in progress. +# All callbacks will be called in the order of when they were registered. +# If all callbacks "pass", for example: +# if the user clicks "yes" on the exit confirmation dialog +# and nothing else is blocking the exit, then the application will quit. # class OnExitCallbackManager: @@ -35,10 +38,12 @@ class OnExitCallbackManager: def getIsAllChecksPassed(self) -> bool: return self._is_all_checks_passed - # Trigger the next callback if available. If not, it means that all callbacks have "passed", which means we should - # not block the application to quit, and it will call the application to actually quit. + # Trigger the next callback if there is one. + # If not, all callbacks have "passed", + # which means we should not prevent the application from quitting, + # and we call the application to actually quit. def triggerNextCallback(self) -> None: - # Get the next callback and schedule that if + # Get the next callback and schedule it this_callback = None if self._current_callback_idx < len(self._on_exit_callback_list): this_callback = self._on_exit_callback_list[self._current_callback_idx] @@ -55,10 +60,11 @@ class OnExitCallbackManager: # Tell the application to exit self._application.callLater(self._application.closeApplication) - # This is the callback function which an on-exit callback should call when it finishes, it should provide the - # "should_proceed" flag indicating whether this check has "passed", or in other words, whether quitting the - # application should be blocked. If the last on-exit callback doesn't block the quitting, it will call the next - # registered on-exit callback if available. + # Callback function which an on-exit callback calls when it finishes. + # It provides a "should_proceed" flag indicating whether the check has "passed", + # or whether quitting the application should be blocked. + # If the last on-exit callback doesn't block quitting, it will call the next + # registered on-exit callback if one is available. def onCurrentCallbackFinished(self, should_proceed: bool = True) -> None: if not should_proceed: Logger.log("d", "on-app-exit callback finished and we should not proceed.") diff --git a/docs/Report.md b/docs/Report.md new file mode 100644 index 0000000000..8b24903878 --- /dev/null +++ b/docs/Report.md @@ -0,0 +1,81 @@ + +# Reporting Issues + +Please attach the following information in case <br> +you want to report crashing or similar issues. + +<br> + +## DxDiag + +### ![Badge Windows] + +The log as produced by **dxdiag**. + +<kbd> start </kbd> » <kbd> run </kbd> » <kbd> dxdiag </kbd> » <kbd> save output </kbd> + +<br> +<br> + +## Cura GUI Log + +If the Cura user interface still starts, you can also <br> +reach these directories from the application menu: + +<kbd> Help </kbd> » <kbd> Show settings folder </kbd> + +<br> + +### ![Badge Windows] + +``` +%APPDATA%\cura\<Cura Version>\cura.log +``` + +or + +``` +C:\Users\<your username>\AppData\Roaming\cura\<Cura Version>\cura.log +``` + +<br> + +### ![Badge Linux] + +``` +~/.local/share/cura/<Cura Version>/cura.log +``` + +<br> + +### ![Badge MacOS] + +``` +~/Library/Application Support/cura/<Cura Version>/cura.log +``` + +<br> +<br> + +## Alternative + +An alternative is to install the **[ExtensiveSupportLogging]** <br> +plugin this creates a zip folder of the relevant log files. + +If you're experiencing performance issues, we might ask <br> +you to connect the CPU profiler in this plugin and attach <br> +the collected data to your support ticket. + +<br> + + +<!-----------------------------------------------------------------------------> + +[ExtensiveSupportLogging]: https://marketplace.ultimaker.com/app/cura/plugins/UltimakerPackages/ExtensiveSupportLogging + + +<!---------------------------------[ Badges ]----------------------------------> + +[Badge Windows]: https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logoColor=white&logo=Windows +[Badge Linux]: https://img.shields.io/badge/Linux-00A95C?style=for-the-badge&logoColor=white&logo=Linux +[Badge MacOS]: https://img.shields.io/badge/MacOS-403C3D?style=for-the-badge&logoColor=white&logo=MacOS diff --git a/docs/profiles/setting_properties.md b/docs/profiles/setting_properties.md index 84fb458c63..27a0d17c4d 100644 --- a/docs/profiles/setting_properties.md +++ b/docs/profiles/setting_properties.md @@ -2,32 +2,77 @@ Setting Properties ==== Each setting in Cura has a number of properties. It's not just a key and a value. This page lists the properties that a setting can define. -* `key` (string): The identifier by which the setting is referenced. This is not a human-readable name, but just a reference string, such as `layer_height_0`. Typically these are named with the most significant category first, in order to sort them better, such as `material_print_temperature`. This is not actually a real property but just an identifier; it can't be changed. -* `value` (optional): The current value of the setting. This can be a function, an arbitrary Python expression that depends on the values of other settings. If it's not present, the `default_value` is used. -* `default_value`: A default value for the setting if `value` is undefined. This property is required however. It can't be a Python expression, but it can be any JSON type. This is made separate so that CuraEngine can read it out as well for its debugging mode via the command line, without needing a complete Python interpreter. -* `label` (string): The human-readable name for the setting. This label is translated. -* `description` (string): A longer description of what the setting does when you change it. This description is translated as well. -* `type` (string): The type of value that this setting contains. Allowed types are: `bool`, `str`, `float`, `int`, `enum`, `category`, `[int]`, `vec3`, `polygon` and `polygons`. -* `unit` (optional string): A unit that is displayed at the right-hand side of the text field where the user enters the setting value. -* `resolve` (optional string): A Python expression that resolves disagreements for global settings if multiple per-extruder profiles define different values for a setting. Typically this takes the values for the setting from all stacks and computes one final value for it that will be used for the global setting. For instance, the `resolve` function for the build plate temperature is `max(extruderValues('material_bed_temperature')`, meaning that it will use the hottest bed temperature of all materials of the extruders in use. -* `limit_to_extruder` (optional): A Python expression that indicates which extruder a setting will be obtained from. This is used for settings that may be extruder-specific but the extruder is not necessarily the current extruder. For instance, support settings need to be evaluated for the support extruder. Infill settings need to be evaluated for the infill extruder if the infill extruder is changed. -* `enabled` (optional string or boolean): Whether the setting can currently be made visible for the user. This can be a simple true/false, or a Python expression that depends on other settings. Typically used for settings that don't apply when another setting is disabled, such as to hide the support settings if support is disabled. -* `minimum_value` (optional): The lowest acceptable value for this setting. If it's any lower, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a layer height of 0mm. This property only applies to numerical settings. -* `maximum_value` (optional): The highest acceptable value for this setting. If it's any higher, Cura will not allow the user to slice. By convention this is used to prevent setting values that are technically or physically impossible, such as a support overhang angle of more than 90 degrees. This property only applies to numerical settings. -* `minimum_value_warning` (optional): The threshold under which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a low setting value. This property only applies to numerical settings. -* `maximum_value_warning` (optional): The threshold above which a warning is displayed to the user. By convention this is used to indicate that it will probably not print very nicely with such a high setting value. This property only applies to numerical settings. -* `settable_globally` (optional boolean): Whether the setting can be changed globally. For some mesh-type settings such as `support_mesh` this doesn't make sense, so those can't be changed globally. They are not displayed in the main settings list then. -* `settable_per_meshgroup` (optional boolean): Whether a setting can be changed per group of meshes. Currently unused in Cura. -* `settable_per_extruder` (optional boolean): Whether a setting can be changed per extruder. Some settings, like the build plate temperature, can't be adjusted separately for each extruder. An icon is shown in the interface to indicate this. If the user changes these settings they are stored in the global stack. -* `settable_per_mesh` (optional boolean): Whether a setting can be changed per mesh. The settings that can be changed per mesh are shown in the list of available settings in the per-object settings tool. -* `children` (optional list): A list of child settings. These are displayed with an indentation. If all child settings are overridden by the user, the parent setting gets greyed out to indicate that the parent setting has no effect any more. This is not strictly always the case though, because that would depend on the inheritance functions in the `value`. -* `icon` (optional string): A path to an icon to be displayed. Only applies to setting categories. -* `allow_empty` (optional bool): Whether the setting is allowed to be empty. If it's not, this will be treated as a setting error and Cura will not allow the user to slice. Only applies to string-type settings. -* `warning_description` (optional string): A warning message to display when the setting has a warning value. This is currently unused by Cura. -* `error_description` (optional string): An error message to display when the setting has an error value. This is currently unused by Cura. -* `options` (dictionary): A list of values that the user can choose from. The keys of this dictionary are keys that CuraEngine identifies the option with. The values are human-readable strings and will be translated. Only applies to (and only required for) enum-type settings. -* `comments` (optional string): Comments to other programmers about the setting. This is not used by Cura. -* `is_uuid` (optional boolean): Whether or not this setting indicates a UUID-4. If it is, the setting will indicate an error if it's not in the correct format. Only applies to string-type settings. -* `regex_blacklist_pattern` (optional string): A regular expression, where if the setting value matches with this regular expression, it gets an error state. Only applies to string-type settings. -* `error_value` (optional): If the setting value is equal to this value, it will show a setting error. This is used to display errors for non-numerical settings such as checkboxes. -* `warning_value` (optional): If the setting value is equal to this value, it will show a setting warning. This is used to display warnings for non-numerical settings such as checkboxes.
\ No newline at end of file +* `key` (string): __The identifier by which the setting is referenced.__ + * This is not a human-readable name, but just a reference string, such as `layer_height_0`. + * This is not actually a real property but just an identifier; it can't be changed. + * Typically these are named with the most significant category first, in order to sort them better, such as `material_print_temperature`. +* `value` (optional): __The current value of the setting.__ + * This can be a function (an arbitrary Python expression) that depends on the values of other settings. + * If it's not present, the `default_value` is used. +* `default_value`: __A default value for the setting if `value` is undefined.__ + * This property is required. + * It can't be a Python expression, but it can be any JSON type. + * This is made separate so that CuraEngine can read it out for its debugging mode via the command line, without needing a complete Python interpreter. +* `label` (string): __The human-readable name for the setting.__ + * This label is translated. +* `description` (string): __A longer description of what the setting does when you change it.__ + * This description is translated. +* `type` (string): __The type of value that this setting contains.__ + * Allowed types are: `bool`, `str`, `float`, `int`, `enum`, `category`, `[int]`, `vec3`, `polygon` and `polygons`. +* `unit` (optional string): __A unit that is displayed at the right-hand side of the text field where the user enters the setting value.__ +* `resolve` (optional string): __A Python expression that resolves disagreements for global settings if multiple per-extruder profiles define different values for a setting.__ + * Typically this takes the values for the setting from all stacks and computes one final value for it that will be used for the global setting. For instance, the `resolve` function for the build plate temperature is `max(extruderValues('material_bed_temperature')`, meaning that it will use the hottest bed temperature of all materials of the extruders in use. +* `limit_to_extruder` (optional): __A Python expression that indicates which extruder a setting will be obtained from.__ + * This is used for settings that may be extruder-specific but the extruder is not necessarily the current extruder. For instance, support settings need to be evaluated for the support extruder. Infill settings need to be evaluated for the infill extruder if the infill extruder is changed. +* `enabled` (optional string or boolean): __Whether the setting can currently be made visible for the user.__ + * This can be a simple true/false, or a Python expression that depends on other settings. + * Typically used for settings that don't apply when another setting is disabled, such as to hide the support settings if support is disabled. +* `minimum_value` (optional): __The lowest acceptable value for this setting.__ + * If it's any lower, Cura will not allow the user to slice. + * This property only applies to numerical settings. + * By convention this is used to prevent setting values that are technically or physically impossible, such as a layer height of 0mm. +* `maximum_value` (optional): __The highest acceptable value for this setting.__ + * If it's any higher, Cura will not allow the user to slice. + * This property only applies to numerical settings. + * By convention this is used to prevent setting values that are technically or physically impossible, such as a support overhang angle of more than 90 degrees. +* `minimum_value_warning` (optional): __The threshold under which a warning is displayed to the user.__ + * This property only applies to numerical settings. + * By convention this is used to indicate that it will probably not print very nicely with such a low setting value. +* `maximum_value_warning` (optional): __The threshold above which a warning is displayed to the user.__ + * This property only applies to numerical settings. + * By convention this is used to indicate that it will probably not print very nicely with such a high setting value. +* `settable_globally` (optional boolean): __Whether the setting can be changed globally.__ + * For some mesh-type settings such as `support_mesh` this doesn't make sense, so those can't be changed globally. They are not displayed in the main settings list then. +* `settable_per_meshgroup` (optional boolean): __Whether a setting can be changed per group of meshes.__ + * *This is currently unused by Cura.* +* `settable_per_extruder` (optional boolean): __Whether a setting can be changed per extruder.__ + * Some settings, like the build plate temperature, can't be adjusted separately for each extruder. An icon is shown in the interface to indicate this. + * If the user changes these settings they are stored in the global stack. +* `settable_per_mesh` (optional boolean): __Whether a setting can be changed per mesh.__ + * The settings that can be changed per mesh are shown in the list of available settings in the per-object settings tool. +* `children` (optional list): __A list of child settings.__ + * These are displayed with an indentation. If all child settings are overridden by the user, the parent setting gets greyed out to indicate that the parent setting has no effect any more. This is not strictly always the case though, because that would depend on the inheritance functions in the `value`. +* `icon` (optional string): __A path to an icon to be displayed.__ + * Only applies to setting categories. +* `allow_empty` (optional bool): __Whether the setting is allowed to be empty.__ + * If it's not, this will be treated as a setting error and Cura will not allow the user to slice. + * Only applies to string-type settings. +* `warning_description` (optional string): __A warning message to display when the setting has a warning value.__ + * *This is currently unused by Cura.* +* `error_description` (optional string): __An error message to display when the setting has an error value.__ + * *This is currently unused by Cura.* +* `options` (dictionary): __A list of values that the user can choose from.__ + * The keys of this dictionary are keys that CuraEngine identifies the option with. + * The values are human-readable strings and will be translated. + * Only applies to (and only required for) enum-type settings. +* `comments` (optional string): __Comments to other programmers about the setting.__ + * *This is currently unused by Cura.* +* `is_uuid` (optional boolean): __Whether or not this setting indicates a UUID-4.__ + * If it is, the setting will indicate an error if it's not in the correct format. + * Only applies to string-type settings. +* `regex_blacklist_pattern` (optional string): __A regular expression, where if the setting value matches with this regular expression, it gets an error state.__ + * Only applies to string-type settings. +* `error_value` (optional): __If the setting value is equal to this value, it will show a setting error.__ + * This is used to display errors for non-numerical settings such as checkboxes. +* `warning_value` (optional): __If the setting value is equal to this value, it will show a setting warning.__ + * This is used to display warnings for non-numerical settings such as checkboxes. diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py index 0cd5304cf9..bddd383b23 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py @@ -232,6 +232,9 @@ class LocalClusterOutputDeviceManager: self._connectToOutputDevice(device, new_machine) self._showCloudFlowMessage(device) + _abstract_machine = CuraStackBuilder.createAbstractMachine(device.printerType) + + def _storeManualAddress(self, address: str) -> None: """Add an address to the stored preferences.""" diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index ce641cf032..4a40d1855a 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -6442,10 +6442,10 @@ "description": "The maximum extrusion area deviation allowed when removing intermediate points from a straight line. An intermediate point may serve as width-changing point in a long straight line. Therefore, if it is removed, it will cause the line to have a uniform width and, as a result, lose (or gain) a bit of extrusion area. If you increase this you may notice slight under- (or over-) extrusion in between straight parallel walls, as more intermediate width-changing points will be allowed to be removed. Your print will be less accurate, but the g-code will be smaller.", "type": "float", "unit": "μm²", - "default_value": 2000, + "default_value": 50000, "minimum_value": "0", "minimum_value_warning": "500", - "maximum_value_warning": "50000", + "maximum_value_warning": "100000", "settable_per_mesh": true } } diff --git a/resources/qml/PrinterSelector/MachineListButton.qml b/resources/qml/PrinterSelector/MachineListButton.qml new file mode 100644 index 0000000000..55ae5497d9 --- /dev/null +++ b/resources/qml/PrinterSelector/MachineListButton.qml @@ -0,0 +1,87 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import UM 1.5 as UM +import Cura 1.0 as Cura + + +Button +{ + id: machineListButton + + width: parent.width + height: UM.Theme.getSize("large_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + checkable: true + hoverEnabled: true + + contentItem: Item + { + width: machineListButton.width - machineListButton.leftPadding - machineListButton.rightPadding + height: UM.Theme.getSize("action_button").height + + UM.ColorImage + { + id: printerIcon + height: UM.Theme.getSize("medium_button").height + width: UM.Theme.getSize("medium_button").width + color: UM.Theme.getColor("machine_selector_printer_icon") + visible: model.isAbstractMachine || !model.isOnline + source: model.isAbstractMachine ? UM.Theme.getIcon("PrinterTriple", "medium") : UM.Theme.getIcon("Printer", "medium") + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + } + } + + UM.Label + { + id: buttonText + anchors + { + left: printerIcon.right + right: printerCount.left + verticalCenter: parent.verticalCenter + leftMargin: UM.Theme.getSize("default_margin").width + } + text: machineListButton.text + font: model.isAbstractMachine ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium") + visible: text != "" + elide: Text.ElideRight + } + + Rectangle + { + id: printerCount + color: UM.Theme.getColor("background_2") + radius: height + width: height + anchors + { + right: parent.right + top: buttonText.top + bottom: buttonText.bottom + } + visible: model.isAbstractMachine + + UM.Label + { + text: model.machineCount + anchors.centerIn: parent + font: UM.Theme.getFont("default_bold") + } + } + } + + background: Rectangle + { + id: backgroundRect + color: machineListButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + } +} diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 77cd2be409..869d536a00 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -192,7 +192,7 @@ Cura.ExpandablePopup contentItem: Item { id: popup - implicitWidth: UM.Theme.getSize("machine_selector_widget_content").width + implicitWidth: Math.max(machineSelector.width, 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 { @@ -224,6 +224,9 @@ Cura.ExpandablePopup anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter + anchors.left: parent.left + anchors.right: parent.right + padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -236,7 +239,7 @@ Cura.ExpandablePopup // The maximum width of the button is half of the total space, minus the padding of the parent, the left // padding of the component and half the spacing because of the space between buttons. fixedWidthMode: true - width: UM.Theme.getSize("machine_selector_widget_content").width / 2 - leftPadding + width: buttonRow.width / 2 - leftPadding * 1.5 onClicked: { toggleContent() @@ -253,7 +256,7 @@ Cura.ExpandablePopup fixedWidthMode: true // The maximum width of the button is half of the total space, minus the padding of the parent, the right // padding of the component and half the spacing because of the space between buttons. - width: UM.Theme.getSize("machine_selector_widget_content").width / 2 - leftPadding + width: buttonRow.width / 2 - rightPadding * 1.5 onClicked: { toggleContent() diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index ae2706f9ab..06c2fdb40c 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -10,8 +10,8 @@ import Cura 1.0 as Cura ListView { id: listView - model: Cura.GlobalStacksModel {} - section.property: "hasRemoteConnection" + model: Cura.MachineListModel {} + section.property: "isOnline" property real contentHeight: childrenRect.height ScrollBar.vertical: UM.ScrollBar @@ -21,7 +21,7 @@ ListView section.delegate: UM.Label { - text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Preset printers") + text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Other printers") width: parent.width - scrollBar.width height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width @@ -29,13 +29,10 @@ ListView color: UM.Theme.getColor("text_medium") } - delegate: MachineSelectorButton + delegate: MachineListButton { text: model.name ? model.name : "" width: listView.width - scrollBar.width - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - checked: Cura.MachineManager.activeMachine ? Cura.MachineManager.activeMachine.id == model.id : false onClicked: { diff --git a/resources/qml/qmldir b/resources/qml/qmldir index a47d85545b..6ec3ca91c8 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -2,6 +2,7 @@ module Cura MachineSelector 1.0 MachineSelector.qml MachineSelectorButton 1.0 MachineSelectorButton.qml +MachineListButton 1.0 MachineListButton.qml CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml ProfileOverview 1.6 ProfileOverview.qml diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index e7622bc685..809bcfdee8 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -564,6 +564,9 @@ "medium_button": [2.5, 2.5], "medium_button_icon": [2, 2], + "large_button": [3.0, 3.0], + "large_button_icon": [2.8, 2.8], + "context_menu": [20, 2], "icon_indicator": [1, 1], |