diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9e016561d5..ab348ae471 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -220,7 +220,8 @@ class CuraApplication(QtApplication): "LocalFileOutputDevice", "TranslateTool", "FileLogger", - "XmlMaterialProfile" + "XmlMaterialProfile", + "PluginBrowser" ]) self._physics = None self._volume = None diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 7a48b1657e..09815da319 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -29,7 +29,8 @@ class CuraStackBuilder: return None machine_definition = definitions[0] - generated_name = registry.createUniqueName("machine", "", machine_definition.name, machine_definition.name) + + generated_name = registry.createUniqueName("machine", "", name, machine_definition.name) # Make sure the new name does not collide with any definition or (quality) profile # createUniqueName() only looks at other stacks, but not at definitions or quality profiles # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true @@ -44,13 +45,7 @@ class CuraStackBuilder: variant = "default", ) - # after creating a global stack can be set custom defined name - if name != generated_name: - name = registry.createUniqueName("machine", "", name, machine_definition.name) - if registry.findContainers(id = name): - name = registry.uniqueName(name) - - new_global_stack.setName(name) + new_global_stack.setName(generated_name) for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id): position = extruder_definition.getMetaDataEntry("position", None) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 6de4ddc998..7e185c8d9f 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -222,10 +222,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader): elif container_type == "definition_changes": definition_name = instance_container.getName() num_settings_overriden_by_definition_changes += len(instance_container._instances) + # Check if definition changes already exists. definition_changes = self._container_registry.findInstanceContainers(id = container_id) - containers_found_dict["definition_changes"] = True # Check if there is any difference the loaded settings from the project file and the settings in Cura. if definition_changes: + containers_found_dict["definition_changes"] = True + # Check if there really is a conflict by comparing the values if definition_changes[0] != instance_container: definition_changes_conflict = True elif container_type == "quality": @@ -357,7 +359,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # - new: create a new container # - override: override the existing container # - None: There is no conflict, which means containers with the same IDs may or may not be there already. - # If there is an existing container, there is no conflict between the them, and default to "override" + # If there is an existing container, there is no conflict between them, and default to "override" # If there is no existing container, default to "new" # # Default values @@ -600,7 +602,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if self._resolve_strategies["machine"] == "new": # The machine is going to get a spiffy new name, so ensure that the id's of user settings match. old_extruder_id = instance_container.getMetaDataEntry("extruder", None) - if old_extruder_id: + # Note that in case of a quality_changes extruder means the definition id of the extruder stack + # For the user settings, it means the actual extruder stack id it's assigned to. + if old_extruder_id and old_extruder_id in extruder_stack_id_map: new_extruder_id = extruder_stack_id_map[old_extruder_id] instance_container.setMetaDataEntry("extruder", new_extruder_id) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index d80fc8b8b5..fc46a3daec 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -233,13 +233,15 @@ class WorkspaceDialog(QObject): self._result["quality_changes"] = None if "definition_changes" in self._result and not self._has_definition_changes_conflict: self._result["definition_changes"] = None - - # If the machine needs to be re-created, the definition_changes should also be re-created. - if "machine" in self._result and self._result["machine"] == "new" and self._result["definition_changes"] is None: - self._result["definition_changes"] = "new" - if "material" in self._result and not self._has_material_conflict: self._result["material"] = None + + # If the machine needs to be re-created, the definition_changes should also be re-created. + # If the machine strategy is None, it means that there is no name conflict with existing ones. In this case + # new definitions changes are created + if "machine" in self._result and self._result["machine"] == "new" or self._result["machine"] is None and self._result["definition_changes"] is None: + self._result["definition_changes"] = "new" + return self._result def _createViewFromQML(self): diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index 7a3b52b99f..0037d4bc59 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -23,9 +23,9 @@ i18n_catalog = i18nCatalog("cura") class PluginBrowser(QObject, Extension): - def __init__(self, parent = None): + def __init__(self, parent=None): super().__init__(parent) - self.addMenuItem(i18n_catalog.i18nc("@menuitem", "Browse plugins"), self.browsePlugins) + self._api_version = 1 self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version @@ -92,6 +92,7 @@ class PluginBrowser(QObject, Extension): def isDownloading(self): return self._is_downloading + @pyqtSlot() def browsePlugins(self): self._createNetworkManager() self.requestPluginList() diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index 994f332d46..3ea48be33c 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -50,7 +50,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._api_prefix = api_prefix self._gcode = None - self._print_finished = True # _print_finsihed == False means we're halfway in a print + self._print_finished = True # _print_finished == False means we're halfway in a print + self._write_finished = True # _write_finished == False means we're currently sending a G-code file self._use_gzip = True # Should we use g-zip compression before sending the data? @@ -650,7 +651,16 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): # \param filter_by_machine Whether to filter MIME types by machine. This # is ignored. # \param kwargs Keyword arguments. - def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs): + def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs): + + # Check if we're already writing + if not self._write_finished: + self._error_message = Message( + i18n_catalog.i18nc("@info:status", + "Sending new jobs (temporarily) blocked, still sending the previous print job.")) + self._error_message.show() + return + if self._printer_state not in ["idle", ""]: self._error_message = Message( i18n_catalog.i18nc("@info:status", "Unable to start a new print job, printer is busy. Current printer status is %s.") % self._printer_state, @@ -750,6 +760,9 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): ) return + # Indicate we're starting a new write action, is set back to True in the startPrint() method + self._write_finished = False + self.startPrint() def _configurationMismatchMessageCallback(self, button): @@ -909,6 +922,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): ## Post request + data self._post_reply = self._manager.post(self._post_request, self._post_multi_part) self._post_reply.uploadProgress.connect(self._onUploadProgress) + self._post_reply.finished.connect(self._onUploadFinished) # used to unblock new write actions except IOError: self._progress_message.hide() @@ -1245,6 +1259,10 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._progress_message.setProgress(0) self._progress_message.hide() + ## Allow new write actions (uploads) again when uploading is finished. + def _onUploadFinished(self): + self._write_finished = True + ## Let the user decide if the hotends and/or material should be synced with the printer def materialHotendChangedMessage(self, callback): Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Sync with your printer"), diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 68a3baa768..cc27520a02 100755 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -61,6 +61,9 @@ Item property alias configureSettingVisibility: configureSettingVisibilityAction + property alias browsePlugins: browsePluginsAction + property alias configurePlugins: configurePluginsAction + UM.I18nCatalog{id: catalog; name:"cura"} Action @@ -362,4 +365,18 @@ Item text: catalog.i18nc("@action:menu", "Configure setting visibility..."); iconName: "configure" } + + Action + { + id: browsePluginsAction + text: catalog.i18nc("@action:menu", "Browse plugins...") + iconName: "plugins_browse" + } + + Action + { + id: configurePluginsAction + text: catalog.i18nc("@action:menu", "Installed plugins..."); + iconName: "plugins_configure" + } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9fecf23ca9..ca32a6eaab 100755 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -207,7 +207,7 @@ UM.MainWindow id: sub_menu title: model.name; visible: actions != null - enabled:actions != null + enabled: actions != null Instantiator { model: actions @@ -226,6 +226,15 @@ UM.MainWindow } } + Menu + { + id: plugin_menu + title: catalog.i18nc("@title:menu menubar:toplevel", "P&lugins") + + MenuItem { action: Cura.Actions.browsePlugins } + MenuItem { action: Cura.Actions.configurePlugins } + } + Menu { title: catalog.i18nc("@title:menu menubar:toplevel","P&references"); @@ -543,6 +552,30 @@ UM.MainWindow } } + // show the installed plugins page in the preferences dialog + Connections + { + target: Cura.Actions.configurePlugins + onTriggered: + { + preferences.visible = true + preferences.setPage(5) + } + } + + UM.ExtensionModel { + id: curaExtensions + } + + // show the plugin browser dialog + Connections + { + target: Cura.Actions.browsePlugins + onTriggered: { + curaExtensions.callExtensionMethod("Plugin Browser", "browsePlugins") + } + } + Timer { id: createProfileTimer diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index b613b1217e..7423fc2368 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -43,7 +43,7 @@ Item { } } - Text { + Label { id: statusLabel width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width anchors.top: parent.top diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 4fdeac752f..99948fc4fc 100755 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -114,7 +114,7 @@ Rectangle } } - Text { + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); anchors.left: parent.left diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index d20feca8d6..9269984a2a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -363,7 +363,7 @@ Column visible: !Cura.MachineManager.isCurrentSetupSupported } - Text { + Label { id: materialInfoLabel wrapMode: Text.WordWrap text: catalog.i18nc("@label", "Check material compatibility") diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index f8c3173c11..03d3e2cd10 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -154,7 +154,7 @@ Item } } - Text + Label { id: qualityRowTitle text: catalog.i18nc("@label", "Layer Height") @@ -171,11 +171,11 @@ Item { model: qualityModel - Text + Label { anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height / 2 + anchors.topMargin: parseInt(UM.Theme.getSize("sidebar_margin").height / 2) color: (Cura.MachineManager.activeMachine != null && Cura.ProfilesModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { @@ -194,13 +194,13 @@ Item // Make sure the text aligns correctly with each tick if (qualityModel.totalTicks == 0) { // If there is only one tick, align it centrally - return ((base.width * 0.55) - width) / 2 + return parseInt(((base.width * 0.55) - width) / 2) } else if (index == 0) { return (base.width * 0.55 / qualityModel.totalTicks) * index } else if (index == qualityModel.totalTicks) { return (base.width * 0.55 / qualityModel.totalTicks) * index - width } else { - return (base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2) + return parseInt((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2)) } } } @@ -310,7 +310,7 @@ Item } } - Text + Label { id: speedLabel anchors.top: speedSlider.bottom @@ -322,7 +322,7 @@ Item color: UM.Theme.getColor("text") } - Text + Label { anchors.bottom: speedLabel.bottom anchors.left: speedSlider.left @@ -333,7 +333,7 @@ Item horizontalAlignment: Text.AlignLeft } - Text + Label { anchors.bottom: speedLabel.bottom anchors.right: speedSlider.right @@ -360,7 +360,7 @@ Item width: UM.Theme.getSize("sidebar").width * .45 - UM.Theme.getSize("sidebar_margin").width - Text + Label { id: infillLabel text: catalog.i18nc("@label", "Infill") @@ -385,7 +385,7 @@ Item anchors.top: infillCellLeft.top anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - Text { + Label { id: selectedInfillRateText //anchors.top: parent.top @@ -482,7 +482,7 @@ Item anchors.right: parent.right anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height / 2 + anchors.topMargin: parseInt(UM.Theme.getSize("sidebar_margin").height / 2) // we loop over all density icons and only show the one that has the current density and steps Repeater @@ -533,7 +533,7 @@ Item property alias _hovered: enableGradualInfillMouseArea.containsMouse anchors.top: infillSlider.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height / 2 // closer to slider since it belongs to the same category + anchors.topMargin: parseInt(UM.Theme.getSize("sidebar_margin").height / 2) // closer to slider since it belongs to the same category anchors.left: infillCellRight.left style: UM.Theme.styles.checkbox @@ -563,10 +563,10 @@ Item } } - Text { + Label { id: gradualInfillLabel anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width / 2 + anchors.leftMargin: parseInt(UM.Theme.getSize("sidebar_margin").width / 2) text: catalog.i18nc("@label", "Enable gradual") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") @@ -621,13 +621,13 @@ Item // // Enable support // - Text + Label { id: enableSupportLabel visible: enableSupportCheckBox.visible anchors.top: infillCellRight.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height * 1.5 + anchors.topMargin: parseInt(UM.Theme.getSize("sidebar_margin").height * 1.5) anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width anchors.verticalCenter: enableSupportCheckBox.verticalCenter @@ -674,7 +674,7 @@ Item } } - Text + Label { id: supportExtruderLabel visible: supportExtruderCombobox.visible @@ -750,7 +750,7 @@ Item } - Text + Label { id: adhesionHelperLabel visible: adhesionCheckBox.visible @@ -836,12 +836,12 @@ Item { id: tipsCell anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height * 2 + anchors.topMargin: parseInt(UM.Theme.getSize("sidebar_margin").height * 2) anchors.left: parent.left width: parent.width height: tipsText.contentHeight * tipsText.lineCount - Text + Label { id: tipsText anchors.left: parent.left diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/SidebarTooltip.qml index 057bd63203..60d01dd6f6 100644 --- a/resources/qml/SidebarTooltip.qml +++ b/resources/qml/SidebarTooltip.qml @@ -43,7 +43,7 @@ UM.PointingRectangle { base.opacity = 0; } - Text { + Label { id: label; anchors { top: parent.top; diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 96ccd469b6..b41ea96846 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -17,10 +17,6 @@ "size": 1.15, "family": "Open Sans" }, - "default_little_big": { - "size": 1.17, - "family": "Open Sans" - }, "default_bold": { "size": 1.15, "bold": true,