diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 667390b946..0493eb68af 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -297,7 +297,6 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): resource_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources") Resources.addSearchPath(resource_path) - Resources.setBundledResourcesPath(resource_path) # Adds custom property types, settings types, and extra operators (functions) that need to be registered in # SettingDefinition and SettingFunction. @@ -397,6 +396,7 @@ class CuraApplication(QtApplication): def __setLatestResouceVersionsForVersionUpgrade(self): self._version_upgrade_manager.setCurrentVersions( { + ("quality", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"), ("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityChangesInstanceContainer, "application/x-uranium-instancecontainer"), ("machine_stack", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.MachineStack, "application/x-cura-globalstack"), ("extruder_train", ContainerStack.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.ExtruderStack, "application/x-cura-extruderstack"), diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index 2b91081e4d..0c2c438fcc 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -18,9 +18,6 @@ from UM.Version import Version class CuraPackageManager(QObject): Version = 1 - # The prefix that's added to all files for an installed package to avoid naming conflicts with user created files. - PREFIX_PLACE_HOLDER = "-CP;" - def __init__(self, parent = None): super().__init__(parent) @@ -28,15 +25,19 @@ class CuraPackageManager(QObject): self._container_registry = self._application.getContainerRegistry() self._plugin_registry = self._application.getPluginRegistry() - # JSON file that keeps track of all installed packages. - self._bundled_package_management_file_path = os.path.join( - os.path.abspath(Resources.getBundledResourcesPath()), - "packages.json" - ) - self._user_package_management_file_path = os.path.join( - os.path.abspath(Resources.getDataStoragePath()), - "packages.json" - ) + #JSON files that keep track of all installed packages. + self._user_package_management_file_path = None + self._bundled_package_management_file_path = None + for search_path in Resources.getSearchPaths(): + candidate_bundled_path = os.path.join(search_path, "bundled_packages.json") + if os.path.exists(candidate_bundled_path): + self._bundled_package_management_file_path = candidate_bundled_path + for search_path in (Resources.getDataStoragePath(), Resources.getConfigStoragePath()): + candidate_user_path = os.path.join(search_path, "packages.json") + if os.path.exists(candidate_user_path): + self._user_package_management_file_path = candidate_user_path + if self._user_package_management_file_path is None: #Doesn't exist yet. + self._user_package_management_file_path = os.path.join(Resources.getDataStoragePath(), "packages.json") self._bundled_package_dict = {} # A dict of all bundled packages self._installed_package_dict = {} # A dict of all installed packages @@ -136,14 +137,14 @@ class CuraPackageManager(QObject): all_installed_ids = all_installed_ids.union(set(self._bundled_package_dict.keys())) if self._installed_package_dict.keys(): all_installed_ids = all_installed_ids.union(set(self._installed_package_dict.keys())) + all_installed_ids = all_installed_ids.difference(self._to_remove_package_set) + # If it's going to be installed and to be removed, then the package is being updated and it should be listed. if self._to_install_package_dict.keys(): all_installed_ids = all_installed_ids.union(set(self._to_install_package_dict.keys())) - all_installed_ids = all_installed_ids.difference(self._to_remove_package_set) # map of -> -> installed_packages_dict = {} for package_id in all_installed_ids: - # Skip required plugins as they should not be tampered with if package_id in Application.getInstance().getRequiredPlugins(): continue @@ -168,7 +169,7 @@ class CuraPackageManager(QObject): package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id) # If the package ID is in bundled, label it as such - package_info["is_bundled"] = package_info["package_id"] in self._bundled_package_dict.keys() + package_info["is_bundled"] = package_info["package_id"] in self._bundled_package_dict.keys() and not self.isUserInstalledPackage(package_info["package_id"]) # If there is not a section in the dict for this type, add it if package_info["package_type"] not in installed_packages_dict: @@ -179,7 +180,7 @@ class CuraPackageManager(QObject): return installed_packages_dict - # Checks if the given package is installed. + # Checks if the given package is installed (at all). def isPackageInstalled(self, package_id: str) -> bool: return self.getInstalledPackageInfo(package_id) is not None @@ -194,11 +195,6 @@ class CuraPackageManager(QObject): return package_id = package_info["package_id"] - # Check the delayed installation and removal lists first - if package_id in self._to_remove_package_set: - self._to_remove_package_set.remove(package_id) - has_changes = True - # Check if it is installed installed_package_info = self.getInstalledPackageInfo(package_info["package_id"]) to_install_package = installed_package_info is None # Install if the package has not been installed @@ -235,23 +231,35 @@ class CuraPackageManager(QObject): self.installedPackagesChanged.emit() # Schedules the given package to be removed upon the next start. + # \param package_id id of the package + # \param force_add is used when updating. In that case you actually want to uninstall & install @pyqtSlot(str) - def removePackage(self, package_id: str) -> None: + def removePackage(self, package_id: str, force_add: bool = False) -> None: # Check the delayed installation and removal lists first if not self.isPackageInstalled(package_id): Logger.log("i", "Attempt to remove package [%s] that is not installed, do nothing.", package_id) return - # Remove from the delayed installation list if present - if package_id in self._to_install_package_dict: - del self._to_install_package_dict[package_id] + # Extra safety check + if package_id not in self._installed_package_dict and package_id in self._bundled_package_dict: + Logger.log("i", "Not uninstalling [%s] because it is a bundled package.") + return - # Schedule for a delayed removal: - self._to_remove_package_set.add(package_id) + if package_id not in self._to_install_package_dict or force_add: + # Schedule for a delayed removal: + self._to_remove_package_set.add(package_id) + else: + if package_id in self._to_install_package_dict: + # Remove from the delayed installation list if present + del self._to_install_package_dict[package_id] self._saveManagementData() self.installedPackagesChanged.emit() + ## Is the package an user installed package? + def isUserInstalledPackage(self, package_id: str): + return package_id in self._installed_package_dict + # Removes everything associated with the given package ID. def _purgePackage(self, package_id: str) -> None: # Iterate through all directories in the data storage directory and look for sub-directories that belong to @@ -302,27 +310,15 @@ class CuraPackageManager(QObject): if not os.path.exists(src_dir_path): continue - - # Need to rename the container files so they don't get ID conflicts - to_rename_files = sub_dir_name not in ("plugins",) - self.__installPackageFiles(package_id, src_dir_path, dst_dir_path, need_to_rename_files= to_rename_files) + self.__installPackageFiles(package_id, src_dir_path, dst_dir_path) # Remove the file os.remove(filename) - def __installPackageFiles(self, package_id: str, src_dir: str, dst_dir: str, need_to_rename_files: bool = True) -> None: + def __installPackageFiles(self, package_id: str, src_dir: str, dst_dir: str) -> None: + Logger.log("i", "Moving package {package_id} from {src_dir} to {dst_dir}".format(package_id=package_id, src_dir=src_dir, dst_dir=dst_dir)) shutil.move(src_dir, dst_dir) - # Rename files if needed - if not need_to_rename_files: - return - for root, _, file_names in os.walk(dst_dir): - for filename in file_names: - new_filename = self.PREFIX_PLACE_HOLDER + package_id + "-" + filename - old_file_path = os.path.join(root, filename) - new_file_path = os.path.join(root, new_filename) - os.rename(old_file_path, new_file_path) - # Gets package information from the given file. def getPackageInfo(self, filename: str) -> Dict[str, Any]: with zipfile.ZipFile(filename) as archive: @@ -342,13 +338,7 @@ class CuraPackageManager(QObject): with zipfile.ZipFile(filename) as archive: # Go through all the files and use the first successful read as the result for file_info in archive.infolist(): - is_dir = lambda file_info: file_info.filename.endswith('/') - if is_dir or not file_info.filename.startswith("files/"): - continue - - filename_parts = os.path.basename(file_info.filename.lower()).split(".") - stripped_filename = filename_parts[0] - if stripped_filename in ("license", "licence"): + if file_info.filename.endswith("LICENSE"): Logger.log("d", "Found potential license file '%s'", file_info.filename) try: with archive.open(file_info.filename, "r") as f: diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 719795ac45..ad3c7f165f 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -113,8 +113,6 @@ class MaterialManager(QObject): grouped_by_type_dict = dict() material_types_without_fallback = set() for root_material_id, material_node in self._material_group_map.items(): - if not self._container_registry.isReadOnly(root_material_id): - continue material_type = material_node.root_material_node.metadata["material"] if material_type not in grouped_by_type_dict: grouped_by_type_dict[material_type] = {"generic": None, @@ -127,9 +125,15 @@ class MaterialManager(QObject): diameter = material_node.root_material_node.metadata.get("approximate_diameter") if diameter != self._default_approximate_diameter_for_quality_search: to_add = False # don't add if it's not the default diameter + if to_add: - grouped_by_type_dict[material_type] = material_node.root_material_node.metadata - material_types_without_fallback.remove(material_type) + # Checking this first allow us to differentiate between not read only materials: + # - if it's in the list, it means that is a new material without fallback + # - if it is not, then it is a custom material with a fallback material (parent) + if material_type in material_types_without_fallback: + grouped_by_type_dict[material_type] = material_node.root_material_node.metadata + material_types_without_fallback.remove(material_type) + # Remove the materials that have no fallback materials for material_type in material_types_without_fallback: del grouped_by_type_dict[material_type] @@ -147,9 +151,6 @@ class MaterialManager(QObject): material_group_dict = dict() keys_to_fetch = ("name", "material", "brand", "color") for root_material_id, machine_node in self._material_group_map.items(): - if not self._container_registry.isReadOnly(root_material_id): - continue - root_material_metadata = machine_node.root_material_node.metadata key_data = [] @@ -157,8 +158,13 @@ class MaterialManager(QObject): key_data.append(root_material_metadata.get(key)) key_data = tuple(key_data) + # If the key_data doesn't exist, no matter if the material is read only... if key_data not in material_group_dict: material_group_dict[key_data] = dict() + else: + # ...but if key_data exists, we just overrite it if the material is read only, otherwise we skip it + if not machine_node.is_read_only: + continue approximate_diameter = root_material_metadata.get("approximate_diameter") material_group_dict[key_data][approximate_diameter] = root_material_metadata["id"] diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 650f5fd880..917085badc 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -345,7 +345,7 @@ class PrintInformation(QObject): except: Logger.log("w", "Unsupported Mime Type Database file extension") - if data is not None: + if data is not None and check_name is not None: self._base_name = data else: self._base_name = '' diff --git a/plugins/Toolbox/resources/images/loading.gif b/plugins/Toolbox/resources/images/loading.gif new file mode 100644 index 0000000000..43cc1ed6d7 Binary files /dev/null and b/plugins/Toolbox/resources/images/loading.gif differ diff --git a/plugins/Toolbox/resources/images/loading.svg b/plugins/Toolbox/resources/images/loading.svg new file mode 100644 index 0000000000..1ceb4a8d7f --- /dev/null +++ b/plugins/Toolbox/resources/images/loading.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml index 80d50616e8..da53fc94af 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml @@ -20,7 +20,7 @@ Item { left: parent.left right: controls.left - rightMargin: UM.Theme.getSize("default_margin").width + rightMargin: UM.Theme.getSize("default_margin").width * 2 + UM.Theme.getSize("toolbox_loader").width top: parent.top } Label @@ -53,60 +53,28 @@ Item anchors.top: tile.top width: childrenRect.width height: childrenRect.height - Button + + ToolboxProgressButton { id: installButton - text: + active: toolbox.isDownloading && toolbox.activePackage == model + complete: tile.installed + readyAction: function() { - if (installed) - { - return catalog.i18nc("@action:button", "Installed") - } - else - { - if (toolbox.isDownloading && toolbox.activePackage == model) - { - return catalog.i18nc("@action:button", "Cancel") - } - else - { - return catalog.i18nc("@action:button", "Install") - } - } + toolbox.activePackage = model + toolbox.startDownload(model.download_url) } - enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) //Don't allow installing while another download is running. + activeAction: function() + { + toolbox.cancelDownload() + } + completeAction: function() + { + toolbox.viewCategory = "installed" + } + // Don't allow installing while another download is running + enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 - - property alias installed: tile.installed - style: UM.Theme.styles.toolbox_action_button - onClicked: - { - if (installed) - { - toolbox.viewCategory = "installed" - } - else - { - // if ( toolbox.isDownloading && toolbox.activePackage == model ) - if ( toolbox.isDownloading ) - { - toolbox.cancelDownload(); - } - else - { - toolbox.activePackage = model - // toolbox.activePackage = model; - if ( model.can_upgrade ) - { - // toolbox.downloadAndInstallPlugin( model.update_url ); - } - else - { - toolbox.startDownload( model.download_url ); - } - } - } - } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 6004832a57..435319b5e9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -90,6 +90,16 @@ Item color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") linkColor: UM.Theme.getColor("text_link") } + + Label + { + text: model.version + width: parent.width + height: UM.Theme.getSize("toolbox_property_label").height + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } } ToolboxInstalledTileActions { @@ -102,4 +112,4 @@ Item onMetadataChanged: canUpdate = toolbox.canUpdate(model.id) } } -} \ No newline at end of file +} diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 1921bcb58e..8bdec4da5f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -11,56 +11,28 @@ Column width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height - Item + ToolboxProgressButton { - width: parent.width - height: childrenRect.height + id: updateButton + active: toolbox.isDownloading && toolbox.activePackage == model + readyLabel: catalog.i18nc("@action:button", "Update") + activeLabel: catalog.i18nc("@action:button", "Updating") + completeLabel: catalog.i18nc("@action:button", "Updated") + readyAction: function() + { + toolbox.activePackage = model + toolbox.update(model.id) + } + activeAction: function() + { + toolbox.cancelDownload() + } + // Don't allow installing while another download is running + enabled: !(toolbox.isDownloading && toolbox.activePackage != model) + opacity: enabled ? 1.0 : 0.5 visible: canUpdate - Button - { - id: updateButton - text: catalog.i18nc("@action:button", "Update") - style: ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_action_button").width - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary") - } - label: Label - { - text: control.text - color: control.hovered ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_text_hover") - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font: UM.Theme.getFont("default_bold") - } - } - onClicked: toolbox.update(model.id) - } - ProgressBar - { - id: progressbar - width: parent.width - value: toolbox.isDownloading ? toolbox.downloadProgress : 0 - visible: toolbox.isDownloading - style: ProgressBarStyle - { - background: Rectangle - { - color: "transparent" - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - } - progress: Rectangle - { - // TODO Define a good color that fits the purpuse - color: "blue" - opacity: 0.5 - } - } - } } + Button { id: removeButton @@ -90,4 +62,4 @@ Column } onClicked: toolbox.uninstall(model.id) } -} \ No newline at end of file +} diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml new file mode 100644 index 0000000000..a977ef999b --- /dev/null +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -0,0 +1,125 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Toolbox is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import UM 1.1 as UM + + +Item +{ + id: base + + property var active: false + property var complete: false + + property var readyLabel: catalog.i18nc("@action:button", "Install") + property var activeLabel: catalog.i18nc("@action:button", "Cancel") + property var completeLabel: catalog.i18nc("@action:button", "Installed") + + property var readyAction: null // Action when button is ready and clicked (likely install) + property var activeAction: null // Action when button is active and clicked (likely cancel) + property var completeAction: null // Action when button is complete and clicked (likely go to installed) + + width: UM.Theme.getSize("toolbox_action_button").width + height: UM.Theme.getSize("toolbox_action_button").height + + Button + { + id: button + text: + { + if (complete) + { + return completeLabel + } + else if (active) + { + return activeLabel + } + else + { + return readyLabel + } + } + onClicked: + { + if (complete) + { + return completeAction() + } + else if (active) + { + return activeAction() + } + else + { + return readyAction() + } + } + style: ButtonStyle + { + background: Rectangle + { + implicitWidth: UM.Theme.getSize("toolbox_action_button").width + implicitHeight: UM.Theme.getSize("toolbox_action_button").height + color: + { + if (base.complete) + { + return UM.Theme.getColor("action_button_disabled") + } + else + { + if (control.hovered) + { + return UM.Theme.getColor("primary_hover") + } + else + { + return UM.Theme.getColor("primary") + } + } + } + } + label: Label + { + text: control.text + color: + { + if (base.complete) + { + return UM.Theme.getColor("action_button_disabled_text") + } + else + { + if (control.hovered) + { + return UM.Theme.getColor("button_text_hover") + } + else + { + return UM.Theme.getColor("button_text") + } + } + } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font: UM.Theme.getFont("default_bold") + } + } + } + + AnimatedImage + { + id: loader + visible: active + source: "../images/loading.gif" + width: UM.Theme.getSize("toolbox_loader").width + height: UM.Theme.getSize("toolbox_loader").height + anchors.right: button.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: button.verticalCenter + } +} diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 3ee6787cf4..622198666d 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -34,7 +34,7 @@ class Toolbox(QObject, Extension): self._plugin_registry = Application.getInstance().getPluginRegistry() self._packages_version = self._getPackagesVersion() self._api_version = 1 - self._api_url = "https://api-staging.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version) + self._api_url = "https://api.ultimaker.com/cura-packages/v{api_version}/cura/v{package_version}".format( api_version = self._api_version, package_version = self._packages_version) # Network: self._get_packages_request = None @@ -61,6 +61,7 @@ class Toolbox(QObject, Extension): "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)) } + self._to_update = [] # Package_ids that are waiting to be updated # Data: self._metadata = { @@ -216,13 +217,36 @@ class Toolbox(QObject, Extension): @pyqtSlot(str) def uninstall(self, plugin_id: str) -> None: - self._package_manager.removePackage(plugin_id) + self._package_manager.removePackage(plugin_id, force_add = True) self.installChanged.emit() self._updateInstalledModels() self.metadataChanged.emit() self._restart_required = True self.restartRequiredChanged.emit() + ## Actual update packages that are in self._to_update + def _update(self) -> None: + if self._to_update: + plugin_id = self._to_update.pop(0) + remote_package = self.getRemotePackage(plugin_id) + if remote_package: + download_url = remote_package["download_url"] + Logger.log("d", "Updating package [%s]..." % plugin_id) + if self._package_manager.isUserInstalledPackage(plugin_id): + self.uninstall(plugin_id) + self.startDownload(download_url) + else: + Logger.log("e", "Could not update package [%s] because there is no remote package info available.", plugin_id) + + if self._to_update: + self._application.callLater(self._update) + + ## Update a plugin by plugin_id + @pyqtSlot(str) + def update(self, plugin_id: str) -> None: + self._to_update.append(plugin_id) + self._application.callLater(self._update) + @pyqtSlot(str) def enable(self, plugin_id: str) -> None: self._plugin_registry.enablePlugin(plugin_id) @@ -251,6 +275,15 @@ class Toolbox(QObject, Extension): def restart(self): CuraApplication.getInstance().windowClosed() + def getRemotePackage(self, package_id: str) -> Optional[Dict]: + # TODO: make the lookup in a dict, not a loop. canUpdate is called for every item. + remote_package = None + for package in self._metadata["packages"]: + if package["package_id"] == package_id: + remote_package = package + break + return remote_package + # Checks # -------------------------------------------------------------------------- @pyqtSlot(str, result = bool) @@ -259,16 +292,13 @@ class Toolbox(QObject, Extension): if local_package is None: return False - remote_package = None - for package in self._metadata["packages"]: - if package["package_id"] == package_id: - remote_package = package + remote_package = self.getRemotePackage(package_id) if remote_package is None: return False - local_version = local_package["package_version"] - remote_version = remote_package["package_version"] - return Version(remote_version) > Version(local_version) + local_version = Version(local_package["package_version"]) + remote_version = Version(remote_package["package_version"]) + return remote_version > local_version @pyqtSlot(str, result = bool) def isInstalled(self, package_id: str) -> bool: @@ -321,8 +351,8 @@ class Toolbox(QObject, Extension): def resetDownload(self) -> None: if self._download_reply: - self._download_reply.abort() self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) + self._download_reply.abort() self._download_reply = None self._download_request = None self.setDownloadProgress(0) diff --git a/resources/packages.json b/resources/bundled_packages.json similarity index 100% rename from resources/packages.json rename to resources/bundled_packages.json diff --git a/resources/definitions/101Hero.def.json b/resources/definitions/101Hero.def.json index 620fcfb519..9a371106bd 100644 --- a/resources/definitions/101Hero.def.json +++ b/resources/definitions/101Hero.def.json @@ -8,7 +8,8 @@ "manufacturer": "101Hero", "file_formats": "text/x-gcode", "platform": "101hero-platform.stl", - "supports_usb_connection": true + "supports_usb_connection": true, + "preferred_quality_type": "draft" }, "overrides": { @@ -19,7 +20,6 @@ "machine_depth": { "default_value": 149.86 }, "machine_height": { "default_value": 99.822 }, "machine_center_is_zero": { "default_value": true }, - "layer_height": { "default_value": 0.2 }, "machine_nozzle_size": { "default_value": 0.4 }, "material_diameter": { "default_value": 1.75 }, "machine_head_with_fans_polygon": { diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 7e726c5c60..5d7eed5c6d 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3703,6 +3703,20 @@ "settable_per_mesh": false, "settable_per_extruder": true }, + "support_wall_count": + { + "label": "Support Wall Line Count", + "description": "The number of walls with which to surround support infill. Adding a wall can make support print more reliably and can support overhangs better, but increases print time and material used.", + "default_value": 1, + "minimum_value": "0", + "minimum_value_warning": "1 if support_pattern == 'concentric' else 0", + "maximum_value_warning": "3", + "type": "int", + "value": "1 if (support_pattern == 'grid' or support_pattern == 'triangles' or support_pattern == 'concentric') else 0", + "enabled": "support_enable", + "limit_to_extruder": "support_infill_extruder_nr", + "settable_per_mesh": true + }, "zig_zaggify_support": { "label": "Connect Support Lines", diff --git a/resources/definitions/maker_starter.def.json b/resources/definitions/maker_starter.def.json index e7e6cb5dcd..de0267dc8e 100644 --- a/resources/definitions/maker_starter.def.json +++ b/resources/definitions/maker_starter.def.json @@ -8,7 +8,8 @@ "manufacturer": "3DMaker", "file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj", "icon": "icon_ultimaker2.png", - "platform": "makerstarter_platform.stl" + "platform": "makerstarter_platform.stl", + "preferred_quality_type": "draft" }, "overrides": { @@ -49,9 +50,6 @@ "machine_nozzle_expansion_angle": { "default_value": 45 }, - "layer_height": { - "default_value": 0.2 - }, "layer_height_0": { "default_value": 0.2 }, @@ -167,4 +165,4 @@ "default_value": 2 } } -} \ No newline at end of file +} diff --git a/resources/i18n/ko_KR/cura.po b/resources/i18n/ko_KR/cura.po index 4097741cb0..d0a18dd3a5 100644 --- a/resources/i18n/ko_KR/cura.po +++ b/resources/i18n/ko_KR/cura.po @@ -675,7 +675,7 @@ msgstr "G 코드 수정" #: /home/ruben/Projects/Cura/plugins/SupportEraser/__init__.py:12 msgctxt "@label" msgid "Support Blocker" -msgstr "지지대 차단기" +msgstr "서포트 차단기" #: /home/ruben/Projects/Cura/plugins/SupportEraser/__init__.py:13 msgctxt "@info:tooltip" @@ -971,7 +971,7 @@ msgstr "펌웨어 업그레이드" #: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py:14 msgctxt "@action" msgid "Checkup" -msgstr "대조" +msgstr "검사" #: /home/ruben/Projects/Cura/plugins/UltimakerMachineActions/BedLevelMachineAction.py:21 msgctxt "@action" @@ -991,7 +991,7 @@ msgstr "내벽" #: /home/ruben/Projects/Cura/cura/PrintInformation.py:98 msgctxt "@tooltip" msgid "Skin" -msgstr "외판" +msgstr "스킨" #: /home/ruben/Projects/Cura/cura/PrintInformation.py:99 msgctxt "@tooltip" @@ -2348,7 +2348,7 @@ msgstr "밀리미터 단위의 빌드 플레이트에서 기저부 높이." #: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:61 msgctxt "@action:label" msgid "Base (mm)" -msgstr "바다 (mm)" +msgstr "바닥 (mm)" #: /home/ruben/Projects/Cura/plugins/ImageReader/ConfigUI.qml:79 msgctxt "@info:tooltip" @@ -2420,7 +2420,7 @@ msgstr "서포터로 프린팅" #: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:84 msgctxt "@label" msgid "Don't support overlap with other models" -msgstr "다른 모델과 오버랩되도록 지지하지 않음" +msgstr "다른 모델과 오버랩되도록 지원하지 않음" #: /home/ruben/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:92 msgctxt "@label" @@ -3631,7 +3631,7 @@ msgstr "버전: %1" #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:56 msgctxt "@label" msgid "End-to-end solution for fused filament 3D printing." -msgstr "3D 인쇄를 위해 필라멘트를 한줄로 용." +msgstr "3D 프린팅을 위한 엔드 투 엔트 솔루션." #: /home/ruben/Projects/Cura/resources/qml/AboutDialog.qml:69 msgctxt "@info:credit" diff --git a/resources/i18n/ko_KR/fdmprinter.def.json.po b/resources/i18n/ko_KR/fdmprinter.def.json.po index e2bf9e52bd..12f7ae306c 100644 --- a/resources/i18n/ko_KR/fdmprinter.def.json.po +++ b/resources/i18n/ko_KR/fdmprinter.def.json.po @@ -433,7 +433,7 @@ msgstr "Repetier" #: fdmprinter.def.json msgctxt "machine_firmware_retract label" msgid "Firmware Retraction" -msgstr "펌웨어 제거" +msgstr "펌웨어 리트렉션" #: fdmprinter.def.json msgctxt "machine_firmware_retract description" @@ -521,7 +521,7 @@ msgstr "노즐의 내경. 비표준 노즐 크기를 사용할 때 이 설정을 #: fdmprinter.def.json msgctxt "machine_use_extruder_offset_to_offset_coords label" msgid "Offset With Extruder" -msgstr "압출기로 오프셋" +msgstr "익스트루더로 오프셋" #: fdmprinter.def.json msgctxt "machine_use_extruder_offset_to_offset_coords description" @@ -531,7 +531,7 @@ msgstr "익스트루더 오프셋을 좌표계에 적용하십시오." #: fdmprinter.def.json msgctxt "extruder_prime_pos_z label" msgid "Extruder Prime Z Position" -msgstr "압출기 프라임 Z 포지션" +msgstr "익스트루더 프라임 Z 포지션" #: fdmprinter.def.json msgctxt "extruder_prime_pos_z description" @@ -543,7 +543,7 @@ msgstr "프린팅가 시작될 때 노즐 위치의 Z 좌표입니다." #: fdmprinter.def.json msgctxt "extruder_prime_pos_abs label" msgid "Absolute Extruder Prime Position" -msgstr "독립 압출 기 프라임 포지션" +msgstr "독립 익스트루더 프라임 포지션" #: fdmprinter.def.json msgctxt "extruder_prime_pos_abs description" @@ -667,7 +667,7 @@ msgstr "Z 방향 모터의 기본 Jerk." #: fdmprinter.def.json msgctxt "machine_max_jerk_e label" msgid "Default Filament Jerk" -msgstr "기본 Filament Jerk" +msgstr "기본 필라멘트 Jerk" #: fdmprinter.def.json msgctxt "machine_max_jerk_e description" @@ -1868,7 +1868,7 @@ msgstr "내부채움 패턴이 Y축을 따라 이 거리만큼 이동합니다." #: fdmprinter.def.json msgctxt "sub_div_rad_add label" msgid "Cubic Subdivision Shell" -msgstr "입방 세분 내관" +msgstr "입방 세분 쉘" #: fdmprinter.def.json msgctxt "sub_div_rad_add description" @@ -2339,7 +2339,7 @@ msgctxt "material_flow description" msgid "" "Flow compensation: the amount of material extruded is multiplied by this " "value." -msgstr "유량 보상: 압출 된 재료의 양에 이 값을 곱합니다." +msgstr "압출량 보상: 압출 된 재료의 양에 이 값을 곱합니다." #: fdmprinter.def.json msgctxt "material_flow_layer_0 label" @@ -2420,7 +2420,7 @@ msgstr "리트렉션 이동 중에 필라멘트가 프라이밍되는 속도입 #: fdmprinter.def.json msgctxt "retraction_extra_prime_amount label" msgid "Retraction Extra Prime Amount" -msgstr "후퇴 Extra 초기 속도" +msgstr "추가적인 리트렉션 정도" #: fdmprinter.def.json msgctxt "retraction_extra_prime_amount description" @@ -2433,7 +2433,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "retraction_min_travel label" msgid "Retraction Minimum Travel" -msgstr "리트렉션 최소 움직임" +msgstr "리트렉션 최소 이동" #: fdmprinter.def.json msgctxt "retraction_min_travel description" @@ -2724,7 +2724,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "speed_travel label" msgid "Travel Speed" -msgstr "움직임 속도" +msgstr "이동 속도" #: fdmprinter.def.json msgctxt "speed_travel description" @@ -2841,7 +2841,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "speed_equalize_flow_max label" msgid "Maximum Speed for Flow Equalization" -msgstr "유량 균등화를위한 최대 속도" +msgstr "압출량 균등화를위한 최대 속도" #: fdmprinter.def.json msgctxt "speed_equalize_flow_max description" @@ -3008,7 +3008,7 @@ msgstr "프라임 타워가 프린팅되는 가속도." #: fdmprinter.def.json msgctxt "acceleration_travel label" msgid "Travel Acceleration" -msgstr "움직임 가속" +msgstr "이동 가속" #: fdmprinter.def.json msgctxt "acceleration_travel description" @@ -3038,7 +3038,7 @@ msgstr "초기 레이어 프린팅 중 가속도." #: fdmprinter.def.json msgctxt "acceleration_travel_layer_0 label" msgid "Initial Layer Travel Acceleration" -msgstr "초기 레이어 움직임 가속도" +msgstr "초기 레이어 이동 가속도" #: fdmprinter.def.json msgctxt "acceleration_travel_layer_0 description" @@ -3229,7 +3229,7 @@ msgstr "프라임 타워가 프린팅되는 최대 순간 속도 변화." #: fdmprinter.def.json msgctxt "jerk_travel label" msgid "Travel Jerk" -msgstr "움직임 Jerk" +msgstr "이동 Jerk" #: fdmprinter.def.json msgctxt "jerk_travel description" @@ -3262,7 +3262,7 @@ msgstr "초기 층의 프린팅 중 최대 순간 속도 변화." #: fdmprinter.def.json msgctxt "jerk_travel_layer_0 label" msgid "Initial Layer Travel Jerk" -msgstr "초기 레이어 움직임 Jerk" +msgstr "초기 레이어 이동 Jerk" #: fdmprinter.def.json msgctxt "jerk_travel_layer_0 description" @@ -3284,12 +3284,12 @@ msgstr "스커트와 브림이 프린팅되는 최대 순간 속도 변화." #: fdmprinter.def.json msgctxt "travel label" msgid "Travel" -msgstr "움직임" +msgstr "이동" #: fdmprinter.def.json msgctxt "travel description" msgid "travel" -msgstr "움직임" +msgstr "이동" #: fdmprinter.def.json msgctxt "retraction_combing label" @@ -3320,7 +3320,7 @@ msgstr "모두" #: fdmprinter.def.json msgctxt "retraction_combing option noskin" msgid "No Skin" -msgstr "피부가 없다" +msgstr "스킨이 없음" #: fdmprinter.def.json msgctxt "travel_retract_before_outer_wall label" @@ -3349,7 +3349,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "travel_avoid_distance label" msgid "Travel Avoid Distance" -msgstr "움직일 때 피하기 거리" +msgstr "이동중 피하는 거리" #: fdmprinter.def.json msgctxt "travel_avoid_distance description" @@ -3826,7 +3826,7 @@ msgstr "십자" #: fdmprinter.def.json msgctxt "zig_zaggify_support label" msgid "Connect Support Lines" -msgstr "지지대 선 연결" +msgstr "서포트 선 연결" #: fdmprinter.def.json msgctxt "zig_zaggify_support description" @@ -3966,7 +3966,7 @@ msgstr "X/Y 방향에서 오버행으로부터 서포트까지의 거리. " #: fdmprinter.def.json msgctxt "support_bottom_stair_step_height label" msgid "Support Stair Step Height" -msgstr "계단 Step Height 지지대" +msgstr "계단 Step Height 서포트" #: fdmprinter.def.json msgctxt "support_bottom_stair_step_height description" @@ -3998,7 +3998,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "support_join_distance label" msgid "Support Join Distance" -msgstr "지지대 Join 거리" +msgstr "서포트 Join 거리" #: fdmprinter.def.json msgctxt "support_join_distance description" @@ -4013,7 +4013,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "support_offset label" msgid "Support Horizontal Expansion" -msgstr "수평 확장 지지대" +msgstr "수평 확장 서포트" #: fdmprinter.def.json msgctxt "support_offset description" @@ -5879,7 +5879,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "support_zag_skip_count label" msgid "Support Chunk Line Count" -msgstr "Chunk 라인 카운트 지지대" +msgstr "Chunk 라인 카운트 서포트" #: fdmprinter.def.json msgctxt "support_zag_skip_count description" @@ -6046,7 +6046,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "skin_alternate_rotation label" msgid "Alternate Skin Rotation" -msgstr "대체 피부 회전" +msgstr "대체 스킨 회전" #: fdmprinter.def.json msgctxt "skin_alternate_rotation description" @@ -6429,7 +6429,7 @@ msgid "" "Flow compensation: the amount of material extruded is multiplied by this " "value. Only applies to Wire Printing." msgstr "" -"유량 보상 : 압출 된 재료의 양에 이 값을 곱합니다. 와이어 프린팅에만 적용됩니" +"압출량 보상 : 압출 된 재료의 양에 이 값을 곱합니다. 와이어 프린팅에만 적용됩니" "다." #: fdmprinter.def.json @@ -6440,7 +6440,7 @@ msgstr "WP 연결 흐름" #: fdmprinter.def.json msgctxt "wireframe_flow_connection description" msgid "Flow compensation when going up or down. Only applies to Wire Printing." -msgstr "위 또는 아래로 이동할 때 유량 보정. 와이어 프린팅에만 적용됩니다." +msgstr "위 또는 아래로 이동할 때 압출량 보정. 와이어 프린팅에만 적용됩니다." #: fdmprinter.def.json msgctxt "wireframe_flow_flat label" @@ -6451,7 +6451,7 @@ msgstr "WP 플랫 플로우" msgctxt "wireframe_flow_flat description" msgid "" "Flow compensation when printing flat lines. Only applies to Wire Printing." -msgstr "평평한 선을 프린팅 할 때 유량 보정. 와이어 프린팅에만 적용됩니다." +msgstr "평평한 선을 프린팅 할 때 압출량 보정. 와이어 프린팅에만 적용됩니다." #: fdmprinter.def.json msgctxt "wireframe_top_delay label" @@ -6496,7 +6496,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "wireframe_up_half_speed label" msgid "WP Ease Upward" -msgstr "WP는 쉽게 상향 조정" +msgstr "WP 상향 조정" #: fdmprinter.def.json msgctxt "wireframe_up_half_speed description" @@ -6761,7 +6761,7 @@ msgstr "" #: fdmprinter.def.json msgctxt "bridge_wall_max_overhang label" msgid "Bridge Wall Max Overhang" -msgstr "브리지 벽 최대 돌출" +msgstr "브리지 벽 최대 오버행" #: fdmprinter.def.json msgctxt "bridge_wall_max_overhang description" @@ -6808,7 +6808,7 @@ msgstr "브릿지 벽이 프린팅되는 속도." #: fdmprinter.def.json msgctxt "bridge_wall_material_flow label" msgid "Bridge Wall Flow" -msgstr "브리지 벽 유량" +msgstr "브리지 벽 압출량" #: fdmprinter.def.json msgctxt "bridge_wall_material_flow description" @@ -6831,7 +6831,7 @@ msgstr "브릿지 스킨 층이 프린팅되는 속도." #: fdmprinter.def.json msgctxt "bridge_skin_material_flow label" msgid "Bridge Skin Flow" -msgstr "브리지 스킨 유량" +msgstr "브리지 스킨 압출량" #: fdmprinter.def.json msgctxt "bridge_skin_material_flow description" @@ -6892,7 +6892,7 @@ msgstr "두번째 브릿지 스킨 레이어를 인쇄 할 때 사용할 인쇄 #: fdmprinter.def.json msgctxt "bridge_skin_material_flow_2 label" msgid "Bridge Second Skin Flow" -msgstr "브리지 두 번째 스킨 유량" +msgstr "브리지 두 번째 스킨 압출량" #: fdmprinter.def.json msgctxt "bridge_skin_material_flow_2 description" @@ -6939,7 +6939,7 @@ msgstr "세번째 브릿지 스킨 레이어를 인쇄 할 때 사용할 인쇄 #: fdmprinter.def.json msgctxt "bridge_skin_material_flow_3 label" msgid "Bridge Third Skin Flow" -msgstr "브리지 세 번째 스킨 유량" +msgstr "브리지 세 번째 스킨 압출량" #: fdmprinter.def.json msgctxt "bridge_skin_material_flow_3 description" diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 272569daea..9152b463f0 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -179,6 +179,15 @@ Item height: visible ? UM.Theme.getSize("setting_control").height : 0 Behavior on height { NumberAnimation { duration: 100 } } + Timer + { + id: settingsSearchTimer + onTriggered: filter.editingFinished() + interval: 500 + running: false + repeat: false + } + TextField { id: filter; @@ -201,6 +210,11 @@ Item property bool lastFindingSettings: false onTextChanged: + { + settingsSearchTimer.restart() + } + + onEditingFinished: { definitionsModel.filter = {"i18n_label": "*" + text}; findingSettings = (text.length > 0); diff --git a/resources/setting_visibility/expert.cfg b/resources/setting_visibility/expert.cfg index d6989f8b26..db271cc985 100644 --- a/resources/setting_visibility/expert.cfg +++ b/resources/setting_visibility/expert.cfg @@ -220,6 +220,7 @@ support_bottom_extruder_nr support_type support_angle support_pattern +support_wall_count zig_zaggify_support support_connect_zigzags support_infill_rate diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f2309fb4a9..4d75ecc1f1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -458,6 +458,7 @@ "toolbox_header": [1.0, 4.0], "toolbox_progress_bar": [8.0, 0.5], "toolbox_chart_row": [1.0, 2.0], - "toolbox_action_button": [8.0, 2.5] + "toolbox_action_button": [8.0, 2.5], + "toolbox_loader": [2.0, 2.0] } } diff --git a/resources/variants/ultimaker_s5_aluminum.inst.cfg b/resources/variants/ultimaker_s5_aluminum.inst.cfg new file mode 100644 index 0000000000..ea349d05f5 --- /dev/null +++ b/resources/variants/ultimaker_s5_aluminum.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = Aluminum +version = 4 +definition = ultimaker_s5 + +[metadata] +setting_version = 4 +type = variant +hardware_type = buildplate + +[values] +material_bed_temperature = =default_material_bed_temperature + 10 +machine_buildplate_type = aluminum