From 6e4300b299fdbb5d8911b6b91215e74ee3967d6d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 8 Jul 2015 21:26:23 +0200 Subject: [PATCH 01/26] Try to load all plugins, not just plugins with certain metadata --- cura/CuraApplication.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4cd4831e47..00a36a1348 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -98,16 +98,12 @@ class CuraApplication(QtApplication): self._plugin_registry.addPluginLocation(os.path.join(QtApplication.getInstallPrefix(), "lib", "cura")) if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) + self._plugin_registry.loadPlugin("ConsoleLogger") - self._plugin_registry.loadPlugins({ "type": "logger"}) - self._plugin_registry.loadPlugins({ "type": "storage_device" }) - self._plugin_registry.loadPlugins({ "type": "view" }) - self._plugin_registry.loadPlugins({ "type": "mesh_reader" }) - self._plugin_registry.loadPlugins({ "type": "mesh_writer" }) - self._plugin_registry.loadPlugins({ "type": "tool" }) - self._plugin_registry.loadPlugins({ "type": "extension" }) + self._plugin_registry.loadPlugins() - self._plugin_registry.loadPlugin("CuraEngineBackend") + if self.getBackend() == None: + raise RuntimeError("Could not load the backend plugin!") def addCommandLineOptions(self, parser): parser.add_argument("file", nargs="*", help="Files to load after starting the application.") From baf4ea9523ac53b7141155bd3681f5baccdaff7e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 8 Jul 2015 21:26:54 +0200 Subject: [PATCH 02/26] Use the OutputDeviceModel for selecting output device Replaces the stuff in CuraApplication which really should not be there --- resources/qml/SaveButton.qml | 91 +++++++++++++----------------------- 1 file changed, 32 insertions(+), 59 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 4708995308..8f75c82ec3 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -6,46 +6,17 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.0 as UM +import UM 1.1 as UM Rectangle { id: base; - property Action saveAction; - property real progress: UM.Backend.progress; Behavior on progress { NumberAnimation { duration: 250; } } - property string currentDevice: "local_file" - property bool defaultOverride: false; - property bool defaultAmbiguous: false; - property variant printDuration: PrintInformation.currentPrintTime; property real printMaterialAmount: PrintInformation.materialAmount; - Connections { - target: Printer; - onOutputDevicesChanged: { - if(!base.defaultOverride) { - base.defaultAmbiguous = false; - var device = null; - for(var i in Printer.outputDevices) { - if(device == null) { - device = i; - } else if(Printer.outputDevices[i].priority > Printer.outputDevices[device].priority) { - device = i; - } else if(Printer.outputDevices[i].priority == Printer.outputDevices[device].priority) { - base.defaultAmbiguous = true; - } - } - - if(device != null) { - base.currentDevice = device; - } - } - } - } - Rectangle{ id: background implicitWidth: base.width; @@ -113,7 +84,7 @@ Rectangle { elide: mediumLengthDuration ? Text.ElideRight : Text.ElideNone visible: base.progress < 0.99 ? false : true //: Print material amount save button label - text: base.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(base.printMaterialAmount); + text: base.printMaterialAmount < 0 ? "" : qsTr("%1m of Material").arg(base.printMaterialAmount); } } Rectangle { @@ -134,29 +105,28 @@ Rectangle { anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height; anchors.left: parent.left anchors.leftMargin: UM.Theme.sizes.default_margin.width; - tooltip: '' + tooltip: devicesModel.currentDevice.description; enabled: progress >= 0.99; width: infoBox.width/6*4.5 height: UM.Theme.sizes.save_button_save_to_button.height + + text: devicesModel.currentDevice.short_description; + style: ButtonStyle { background: Rectangle { color: !control.enabled ? UM.Theme.colors.save_button_inactive : control.hovered ? UM.Theme.colors.save_button_active_hover : UM.Theme.colors.save_button_active; + Label { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter + anchors.centerIn: parent color: UM.Theme.colors.save_button_safe_to_text; font: UM.Theme.fonts.sidebar_save_to; - text: Printer.outputDevices[base.currentDevice].shortDescription; + text: control.text; } } + label: Item { } } - onClicked: - if(base.defaultAmbiguous) { - devicesMenu.popup(); - } else { - Printer.writeToOutputDevice(base.currentDevice); - } + onClicked: devicesModel.requestWriteToCurrentDevice() } Button { @@ -165,16 +135,20 @@ Rectangle { anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height anchors.right: parent.right anchors.rightMargin: UM.Theme.sizes.default_margin.width; - tooltip: '' + + tooltip: qsTr("Select the active output device"); width: infoBox.width/6*1.3 - UM.Theme.sizes.save_button_text_margin.height; height: UM.Theme.sizes.save_button_save_to_button.height + iconSource: UM.Theme.icons[devicesModel.currentDevice.icon_name]; + style: ButtonStyle { background: Rectangle { - color: UM.Theme.colors.save_button_background; - border.width: control.hovered ? UM.Theme.sizes.save_button_border.width : 0 - border.color: UM.Theme.colors.save_button_border + color: UM.Theme.colors.save_button_background; + border.width: control.hovered ? UM.Theme.sizes.save_button_border.width : 0 + border.color: UM.Theme.colors.save_button_border + Rectangle { id: deviceSelectionIcon color: UM.Theme.colors.save_button_background; @@ -183,14 +157,13 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter; width: parent.height - UM.Theme.sizes.save_button_text_margin.width ; height: parent.height - UM.Theme.sizes.save_button_text_margin.width; + UM.RecolorImage { - anchors.centerIn: parent; - width: parent.width; - height: parent.height; + anchors.fill: parent; sourceSize.width: width; sourceSize.height: height; color: UM.Theme.colors.save_button_active - source: UM.Theme.icons[Printer.outputDevices[base.currentDevice].icon]; + source: control.iconSource; } } Label { @@ -203,24 +176,20 @@ Rectangle { color: UM.Theme.colors.save_button_active; } } + label: Item { } } menu: Menu { id: devicesMenu; Instantiator { - model: Printer.outputDeviceNames; + model: devicesModel; MenuItem { - text: Printer.outputDevices[modelData].description; + text: model.description checkable: true; - checked: base.defaultAmbiguous ? false : modelData == base.currentDevice; + checked: model.id == devicesModel.currentDevice.id; exclusiveGroup: devicesMenuGroup; onTriggered: { - base.defaultOverride = true; - base.currentDevice = modelData; - if(base.defaultAmbiguous) { - base.defaultAmbiguous = false; - Printer.writeToOutputDevice(modelData); - } + devicesModel.setCurrentDevice(model.id); } } onObjectAdded: devicesMenu.insertItem(index, object) @@ -230,4 +199,8 @@ Rectangle { } } } -} \ No newline at end of file + + UM.OutputDevicesModel { + id: devicesModel; + } +} From c2e672591c7492531d5dfb140ea9336a2e4e5c78 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 8 Jul 2015 21:42:20 +0200 Subject: [PATCH 03/26] Remove the output_device related stuff from CuraApplication and fix the qml This is now properly handled by the Output Device API in Uranium --- cura/CuraApplication.py | 117 -------------------------------------- resources/qml/Cura.qml | 61 ++++++++++---------- resources/qml/Sidebar.qml | 1 - 3 files changed, 31 insertions(+), 148 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 00a36a1348..6ca6becabd 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -111,15 +111,6 @@ class CuraApplication(QtApplication): def run(self): self._i18n_catalog = i18nCatalog("cura"); - self.addOutputDevice("local_file", { - "id": "local_file", - "function": self._writeToLocalFile, - "description": self._i18n_catalog.i18nc("Save button tooltip", "Save to Disk"), - "shortDescription": self._i18n_catalog.i18nc("Save button tooltip", "Save to Disk"), - "icon": "save", - "priority": 0 - }) - self.showSplashMessage(self._i18n_catalog.i18nc("Splash screen message", "Setting up scene...")) controller = self.getController() @@ -159,8 +150,6 @@ class CuraApplication(QtApplication): self.setMainQml(Resources.getPath(Resources.QmlFilesLocation, "Cura.qml")) self.initializeEngine() - self.getStorageDevice("LocalFileStorage").removableDrivesChanged.connect(self._removableDrivesChanged) - if self.getMachines(): active_machine_pref = Preferences.getInstance().getValue("cura/active_machine") if active_machine_pref: @@ -173,7 +162,6 @@ class CuraApplication(QtApplication): else: self.requestAddPrinter.emit() - self._removableDrivesChanged() if self._engine.rootObjects: self.closeSplash() @@ -360,16 +348,6 @@ class CuraApplication(QtApplication): def expandedCategories(self): return Preferences.getInstance().getValue("cura/categories_expanded").split(";") - outputDevicesChanged = pyqtSignal() - - @pyqtProperty("QVariantMap", notify = outputDevicesChanged) - def outputDevices(self): - return self._output_devices - - @pyqtProperty("QStringList", notify = outputDevicesChanged) - def outputDeviceNames(self): - return self._output_devices.keys() - @pyqtSlot(str, result = "QVariant") def getSettingValue(self, key): if not self.getActiveMachine(): @@ -385,82 +363,6 @@ class CuraApplication(QtApplication): self.getActiveMachine().setSettingValueByKey(key, value) - ## Add an output device that can be written to. - # - # \param id \type{string} The identifier used to identify the device. - # \param device \type{StorageDevice} A dictionary of device information. - # It should contains the following: - # - function: A function to be called when trying to write to the device. Will be passed the device id as first parameter. - # - description: A translated string containing a description of what happens when writing to the device. - # - icon: The icon to use to represent the device. - # - priority: The priority of the device. The device with the highest priority will be used as the default device. - def addOutputDevice(self, id, device): - self._output_devices[id] = device - self.outputDevicesChanged.emit() - - ## Remove output device - # \param id \type{string} The identifier used to identify the device. - # \sa PrinterApplication::addOutputDevice() - def removeOutputDevice(self, id): - if id in self._output_devices: - del self._output_devices[id] - self.outputDevicesChanged.emit() - - @pyqtSlot(str) - def writeToOutputDevice(self, device): - self._output_devices[device]["function"](device) - - writeToLocalFileRequested = pyqtSignal() - - def _writeToLocalFile(self, device): - self.writeToLocalFileRequested.emit() - - def _writeToSD(self, device): - for node in DepthFirstIterator(self.getController().getScene().getRoot()): - if type(node) is not SceneNode or not node.getMeshData(): - continue - - try: - path = self.getStorageDevice("LocalFileStorage").getRemovableDrives()[device] - except KeyError: - Logger.log("e", "Tried to write to unknown SD card %s", device) - return - - filename = os.path.join(path, node.getName()[0:node.getName().rfind(".")] + ".gcode") - - message = Message(self._output_devices[device]["description"], 0, False, -1) - message.show() - - job = WriteMeshJob(filename, node.getMeshData()) - job._sdcard = device - job._message = message - job.start() - job.finished.connect(self._onWriteToSDFinished) - - return - - def _removableDrivesChanged(self): - drives = self.getStorageDevice("LocalFileStorage").getRemovableDrives() - for drive in drives: - if drive not in self._output_devices: - self.addOutputDevice(drive, { - "id": drive, - "function": self._writeToSD, - "description": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}").format(drive), - "shortDescription": self._i18n_catalog.i18nc("Save button tooltip. {0} is sd card name", "Save to SD Card {0}").format(""), - "icon": "save_sd", - "priority": 1 - }) - - drives_to_remove = [] - for device in self._output_devices: - if device not in drives: - if self._output_devices[device]["function"] == self._writeToSD: - drives_to_remove.append(device) - - for drive in drives_to_remove: - self.removeOutputDevice(drive) - def _onActiveMachineChanged(self): machine = self.getActiveMachine() if machine: @@ -486,25 +388,6 @@ class CuraApplication(QtApplication): else: self._platform.setPosition(Vector(0.0, 0.0, 0.0)) - def _onWriteToSDFinished(self, job): - message = Message(self._i18n_catalog.i18nc("Saved to SD message, {0} is sdcard, {1} is filename", "Saved to SD Card {0} as {1}").format(job._sdcard, job.getFileName())) - message.addAction( - "eject", - self._i18n_catalog.i18nc("Message action", "Eject"), - "eject", - self._i18n_catalog.i18nc("Message action tooltip, {0} is sdcard", "Eject SD Card {0}").format(job._sdcard) - ) - - job._message.hide() - - message._sdcard = job._sdcard - message.actionTriggered.connect(self._onMessageActionTriggered) - message.show() - - def _onMessageActionTriggered(self, message, action): - if action == "eject": - self.getStorageDevice("LocalFileStorage").ejectRemovableDrive(message._sdcard) - def _onFileLoaded(self, job): mesh = job.getResult() if mesh != None: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 226f4f59a9..6cb4d0112e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.1 -import UM 1.0 as UM +import UM 1.1 as UM UM.MainWindow { id: base @@ -30,21 +30,40 @@ UM.MainWindow { title: qsTr("&File"); MenuItem { action: actions.open; } - MenuItem { action: actions.save; } - MenuSeparator { } + Menu { + id: recentFilesMenu; + title: "Open Recent" - Instantiator { - model: Printer.recentFiles - MenuItem { - text: { - var path = modelData.toString() - return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); + Instantiator { + model: Printer.recentFiles + MenuItem { + text: { + var path = modelData.toString() + return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); + } + onTriggered: UM.MeshFileHandler.readLocalFile(modelData); } - onTriggered: UM.MeshFileHandler.readLocalFile(modelData); + onObjectAdded: recentFilesMenu.insertItem(index, object) + onObjectRemoved: recentFilesMenu.removeItem(object) + } + } + + MenuItem { text: "Save Selection" } + Menu { + id: saveAllMenu + title: "Save All" + + Instantiator { + model: UM.OutputDevicesModel { } + + MenuItem { + text: model.description + onTriggered: model.requestWriteToCurrentDevice(); + } + onObjectAdded: saveAllMenu.insertItem(index, object) + onObjectRemoved: saveAllMenu.removeItem(object) } - onObjectAdded: fileMenu.insertItem(index, object) - onObjectRemoved: fileMenu.removeItem(object) } MenuSeparator { } @@ -278,7 +297,6 @@ UM.MainWindow { addMachineAction: actions.addMachine; configureMachinesAction: actions.configureMachines; - saveAction: actions.save; } Rectangle { @@ -411,22 +429,6 @@ UM.MainWindow { } } - FileDialog { - id: saveDialog; - //: File save dialog title - title: qsTr("Save File"); - selectExisting: false; - - modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; - - nameFilters: UM.MeshFileHandler.supportedWriteFileTypes - - onAccepted: - { - UM.MeshFileHandler.writeLocalFile(fileUrl); - } - } - EngineLog { id: engineLog; } @@ -442,7 +444,6 @@ UM.MainWindow { Connections { target: Printer onRequestAddPrinter: addMachine.visible = true; - onWriteToLocalFileRequested: saveDialog.open(); } Component.onCompleted: UM.Theme.load(UM.Resources.getPath(UM.Resources.ThemesLocation, "cura")) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 23ddfe4ed7..0c908fc789 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -13,7 +13,6 @@ Rectangle { property Action addMachineAction; property Action configureMachinesAction; - property alias saveAction: saveButton.saveAction; color: UM.Theme.colors.sidebar; From 078295d6e1e916197193f715d9c56b4e51e896ee Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 8 Jul 2015 22:04:50 +0200 Subject: [PATCH 04/26] Write to the right device after changes in Uranium API --- resources/qml/Cura.qml | 4 ++-- resources/qml/SaveButton.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 6cb4d0112e..06401a68d2 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -55,11 +55,11 @@ UM.MainWindow { title: "Save All" Instantiator { - model: UM.OutputDevicesModel { } + model: UM.OutputDevicesModel { id: devicesModel; } MenuItem { text: model.description - onTriggered: model.requestWriteToCurrentDevice(); + onTriggered: devicesModel.requestWriteToDevice(model.id); } onObjectAdded: saveAllMenu.insertItem(index, object) onObjectRemoved: saveAllMenu.removeItem(object) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 8f75c82ec3..214088bf72 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -126,7 +126,7 @@ Rectangle { } label: Item { } } - onClicked: devicesModel.requestWriteToCurrentDevice() + onClicked: devicesModel.requestWriteToDevice(devicesModel.currentDevice.id) } Button { From 2f2cef54a98e2328a638d9bbdfd2e0312606d906 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 16 Jul 2015 09:26:06 +0200 Subject: [PATCH 05/26] Add mime types to GCodeWriter plugin --- plugins/GCodeWriter/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/GCodeWriter/__init__.py b/plugins/GCodeWriter/__init__.py index 3897cc3f75..65f53f64c3 100644 --- a/plugins/GCodeWriter/__init__.py +++ b/plugins/GCodeWriter/__init__.py @@ -18,7 +18,10 @@ def getMetaData(): "mesh_writer": { "extension": "gcode", - "description": catalog.i18nc("GCode Writer File Description", "GCode File") + "description": catalog.i18nc("GCode Writer File Description", "GCode File"), + "mime_types": [ + "text/x-gcode" + ] } } From d6b3044c797b3a6fa699ee15842c6d02e9973d47 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jul 2015 17:57:08 +0200 Subject: [PATCH 06/26] Update GCodeWriter to the new API --- plugins/GCodeWriter/GCodeWriter.py | 23 +++++++++++------------ plugins/GCodeWriter/__init__.py | 11 ++++++++--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index f776c6f0c1..d3db35e762 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -10,18 +10,17 @@ import io class GCodeWriter(MeshWriter): def __init__(self): super().__init__() - self._gcode = None - def write(self, file_name, storage_device, mesh_data): - if "gcode" in file_name: - scene = Application.getInstance().getController().getScene() - gcode_list = getattr(scene, "gcode_list") - if gcode_list: - f = storage_device.openFile(file_name, "wt") - Logger.log("d", "Writing GCode to file %s", file_name) - for gcode in gcode_list: - f.write(gcode) - storage_device.closeFile(f) - return True + def write(self, stream, node, mode = MeshWriter.OutputMode.TextMode): + if mode != MeshWriter.OutputMode.TextMode: + Logger.log("e", "GCode Writer does not support non-text mode") + return False + + scene = Application.getInstance().getController().getScene() + gcode_list = getattr(scene, "gcode_list") + if gcode_list: + for gcode in gcode_list: + stream.write(gcode) + return True return False diff --git a/plugins/GCodeWriter/__init__.py b/plugins/GCodeWriter/__init__.py index 3897cc3f75..4f0b5dd82a 100644 --- a/plugins/GCodeWriter/__init__.py +++ b/plugins/GCodeWriter/__init__.py @@ -13,12 +13,17 @@ def getMetaData(): "name": "GCode Writer", "author": "Ultimaker", "version": "1.0", - "description": catalog.i18nc("GCode Writer Plugin Description", "Writes GCode to a file") + "description": catalog.i18nc("GCode Writer Plugin Description", "Writes GCode to a file"), + "api": 2 }, "mesh_writer": { - "extension": "gcode", - "description": catalog.i18nc("GCode Writer File Description", "GCode File") + "output": [{ + "extension": "gcode", + "description": catalog.i18nc("GCode Writer File Description", "GCode File"), + "mime_type": "text/x-gcode", + "mode": GCodeWriter.GCodeWriter.OutputMode.TextMode + }] } } From 68cde3f7806e130b33eacf2fae4309ed9bae6c82 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 09:31:28 +0200 Subject: [PATCH 07/26] Update SaveButton to the changed OutputDevicesModel API --- resources/qml/SaveButton.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 214088bf72..710fbb5293 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -105,13 +105,13 @@ Rectangle { anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height; anchors.left: parent.left anchors.leftMargin: UM.Theme.sizes.default_margin.width; - tooltip: devicesModel.currentDevice.description; + tooltip: devicesModel.activeDevice.description; enabled: progress >= 0.99; width: infoBox.width/6*4.5 height: UM.Theme.sizes.save_button_save_to_button.height - text: devicesModel.currentDevice.short_description; + text: devicesModel.activeDevice.short_description; style: ButtonStyle { background: Rectangle { @@ -126,7 +126,7 @@ Rectangle { } label: Item { } } - onClicked: devicesModel.requestWriteToDevice(devicesModel.currentDevice.id) + onClicked: devicesModel.requestWriteToDevice(devicesModel.activeDevice.id) } Button { @@ -141,7 +141,7 @@ Rectangle { width: infoBox.width/6*1.3 - UM.Theme.sizes.save_button_text_margin.height; height: UM.Theme.sizes.save_button_save_to_button.height - iconSource: UM.Theme.icons[devicesModel.currentDevice.icon_name]; + iconSource: UM.Theme.icons[devicesModel.activeDevice.icon_name]; style: ButtonStyle { background: Rectangle { @@ -186,10 +186,10 @@ Rectangle { MenuItem { text: model.description checkable: true; - checked: model.id == devicesModel.currentDevice.id; + checked: model.id == devicesModel.activeDevice.id; exclusiveGroup: devicesMenuGroup; onTriggered: { - devicesModel.setCurrentDevice(model.id); + devicesModel.setActiveDevice(model.id); } } onObjectAdded: devicesMenu.insertItem(index, object) From 7c2617133e30a20b2b073db901a6a2431c945dea Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 30 Jul 2015 15:09:50 +0200 Subject: [PATCH 08/26] Updated how machine selection works; Id now identifies a type of machine so multiple variations of the same machine are possible without cluttering your selection --- resources/qml/AddMachineWizard.qml | 52 +++++++++++++++++-- resources/settings/fdmprinter.json | 1 + resources/settings/grr_neo.json | 2 +- resources/settings/hephestos.json | 2 +- resources/settings/hephestos_XL.json | 2 +- resources/settings/prusai3.json | 3 +- resources/settings/ultimaker2.json | 3 +- resources/settings/ultimaker2extended.json | 3 +- resources/settings/ultimaker2go.json | 3 +- resources/settings/ultimaker_original.json | 3 +- .../settings/ultimaker_original_plus.json | 2 +- resources/settings/witbox.json | 2 +- 12 files changed, 63 insertions(+), 15 deletions(-) diff --git a/resources/qml/AddMachineWizard.qml b/resources/qml/AddMachineWizard.qml index 06341e140c..4a59f798a5 100644 --- a/resources/qml/AddMachineWizard.qml +++ b/resources/qml/AddMachineWizard.qml @@ -42,23 +42,65 @@ UM.Dialog model: UM.Models.availableMachinesModel delegate: RadioButton { + id:machine_button exclusiveGroup: printerGroup; text: model.name; onClicked: ListView.view.currentIndex = index; + Component.onCompleted: + { + if(index == 0) + { + machine_button.checked = true + ListView.view.currentIndex = index; + } + } } } } - - Label { + Label + { + text: qsTr("Variation:"); + } + + ScrollView + { + width: 50 + height:150 + + ListView + { + Component.onCompleted:console.log(model) + id: variations_list + model: machineList.model.getItem(machineList.currentIndex).variations + delegate: RadioButton + { + id: variation_radio_button + exclusiveGroup: variationGroup; + text: model.name; + onClicked: ListView.view.currentIndex = index; + Component.onCompleted: + { + if(index == 0) + { + variation_radio_button.checked = true + ListView.view.currentIndex = index; + } + } + } + } + + } + Label + { //: Add Printer wizard field label text: qsTr("Printer Name:"); } - - TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).variations.getItem(variations_list.currentIndex).name } Item { Layout.fillWidth: true; Layout.fillHeight: true; } ExclusiveGroup { id: printerGroup; } + ExclusiveGroup { id: variationGroup; } } rightButtons: [ @@ -70,7 +112,7 @@ UM.Dialog { if(machineList.currentIndex != -1) { - UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, machineName.text) + UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, variations_list.currentIndex, machineName.text) base.visible = false } } diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 77082e3722..7d7c396228 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1,5 +1,6 @@ { "visible": false, + "manufacturer": "other", "machine_settings": { "machine_start_gcode": { "default": "G28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0" diff --git a/resources/settings/grr_neo.json b/resources/settings/grr_neo.json index fbec330adc..fa6daa1054 100644 --- a/resources/settings/grr_neo.json +++ b/resources/settings/grr_neo.json @@ -1,5 +1,5 @@ { - "id": "GRRneo", + "id": "German RepRap Neo", "name": "German RepRap Neo", "icon": "icon_ultimaker.png", "platform": "grr_neo_platform.stl", diff --git a/resources/settings/hephestos.json b/resources/settings/hephestos.json index ce97fc21a5..bd702ffd0c 100644 --- a/resources/settings/hephestos.json +++ b/resources/settings/hephestos.json @@ -1,5 +1,5 @@ { - "id": "hephestos", + "id": "BQ Prusa i3 Hephestos", "name": "BQ Prusa i3 Hephestos", "platform": "hephestos_platform.stl", "inherits": "fdmprinter.json", diff --git a/resources/settings/hephestos_XL.json b/resources/settings/hephestos_XL.json index 5a308bab2b..f9b5ee5240 100644 --- a/resources/settings/hephestos_XL.json +++ b/resources/settings/hephestos_XL.json @@ -1,5 +1,5 @@ { - "id": "hephestos_XL", + "id": "BQ Prusa i3 Hephestos XL", "name": "BQ Prusa i3 Hephestos XL", "platform": "hephestos_platform.stl", "inherits": "fdmprinter.json", diff --git a/resources/settings/prusai3.json b/resources/settings/prusai3.json index d411a114e8..ca626976f1 100644 --- a/resources/settings/prusai3.json +++ b/resources/settings/prusai3.json @@ -1,5 +1,6 @@ { - "id": "prusa_i3", + "id": "Prusa i3", + "manufacturer": "ultimaker", "icon": "icon_ultimaker2.png", "name": "Prusa i3", "platform": "prusai3_platform.stl", diff --git a/resources/settings/ultimaker2.json b/resources/settings/ultimaker2.json index f845573ec7..7d35d349da 100644 --- a/resources/settings/ultimaker2.json +++ b/resources/settings/ultimaker2.json @@ -1,5 +1,6 @@ { - "id": "ultimaker2", + "id": "Ultimaker 2", + "manufacturer": "ultimaker", "name": "Ultimaker 2", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", diff --git a/resources/settings/ultimaker2extended.json b/resources/settings/ultimaker2extended.json index 7ef1e35d3f..720deebfd2 100644 --- a/resources/settings/ultimaker2extended.json +++ b/resources/settings/ultimaker2extended.json @@ -1,5 +1,6 @@ { - "id": "ultimaker2extended", + "id": "Ultimaker 2 Extended", + "manufacturer": "ultimaker", "name": "Ultimaker 2 Extended", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", diff --git a/resources/settings/ultimaker2go.json b/resources/settings/ultimaker2go.json index 6d102c4e7c..c173cb5f8e 100644 --- a/resources/settings/ultimaker2go.json +++ b/resources/settings/ultimaker2go.json @@ -1,5 +1,6 @@ { - "id": "ultimaker2go", + "id": "Ultimaker 2 Go", + "manufacturer": "ultimaker", "name": "Ultimaker 2 Go", "icon": "icon_ultimaker2.png", "platform": "ultimaker2go_platform.obj", diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index 360bbbe6b6..4fd921466f 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -1,5 +1,6 @@ { - "id": "ultimaker_original", + "id": "Ultimaker Original", + "manufacturer": "ultimaker", "name": "Ultimaker Original", "icon": "icon_ultimaker.png", "platform": "ultimaker_platform.stl", diff --git a/resources/settings/ultimaker_original_plus.json b/resources/settings/ultimaker_original_plus.json index 9e5cf6370d..4e002f6d2a 100644 --- a/resources/settings/ultimaker_original_plus.json +++ b/resources/settings/ultimaker_original_plus.json @@ -1,5 +1,5 @@ { - "id": "ultimaker_original_plus", + "id": "Ultimaker Original", "name": "Ultimaker Original+", "icon": "icon_ultimaker.png", "platform": "ultimaker2_platform.obj", diff --git a/resources/settings/witbox.json b/resources/settings/witbox.json index bafcceccdd..9a8c1ed9ea 100644 --- a/resources/settings/witbox.json +++ b/resources/settings/witbox.json @@ -1,5 +1,5 @@ { - "id": "witbox", + "id": "BQ Witbox", "name": "BQ Witbox", "platform": "witbox_platform.stl", "inherits": "fdmprinter.json", From b5e8f01cfab2d0e64d786b53ea0108bfed6fc3cb Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 16:53:16 +0200 Subject: [PATCH 09/26] Return empty string so we get no errors about assigning undefined to string --- resources/qml/SaveButton.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 710fbb5293..83d268fb2b 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -46,7 +46,7 @@ Rectangle { visible: base.progress >= 0 && base.progress < 0.99 ? false : true color: UM.Theme.colors.save_button_estimated_text; font: UM.Theme.fonts.small; - text: + text: { if(base.progress < 0) { //: Save button label return qsTr("Please load a 3D model"); @@ -60,6 +60,8 @@ Rectangle { //: Save button label return qsTr("Estimated Print-time"); } + return ""; + } } Label { id: printDurationLabel From db599545215cc34c9c4fae80107dc7d90886e635 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 17:07:18 +0200 Subject: [PATCH 10/26] Properly implement Save Selection --- resources/qml/Cura.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 06401a68d2..02e6738c14 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -49,7 +49,14 @@ UM.MainWindow { } } - MenuItem { text: "Save Selection" } + MenuSeparator { } + + MenuItem { + text: "Save Selection to File"; + enabled: UM.Selection.hasSelection; + iconName: "document-save-as"; + onTriggered: devicesModel.requestWriteSelectionToDevice("local_file"); + } Menu { id: saveAllMenu title: "Save All" From 619e178f0cb8f737e0507dd7df7ba766c52b4672 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 17:07:46 +0200 Subject: [PATCH 11/26] Disable recent files if there are no recent files and add an icon --- resources/qml/Cura.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 02e6738c14..7e12b9ba12 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -34,6 +34,9 @@ UM.MainWindow { Menu { id: recentFilesMenu; title: "Open Recent" + iconName: "document-open-recent"; + + enabled: Printer.recentFiles.length > 0; Instantiator { model: Printer.recentFiles From 3e024e1618bd4fb2a1dfc6280030a249515ef6a8 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 17:08:10 +0200 Subject: [PATCH 12/26] Add an icon for "save all" and only enable the action when it makes sense --- resources/qml/Cura.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 7e12b9ba12..760f96fe62 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -63,6 +63,8 @@ UM.MainWindow { Menu { id: saveAllMenu title: "Save All" + iconName: "document-save"; + enabled: devicesModel.count > 0 && UM.Backend.progress > 0.99; Instantiator { model: UM.OutputDevicesModel { id: devicesModel; } From 825349b47bc9f11b741a71eaae5952d0f7f3b1eb Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jul 2015 17:14:22 +0200 Subject: [PATCH 13/26] Add RemovableDrive plugin that has been moved from Uranium Since it now depends on GCodeWriter we should put it somewhere where GCodeWriter actually exists. --- .../LinuxRemovableDrivePlugin.py | 41 ++++++++ .../OSXRemovableDrivePlugin.py | 64 ++++++++++++ .../RemovableDriveOutputDevice.py | 87 ++++++++++++++++ .../RemovableDrivePlugin.py | 73 ++++++++++++++ .../WindowsRemovableDrivePlugin.py | 98 +++++++++++++++++++ .../RemovableDriveOutputDevice/__init__.py | 32 ++++++ 6 files changed, 395 insertions(+) create mode 100644 plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py create mode 100644 plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py create mode 100644 plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py create mode 100644 plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py create mode 100644 plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py create mode 100644 plugins/RemovableDriveOutputDevice/__init__.py diff --git a/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py new file mode 100644 index 0000000000..ce948c472b --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/LinuxRemovableDrivePlugin.py @@ -0,0 +1,41 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2013 David Braam +# Uranium is released under the terms of the AGPLv3 or higher. + +from . import RemovableDrivePlugin + +import glob +import os +import subprocess + +## Support for removable devices on Linux. +# +# TODO: This code uses the most basic interfaces for handling this. +# We should instead use UDisks2 to handle mount/unmount and hotplugging events. +# +class LinuxRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + def checkRemovableDrives(self): + drives = {} + for volume in glob.glob("/media/*"): + if os.path.ismount(volume): + drives[volume] = os.path.basename(volume) + elif volume == "/media/"+os.getenv("USER"): + for volume in glob.glob("/media/"+os.getenv("USER")+"/*"): + if os.path.ismount(volume): + drives[volume] = os.path.basename(volume) + + for volume in glob.glob("/run/media/" + os.getenv("USER") + "/*"): + if os.path.ismount(volume): + drives[volume] = os.path.basename(volume) + + return drives + + def performEjectDevice(self, device): + p = subprocess.Popen(["umount", device.getId()], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = p.communicate() + + return_code = p.wait() + if return_code != 0: + return False + else: + return True diff --git a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py new file mode 100644 index 0000000000..c50443cb92 --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2013 David Braam +# Uranium is released under the terms of the AGPLv3 or higher. + +from . import RemovableDrivePlugin + +import threading + +import subprocess +import time +import os + +import plistlib + +## Support for removable devices on Mac OSX +class OSXRemovableDrives(RemovableDrivePlugin.RemovableDrivePlugin): + def run(self): + drives = {} + p = subprocess.Popen(["system_profiler", "SPUSBDataType", "-xml"], stdout=subprocess.PIPE) + plist = plistlib.loads(p.communicate()[0]) + p.wait() + + for dev in self._findInTree(plist, "Mass Storage Device"): + if "removable_media" in dev and dev["removable_media"] == "yes" and "volumes" in dev and len(dev["volumes"]) > 0: + for vol in dev["volumes"]: + if "mount_point" in vol: + volume = vol["mount_point"] + drives[volume] = os.path.basename(volume) + + p = subprocess.Popen(["system_profiler", "SPCardReaderDataType", "-xml"], stdout=subprocess.PIPE) + plist = plistlib.loads(p.communicate()[0]) + p.wait() + + for entry in plist: + if "_items" in entry: + for item in entry["_items"]: + for dev in item["_items"]: + if "removable_media" in dev and dev["removable_media"] == "yes" and "volumes" in dev and len(dev["volumes"]) > 0: + for vol in dev["volumes"]: + if "mount_point" in vol: + volume = vol["mount_point"] + drives[volume] = os.path.basename(volume) + + def performEjectDevice(self, device): + p = subprocess.Popen(["diskutil", "eject", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = p.communicate() + + return_code = p.wait() + if return_code != 0: + return False + else: + return True + + def _findInTree(self, t, n): + ret = [] + if type(t) is dict: + if "_name" in t and t["_name"] == n: + ret.append(t) + for k, v in t.items(): + ret += self._findInTree(v, n) + if type(t) is list: + for v in t: + ret += self._findInTree(v, n) + return ret diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py new file mode 100644 index 0000000000..2728dfd90b --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -0,0 +1,87 @@ +import os.path + +from UM.Application import Application +from UM.Logger import Logger +from UM.Message import Message +from UM.Mesh.WriteMeshJob import WriteMeshJob +from UM.Mesh.MeshWriter import MeshWriter +from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator +from UM.OutputDevice.OutputDevice import OutputDevice +from UM.OutputDevice import OutputDeviceError + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("uranium") + +class RemovableDriveOutputDevice(OutputDevice): + def __init__(self, device_id, device_name): + super().__init__(device_id) + + self.setName(device_name) + self.setShortDescription(catalog.i18nc("", "Save to Removable Drive")) + self.setDescription(catalog.i18nc("", "Save to Removable Drive {0}").format(device_name)) + self.setIconName("save_sd") + self.setPriority(1) + + def requestWrite(self, node): + gcode_writer = Application.getInstance().getMeshFileHandler().getWriterByMimeType("text/x-gcode") + if not gcode_writer: + Logger.log("e", "Could not find GCode writer, not writing to removable drive %s", self.getName()) + raise OutputDeviceError.WriteRequestFailedError() + + file_name = None + for n in BreadthFirstIterator(node): + if n.getMeshData(): + file_name = n.getName() + if file_name: + break + + if not file_name: + Logger.log("e", "Could not determine a proper file name when trying to write to %s, aborting", self.getName()) + raise OutputDeviceError.WriteRequestFailedError() + + file_name = os.path.join(self.getId(), os.path.splitext(file_name)[0] + ".gcode") + + try: + Logger.log("d", "Writing to %s", file_name) + stream = open(file_name, "wt") + job = WriteMeshJob(gcode_writer, stream, node, MeshWriter.OutputMode.TextMode) + job.setFileName(file_name) + job.progress.connect(self._onProgress) + job.finished.connect(self._onFinished) + + message = Message(catalog.i18nc("", "Saving to Removable Drive {0}").format(self.getName()), 0, False, -1) + message.show() + + job._message = message + job.start() + except PermissionError as e: + raise OutputDeviceError.PermissionDeniedError() from e + except OSError as e: + raise OutputDeviceError.WriteRequestFailedError() from e + + def _onProgress(self, job, progress): + if hasattr(job, "_message"): + job._message.setProgress(progress) + self.writeProgress.emit(self, progress) + + def _onFinished(self, job): + if hasattr(job, "_message"): + job._message.hide() + job._message = None + self.writeFinished.emit(self) + if job.getResult(): + message = Message(catalog.i18nc("", "Saved to Removable Drive {0} as {1}").format(self.getName(), os.path.basename(job.getFileName()))) + message.addAction("eject", catalog.i18nc("", "Eject"), "eject", catalog.i18nc("", "Eject removable device {0}").format(self.getName())) + message.actionTriggered.connect(self._onActionTriggered) + message.show() + self.writeSuccess.emit(self) + else: + message = Message(catalog.i18nc("", "Could not save to removable drive {0}: {1}").format(self.getName(), str(job.getError()))) + message.show() + self.writeError.emit(self) + job.getStream().close() + + def _onActionTriggered(self, message, action): + if action == "eject": + Application.getInstance().getOutputDeviceManager().getOutputDevicePlugin("RemovableDriveOutputDevice").ejectDevice(self) + diff --git a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py new file mode 100644 index 0000000000..a4e5e4f3f9 --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py @@ -0,0 +1,73 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Uranium is released under the terms of the AGPLv3 or higher. + +import threading +import time + +from UM.Signal import Signal +from UM.Message import Message +from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin + +from . import RemovableDriveOutputDevice + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("uranium") + +class RemovableDrivePlugin(OutputDevicePlugin): + def __init__(self): + super().__init__() + + self._update_thread = threading.Thread(target = self._updateThread) + self._update_thread.setDaemon(True) + + self._check_updates = True + + self._drives = {} + + def start(self): + self._update_thread.start() + + def stop(self): + self._check_updates = False + self._update_thread.join() + + self._addRemoveDrives({}) + + def checkRemovableDrives(self): + raise NotImplementedError() + + def ejectDevice(self, device): + result = self.performEjectDevice(device) + if result: + message = Message(catalog.i18n("Ejected {0}. You can now safely remove the drive.").format(device.getName())) + message.show() + else: + message = Message(catalog.i18n("Failed to eject {0}. Maybe it is still in use?").format(device.getName())) + message.show() + + def performEjectDevice(self, device): + raise NotImplementedError() + + def _updateThread(self): + while self._check_updates: + result = self.checkRemovableDrives() + self._addRemoveDrives(result) + time.sleep(5) + + def _addRemoveDrives(self, drives): + # First, find and add all new or changed keys + for key, value in drives.items(): + if key not in self._drives: + self.getOutputDeviceManager().addOutputDevice(RemovableDriveOutputDevice.RemovableDriveOutputDevice(key, value)) + continue + + if self._drives[key] != value: + self.getOutputDeviceManager().removeOutputDevice(key) + self.getOutputDeviceManager().addOutputDevice(RemovableDriveOutputDevice.RemovableDriveOutputDevice(key, value)) + + # Then check for keys that have been removed + for key in self._drives.keys(): + if key not in drives: + self.getOutputDeviceManager().removeOutputDevice(key) + + self._drives = drives diff --git a/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py new file mode 100644 index 0000000000..aa85db0c09 --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py @@ -0,0 +1,98 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2013 David Braam +# Uranium is released under the terms of the AGPLv3 or higher. + +from . import RemovableDrivePlugin + +import threading +import string + +from ctypes import windll +from ctypes import wintypes + +import ctypes +import time +import os +import subprocess + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("uranium") + +# WinAPI Constants that we need +# Hardcoded here due to stupid WinDLL stuff that does not give us access to these values. +DRIVE_REMOVABLE = 2 + +GENERIC_READ = 2147483648 +GENERIC_WRITE = 1073741824 + +FILE_SHARE_READ = 1 +FILE_SHARE_WRITE = 2 + +IOCTL_STORAGE_EJECT_MEDIA = 2967560 + +OPEN_EXISTING = 3 + +## Removable drive support for windows +class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + def checkRemovableDrives(self): + drives = {} + + bitmask = windll.kernel32.GetLogicalDrives() + # Check possible drive letters, from A to Z + # Note: using ascii_uppercase because we do not want this to change with locale! + for letter in string.ascii_uppercase: + drive = "{0}:/".format(letter) + + # Do we really want to skip A and B? + # GetDriveTypeA explicitly wants a byte array of type ascii. It will accept a string, but this wont work + if bitmask & 1 and windll.kernel32.GetDriveTypeA(drive.encode("ascii")) == DRIVE_REMOVABLE: + volume_name = "" + name_buffer = ctypes.create_unicode_buffer(1024) + filesystem_buffer = ctypes.create_unicode_buffer(1024) + error = windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(drive), name_buffer, ctypes.sizeof(name_buffer), None, None, None, filesystem_buffer, ctypes.sizeof(filesystem_buffer)) + + if error != 0: + volume_name = name_buffer.value + + if not volume_name: + volume_name = catalog.i18nc("Default name for removable device", "Removable Drive") + + # Certain readers will report themselves as a volume even when there is no card inserted, but will show an + # "No volume in drive" warning when trying to call GetDiskFreeSpace. However, they will not report a valid + # filesystem, so we can filter on that. In addition, this excludes other things with filesystems Windows + # does not support. + if filesystem_buffer.value == "": + continue + + # Check for the free space. Some card readers show up as a drive with 0 space free when there is no card inserted. + freeBytes = ctypes.c_longlong(0) + if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(freeBytes), None, None) == 0: + continue + + if freeBytes.value < 1: + continue + + drives[drive] = "{0} ({1}:)".format(volume_name, letter) + bitmask >>= 1 + + return drives + + def performEjectDevice(self, device): + # Magic WinAPI stuff + # First, open a handle to the Device + handle = windll.kernel32.CreateFileA("\\\\.\\{0}".format(device.getId()[:-1]).encode("ascii"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, 0, None ) + + if handle == -1: + print(windll.kernel32.GetLastError()) + return + + result = None + # Then, try and tell it to eject + if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None): + result = False + else: + result = True + + # Finally, close the handle + windll.kernel32.CloseHandle(handle) + return result diff --git a/plugins/RemovableDriveOutputDevice/__init__.py b/plugins/RemovableDriveOutputDevice/__init__.py new file mode 100644 index 0000000000..72ba10f01f --- /dev/null +++ b/plugins/RemovableDriveOutputDevice/__init__.py @@ -0,0 +1,32 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Uranium is released under the terms of the AGPLv3 or higher. + +import platform + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("uranium") + +def getMetaData(): + return { + "plugin": { + "name": catalog.i18nc("Removable Drive Output Device Plugin name", "Removable Drive Output Device Plugin"), + "author": "Ultimaker B.V.", + "description": catalog.i18nc("Removable Drive Output Device Plugin description", "Provides removable drive hotplugging and writing support"), + "version": "1.0", + "api": 2 + } + } + +def register(app): + if platform.system() == "Windows": + from . import WindowsRemovableDrivePlugin + return { "output_device": WindowsRemovableDrivePlugin.WindowsRemovableDrivePlugin() } + elif platform.system() == "Darwin": + from . import OSXRemovableDrivePlugin + return { "output_device": OSXRemovableDrivePlugin.OSXRemovableDrivePlugin() } + elif platform.system() == "Linux": + from . import LinuxRemovableDrivePlugin + return { "output_device": LinuxRemovableDrivePlugin.LinuxRemovableDrivePlugin() } + else: + Logger.log("e", "Unsupported system %s, no removable device hotplugging support available.", platform.system()) + return { } From 8596c08a80c10ef2595b5efe3b99b044a08f9d28 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 30 Jul 2015 19:11:48 +0200 Subject: [PATCH 14/26] Adds a generic wizard contributes to #128 --- resources/qml/AddMachineWizard.qml | 10 +++ resources/qml/Cura.qml | 10 ++- resources/qml/WizardPages/AddMachine.qml | 89 +++++++++++++++++++ resources/qml/WizardPages/Bedleveling.qml | 52 +++++++++++ .../qml/WizardPages/SelectUpgradedParts.qml | 52 +++++++++++ .../qml/WizardPages/UltimakerCheckup.qml | 52 +++++++++++ resources/qml/WizardPages/UpgradeFirmware.qml | 52 +++++++++++ resources/settings/fdmprinter.json | 7 ++ resources/settings/ultimaker_original.json | 7 ++ 9 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 resources/qml/WizardPages/AddMachine.qml create mode 100644 resources/qml/WizardPages/Bedleveling.qml create mode 100644 resources/qml/WizardPages/SelectUpgradedParts.qml create mode 100644 resources/qml/WizardPages/UltimakerCheckup.qml create mode 100644 resources/qml/WizardPages/UpgradeFirmware.qml diff --git a/resources/qml/AddMachineWizard.qml b/resources/qml/AddMachineWizard.qml index 4a59f798a5..5f88be3e13 100644 --- a/resources/qml/AddMachineWizard.qml +++ b/resources/qml/AddMachineWizard.qml @@ -8,12 +8,20 @@ import QtQuick.Window 2.1 import UM 1.0 as UM +<<<<<<< Updated upstream UM.Dialog { +======= +UM.Wizard{ +>>>>>>> Stashed changes id: base + property bool printer: true + file: "ultimaker2.json" + firstRun: printer ? false : true //: Add Printer dialog title title: qsTr("Add Printer"); +<<<<<<< Updated upstream ColumnLayout { @@ -124,4 +132,6 @@ UM.Dialog onClicked: base.visible = false; } ] +======= +>>>>>>> Stashed changes } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 35a1ad5e6c..a2f3f20763 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -368,7 +368,7 @@ UM.MainWindow { resetAll.onTriggered: Printer.resetAll() reloadAll.onTriggered: Printer.reloadAll() - addMachine.onTriggered: addMachine.visible = true; + addMachine.onTriggered: addMachineWizard.visible = true; preferences.onTriggered: preferences.visible = true; configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(2); } @@ -460,16 +460,20 @@ UM.MainWindow { } AddMachineWizard { - id: addMachine; + id: addMachineWizard } + AboutDialog { id: aboutDialog } Connections { target: Printer - onRequestAddPrinter: addMachine.visible = true; + onRequestAddPrinter: { + addMachineWizard.visible = true + addMachineWizard.printer = false + } onWriteToLocalFileRequested: saveDialog.open(); } diff --git a/resources/qml/WizardPages/AddMachine.qml b/resources/qml/WizardPages/AddMachine.qml new file mode 100644 index 0000000000..c6be542713 --- /dev/null +++ b/resources/qml/WizardPages/AddMachine.qml @@ -0,0 +1,89 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.0 as UM +import ".." + +ColumnLayout { + id: wizardPage + property string title + anchors.fill: parent + signal openFile(string fileName) + signal closeWizard() + + Connections { + target: rootElement + onFinalClicked: {//You can add functions here that get triggered when the final button is clicked in the wizard-element + saveMachine() + } + } + + Label { + text: parent.title + font.pointSize: 18; + } + + Label { + //: Add Printer wizard page description + text: qsTr("Please select the type of printer:"); + } + + ScrollView { + Layout.fillWidth: true; + ListView { + id: machineList; + model: UM.Models.availableMachinesModel + delegate: RadioButton { + exclusiveGroup: printerGroup; + checked: ListView.view.currentIndex == index ? true : false + text: model.name; + onClicked: { + ListView.view.currentIndex = index; + + } + } + } + } + + Label { + //: Add Printer wizard field label + text: qsTr("Printer Name:"); + } + + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + + Item { Layout.fillWidth: true; Layout.fillHeight: true; } + + ExclusiveGroup { id: printerGroup; } + + function getSpecialMachineType(machineId){ + for (var i = 0; i < UM.Models.addMachinesModel.rowCount(); i++) { + if (UM.Models.addMachinesModel.getItem(i).id == machineId){ + return UM.Models.addMachinesModel.getItem(i).file + } + } + } + + function saveMachine(){ + if(machineList.currentIndex != -1) { + UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, machineName.text) + + var chosenMachineType = UM.Models.availableMachinesModel.getItem(machineList.currentIndex).type + var originalMachineType = getSpecialMachineType("ultimaker_original") + var orginalPlusMachineType = getSpecialMachineType("ultimaker_original_plus") + + if (chosenMachineType == originalMachineType) + wizardPage.openFile(originalMachineType) + if (chosenMachineType == orginalPlusMachineType) + wizardPage.openFile(orginalPlusMachineType) + else + wizardPage.closeWizard() + } + } +} + diff --git a/resources/qml/WizardPages/Bedleveling.qml b/resources/qml/WizardPages/Bedleveling.qml new file mode 100644 index 0000000000..eb8207b37d --- /dev/null +++ b/resources/qml/WizardPages/Bedleveling.qml @@ -0,0 +1,52 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.0 as UM + +ColumnLayout { + property string title + anchors.fill: parent; + + Label { + text: parent.title + font.pointSize: 18; + } + + Label { + //: Add Printer wizard page description + text: qsTr("Please select the type of printer:"); + } + + ScrollView { + Layout.fillWidth: true; + + ListView { + id: machineList; + model: UM.Models.availableMachinesModel + delegate: RadioButton { + exclusiveGroup: printerGroup; + text: model.name; + onClicked: { + ListView.view.currentIndex = index; + + } + } + } + } + + Label { + //: Add Printer wizard field label + text: qsTr("Printer Name:"); + } + + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + + Item { Layout.fillWidth: true; Layout.fillHeight: true; } + + ExclusiveGroup { id: printerGroup; } +} \ No newline at end of file diff --git a/resources/qml/WizardPages/SelectUpgradedParts.qml b/resources/qml/WizardPages/SelectUpgradedParts.qml new file mode 100644 index 0000000000..eb8207b37d --- /dev/null +++ b/resources/qml/WizardPages/SelectUpgradedParts.qml @@ -0,0 +1,52 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.0 as UM + +ColumnLayout { + property string title + anchors.fill: parent; + + Label { + text: parent.title + font.pointSize: 18; + } + + Label { + //: Add Printer wizard page description + text: qsTr("Please select the type of printer:"); + } + + ScrollView { + Layout.fillWidth: true; + + ListView { + id: machineList; + model: UM.Models.availableMachinesModel + delegate: RadioButton { + exclusiveGroup: printerGroup; + text: model.name; + onClicked: { + ListView.view.currentIndex = index; + + } + } + } + } + + Label { + //: Add Printer wizard field label + text: qsTr("Printer Name:"); + } + + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + + Item { Layout.fillWidth: true; Layout.fillHeight: true; } + + ExclusiveGroup { id: printerGroup; } +} \ No newline at end of file diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml new file mode 100644 index 0000000000..eb8207b37d --- /dev/null +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -0,0 +1,52 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.0 as UM + +ColumnLayout { + property string title + anchors.fill: parent; + + Label { + text: parent.title + font.pointSize: 18; + } + + Label { + //: Add Printer wizard page description + text: qsTr("Please select the type of printer:"); + } + + ScrollView { + Layout.fillWidth: true; + + ListView { + id: machineList; + model: UM.Models.availableMachinesModel + delegate: RadioButton { + exclusiveGroup: printerGroup; + text: model.name; + onClicked: { + ListView.view.currentIndex = index; + + } + } + } + } + + Label { + //: Add Printer wizard field label + text: qsTr("Printer Name:"); + } + + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + + Item { Layout.fillWidth: true; Layout.fillHeight: true; } + + ExclusiveGroup { id: printerGroup; } +} \ No newline at end of file diff --git a/resources/qml/WizardPages/UpgradeFirmware.qml b/resources/qml/WizardPages/UpgradeFirmware.qml new file mode 100644 index 0000000000..9369df6e42 --- /dev/null +++ b/resources/qml/WizardPages/UpgradeFirmware.qml @@ -0,0 +1,52 @@ +// Copyright (c) 2015 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.0 as UM + +ColumnLayout { + property string title + anchors.fill: parent; + signal openFile(string fileName) + + Label { + text: parent.title + font.pointSize: 18; + } + + Label { + //: Add Printer wizard page description + text: qsTr("Please select the type of printer:"); + } + + ScrollView { + Layout.fillWidth: true; + + ListView { + id: machineList; + model: UM.Models.availableMachinesModel + delegate: RadioButton { + exclusiveGroup: printerGroup; + text: model.name; + onClicked: { + ListView.view.currentIndex = index; + } + } + } + } + + Label { + //: Add Printer wizard field label + text: qsTr("Printer Name:"); + } + + TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } + + Item { Layout.fillWidth: true; Layout.fillHeight: true; } + + ExclusiveGroup { id: printerGroup; } +} \ No newline at end of file diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 7d7c396228..62133f1858 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1,6 +1,13 @@ { "visible": false, +<<<<<<< Updated upstream "manufacturer": "other", +======= + "add_pages": [ + {"page": "AddMachine", "title": "Add new printer"} + ], + +>>>>>>> Stashed changes "machine_settings": { "machine_start_gcode": { "default": "G28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0" diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index 4fd921466f..a517e6e244 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -7,6 +7,13 @@ "inherits": "fdmprinter.json", + "add_pages": [ + {"page": "SelectUpgradedParts", "title": "Select Upgraded Parts"}, + {"page": "UpgradeFirmware", "title": "Upgrade Ultimaker Firmware"}, + {"page": "UltimakerCheckup", "title": "Ultimaker Checkup"}, + {"page": "Bedleveling", "title": "Bedleveling Wizard"} + ], + "machine_settings": { "machine_width": { "default": 205 }, "machine_height": { "default": 200 }, From b4a897397581d03b59d60cb9d204af6421252a20 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 30 Jul 2015 19:36:05 +0200 Subject: [PATCH 15/26] cleanup --- resources/qml/AddMachineWizard.qml | 121 ----------------------------- resources/settings/fdmprinter.json | 8 +- 2 files changed, 2 insertions(+), 127 deletions(-) diff --git a/resources/qml/AddMachineWizard.qml b/resources/qml/AddMachineWizard.qml index 5f88be3e13..5d6608a7da 100644 --- a/resources/qml/AddMachineWizard.qml +++ b/resources/qml/AddMachineWizard.qml @@ -8,130 +8,9 @@ import QtQuick.Window 2.1 import UM 1.0 as UM -<<<<<<< Updated upstream -UM.Dialog -{ -======= UM.Wizard{ ->>>>>>> Stashed changes id: base property bool printer: true file: "ultimaker2.json" firstRun: printer ? false : true - - //: Add Printer dialog title - title: qsTr("Add Printer"); -<<<<<<< Updated upstream - - ColumnLayout - { - anchors.fill: parent; - - Label - { - //: Add Printer wizard page title - text: qsTr("Add Printer"); - font.pointSize: 18; - } - - Label - { - //: Add Printer wizard page description - text: qsTr("Please select the type of printer:"); - } - - ScrollView - { - Layout.fillWidth: true; - - ListView - { - id: machineList; - model: UM.Models.availableMachinesModel - delegate: RadioButton - { - id:machine_button - exclusiveGroup: printerGroup; - text: model.name; - onClicked: ListView.view.currentIndex = index; - Component.onCompleted: - { - if(index == 0) - { - machine_button.checked = true - ListView.view.currentIndex = index; - } - } - } - } - } - Label - { - text: qsTr("Variation:"); - } - - ScrollView - { - width: 50 - height:150 - - ListView - { - Component.onCompleted:console.log(model) - id: variations_list - model: machineList.model.getItem(machineList.currentIndex).variations - delegate: RadioButton - { - id: variation_radio_button - exclusiveGroup: variationGroup; - text: model.name; - onClicked: ListView.view.currentIndex = index; - Component.onCompleted: - { - if(index == 0) - { - variation_radio_button.checked = true - ListView.view.currentIndex = index; - } - } - } - } - - } - Label - { - //: Add Printer wizard field label - text: qsTr("Printer Name:"); - } - TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).variations.getItem(variations_list.currentIndex).name } - - Item { Layout.fillWidth: true; Layout.fillHeight: true; } - - ExclusiveGroup { id: printerGroup; } - ExclusiveGroup { id: variationGroup; } - } - - rightButtons: [ - Button - { - //: Add Printer wizarad button - text: qsTr("Next"); - onClicked: - { - if(machineList.currentIndex != -1) - { - UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, variations_list.currentIndex, machineName.text) - base.visible = false - } - } - }, - Button - { - //: Add Printer wizarad button - text: qsTr("Cancel"); - onClicked: base.visible = false; - } - ] -======= ->>>>>>> Stashed changes } diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 62133f1858..8a638a0e77 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1,13 +1,9 @@ { "visible": false, -<<<<<<< Updated upstream "manufacturer": "other", -======= - "add_pages": [ - {"page": "AddMachine", "title": "Add new printer"} - ], ->>>>>>> Stashed changes + "add_pages": [{"page": "AddMachine", "title": "Add new printer"}], + "machine_settings": { "machine_start_gcode": { "default": "G28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0" From 084bd25e0207fc6c84402cfcc8b4d60b7500e9c0 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 31 Jul 2015 16:41:53 +0200 Subject: [PATCH 16/26] Machine settings cleanup added manufacturers and authors to the file and changed the id's to a string more usuable in combination with other files --- resources/settings/fdmprinter.json | 1 + resources/settings/grr_neo.json | 4 +++- resources/settings/hephestos.json | 4 +++- .../settings/{hephestos_XL.json => hephestos_xl.json} | 4 +++- resources/settings/{prusai3.json => prusa_i3.json} | 7 ++++--- resources/settings/ultimaker2.json | 5 +++-- .../{ultimaker2extended.json => ultimaker2_extended.json} | 3 +-- .../settings/{ultimaker2go.json => ultimaker2_go.json} | 3 +-- resources/settings/ultimaker_original.json | 5 +++-- resources/settings/ultimaker_original_plus.json | 2 +- resources/settings/witbox.json | 4 +++- 11 files changed, 26 insertions(+), 16 deletions(-) rename resources/settings/{hephestos_XL.json => hephestos_xl.json} (98%) rename resources/settings/{prusai3.json => prusa_i3.json} (96%) rename resources/settings/{ultimaker2extended.json => ultimaker2_extended.json} (84%) rename resources/settings/{ultimaker2go.json => ultimaker2_go.json} (93%) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 8a638a0e77..9aae3d6dc4 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1,6 +1,7 @@ { "visible": false, "manufacturer": "other", + "author": "other", "add_pages": [{"page": "AddMachine", "title": "Add new printer"}], diff --git a/resources/settings/grr_neo.json b/resources/settings/grr_neo.json index fa6daa1054..cb8898f2ff 100644 --- a/resources/settings/grr_neo.json +++ b/resources/settings/grr_neo.json @@ -1,6 +1,8 @@ { - "id": "German RepRap Neo", + "id": "grr_neo", "name": "German RepRap Neo", + "manufacturer": "German RepRap", + "author": "other", "icon": "icon_ultimaker.png", "platform": "grr_neo_platform.stl", diff --git a/resources/settings/hephestos.json b/resources/settings/hephestos.json index bd702ffd0c..69af6b87ec 100644 --- a/resources/settings/hephestos.json +++ b/resources/settings/hephestos.json @@ -1,6 +1,8 @@ { - "id": "BQ Prusa i3 Hephestos", + "id": "hephestos", "name": "BQ Prusa i3 Hephestos", + "manufacturer": "BQ", + "author": "other", "platform": "hephestos_platform.stl", "inherits": "fdmprinter.json", diff --git a/resources/settings/hephestos_XL.json b/resources/settings/hephestos_xl.json similarity index 98% rename from resources/settings/hephestos_XL.json rename to resources/settings/hephestos_xl.json index f9b5ee5240..9c4538ede0 100644 --- a/resources/settings/hephestos_XL.json +++ b/resources/settings/hephestos_xl.json @@ -1,6 +1,8 @@ { - "id": "BQ Prusa i3 Hephestos XL", + "id": "hephestos_xl", "name": "BQ Prusa i3 Hephestos XL", + "manufacturer": "BQ", + "author": "other", "platform": "hephestos_platform.stl", "inherits": "fdmprinter.json", diff --git a/resources/settings/prusai3.json b/resources/settings/prusa_i3.json similarity index 96% rename from resources/settings/prusai3.json rename to resources/settings/prusa_i3.json index ca626976f1..af90a6f80f 100644 --- a/resources/settings/prusai3.json +++ b/resources/settings/prusa_i3.json @@ -1,8 +1,9 @@ { - "id": "Prusa i3", - "manufacturer": "ultimaker", - "icon": "icon_ultimaker2.png", + "id": "prusa_i3", "name": "Prusa i3", + "manufacturer": "Prusa", + "author": "other", + "icon": "icon_ultimaker2.png", "platform": "prusai3_platform.stl", "inherits": "fdmprinter.json", diff --git a/resources/settings/ultimaker2.json b/resources/settings/ultimaker2.json index 7d35d349da..f74b208cc8 100644 --- a/resources/settings/ultimaker2.json +++ b/resources/settings/ultimaker2.json @@ -1,7 +1,8 @@ { - "id": "Ultimaker 2", - "manufacturer": "ultimaker", + "id": "ultimaker2", "name": "Ultimaker 2", + "manufacturer": "Ultimaker", + "author": "Ultimaker", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", "platform_texture": "Ultimaker2backplate.png", diff --git a/resources/settings/ultimaker2extended.json b/resources/settings/ultimaker2_extended.json similarity index 84% rename from resources/settings/ultimaker2extended.json rename to resources/settings/ultimaker2_extended.json index 720deebfd2..b6156ec18e 100644 --- a/resources/settings/ultimaker2extended.json +++ b/resources/settings/ultimaker2_extended.json @@ -1,6 +1,5 @@ { - "id": "Ultimaker 2 Extended", - "manufacturer": "ultimaker", + "id": "ultimaker2_extended", "name": "Ultimaker 2 Extended", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", diff --git a/resources/settings/ultimaker2go.json b/resources/settings/ultimaker2_go.json similarity index 93% rename from resources/settings/ultimaker2go.json rename to resources/settings/ultimaker2_go.json index c173cb5f8e..4d7a535553 100644 --- a/resources/settings/ultimaker2go.json +++ b/resources/settings/ultimaker2_go.json @@ -1,6 +1,5 @@ { - "id": "Ultimaker 2 Go", - "manufacturer": "ultimaker", + "id": "ultimaker2_go", "name": "Ultimaker 2 Go", "icon": "icon_ultimaker2.png", "platform": "ultimaker2go_platform.obj", diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index a517e6e244..14e9b9348a 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -1,7 +1,8 @@ { - "id": "Ultimaker Original", - "manufacturer": "ultimaker", + "id": "ultimaker_original", "name": "Ultimaker Original", + "manufacturer": "Ultimaker", + "author": "Ultimaker", "icon": "icon_ultimaker.png", "platform": "ultimaker_platform.stl", diff --git a/resources/settings/ultimaker_original_plus.json b/resources/settings/ultimaker_original_plus.json index 4e002f6d2a..9e5cf6370d 100644 --- a/resources/settings/ultimaker_original_plus.json +++ b/resources/settings/ultimaker_original_plus.json @@ -1,5 +1,5 @@ { - "id": "Ultimaker Original", + "id": "ultimaker_original_plus", "name": "Ultimaker Original+", "icon": "icon_ultimaker.png", "platform": "ultimaker2_platform.obj", diff --git a/resources/settings/witbox.json b/resources/settings/witbox.json index 9a8c1ed9ea..aee1030219 100644 --- a/resources/settings/witbox.json +++ b/resources/settings/witbox.json @@ -1,6 +1,8 @@ { - "id": "BQ Witbox", + "id": "bq_witbox", "name": "BQ Witbox", + "manufacturer": "BQ", + "author": "other", "platform": "witbox_platform.stl", "inherits": "fdmprinter.json", From 783b75bcd9e13760caafb41e480267a714e9f7c7 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 31 Jul 2015 16:42:57 +0200 Subject: [PATCH 17/26] combines work on the wizard by Jaime and myself contributes to #128 --- resources/qml/WizardPages/AddMachine.qml | 50 +++++++++++++++++------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/resources/qml/WizardPages/AddMachine.qml b/resources/qml/WizardPages/AddMachine.qml index c6be542713..7e69c06366 100644 --- a/resources/qml/WizardPages/AddMachine.qml +++ b/resources/qml/WizardPages/AddMachine.qml @@ -12,7 +12,6 @@ import ".." ColumnLayout { id: wizardPage property string title - anchors.fill: parent signal openFile(string fileName) signal closeWizard() @@ -34,11 +33,11 @@ ColumnLayout { } ScrollView { - Layout.fillWidth: true; ListView { id: machineList; model: UM.Models.availableMachinesModel delegate: RadioButton { + id:machine_button exclusiveGroup: printerGroup; checked: ListView.view.currentIndex == index ? true : false text: model.name; @@ -50,39 +49,60 @@ ColumnLayout { } } + Label { + text: qsTr("Variation:"); + } + + ScrollView { + ListView { + id: variations_list + model: machineList.model.getItem(machineList.currentIndex).variations + delegate: RadioButton { + id: variation_radio_button + checked: ListView.view.currentIndex == index ? true : false + exclusiveGroup: variationGroup; + text: model.name; + onClicked: ListView.view.currentIndex = index; + } + } + } + Label { //: Add Printer wizard field label text: qsTr("Printer Name:"); } TextField { id: machineName; Layout.fillWidth: true; text: machineList.model.getItem(machineList.currentIndex).name } - Item { Layout.fillWidth: true; Layout.fillHeight: true; } - ExclusiveGroup { id: printerGroup; } + ExclusiveGroup { id: variationGroup; } function getSpecialMachineType(machineId){ for (var i = 0; i < UM.Models.addMachinesModel.rowCount(); i++) { - if (UM.Models.addMachinesModel.getItem(i).id == machineId){ - return UM.Models.addMachinesModel.getItem(i).file + if (UM.Models.addMachinesModel.getItem(i).name == machineId){ + return UM.Models.addMachinesModel.getItem(i).name } } } function saveMachine(){ if(machineList.currentIndex != -1) { - UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, machineName.text) + UM.Models.availableMachinesModel.createMachine(machineList.currentIndex, variations_list.currentIndex, machineName.text) - var chosenMachineType = UM.Models.availableMachinesModel.getItem(machineList.currentIndex).type - var originalMachineType = getSpecialMachineType("ultimaker_original") - var orginalPlusMachineType = getSpecialMachineType("ultimaker_original_plus") + var originalString = "Ultimaker Original" + var originalPlusString = "Ultimaker Original+" + var originalMachineType = getSpecialMachineType(originalString) - if (chosenMachineType == originalMachineType) - wizardPage.openFile(originalMachineType) - if (chosenMachineType == orginalPlusMachineType) - wizardPage.openFile(orginalPlusMachineType) - else + if (UM.Models.availableMachinesModel.getItem(machineList.currentIndex).name == originalMachineType){ + var variation = UM.Models.availableMachinesModel.getItem(machineList.currentIndex).variations.getItem(variations_list.currentIndex).name + if (variation == originalString || variation == originalPlusString){ + console.log(UM.Models.availableMachinesModel.getItem(machineList.currentIndex).variations.getItem(variations_list.currentIndex).type) + wizardPage.openFile(UM.Models.availableMachinesModel.getItem(machineList.currentIndex).variations.getItem(variations_list.currentIndex).type) + } + } + else { wizardPage.closeWizard() + } } } } From a82717f65cb5ff7026573ea4ed474313e4865212 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 31 Jul 2015 17:05:44 +0200 Subject: [PATCH 18/26] Added views to top bar Closes #73 and CURA-38 #close --- resources/qml/Cura.qml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a2f3f20763..99c61d68e0 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -65,7 +65,26 @@ UM.MainWindow { MenuItem { action: actions.deleteSelection; } MenuItem { action: actions.deleteAll; } } - + Menu + { + title: qsTr("&View"); + id: top_view_menu + Instantiator + { + model: UM.Models.viewModel + MenuItem + { + text: model.name; + checkable: true; + checked: model.active; + exclusiveGroup: view_menu_top_group; + onTriggered: UM.Controller.setActiveView(model.id); + } + onObjectAdded: top_view_menu.insertItem(index, object) + onObjectRemoved: top_view_menu.removeItem(object) + } + ExclusiveGroup { id: view_menu_top_group; } + } Menu { id: machineMenu; //: Machine menu From b96b069c21083150affb5b73513dfb6f9e468e6f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 31 Jul 2015 17:07:06 +0200 Subject: [PATCH 19/26] Remove LocalFileStorage from required plugins and add LocalFileOutputDevice --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4020d39005..fc810ac76b 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -66,7 +66,7 @@ class CuraApplication(QtApplication): "SelectionTool", "CameraTool", "GCodeWriter", - "LocalFileStorage" + "LocalFileOutputDevice" ]) self._physics = None self._volume = None From a7780d9e42107b9778d55e9cc10ca6d0216fcf2c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 31 Jul 2015 17:07:33 +0200 Subject: [PATCH 20/26] Update all plugin metadata to specify API version --- plugins/CuraEngineBackend/__init__.py | 4 ++-- plugins/GCodeWriter/__init__.py | 1 - plugins/LayerView/__init__.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/CuraEngineBackend/__init__.py b/plugins/CuraEngineBackend/__init__.py index 0c9588b1e7..fc986c6f67 100644 --- a/plugins/CuraEngineBackend/__init__.py +++ b/plugins/CuraEngineBackend/__init__.py @@ -9,11 +9,11 @@ catalog = i18nCatalog("cura") def getMetaData(): return { - "type": "backend", "plugin": { "name": "CuraEngine Backend", "author": "Ultimaker", - "description": catalog.i18nc("CuraEngine backend plugin description", "Provides the link to the CuraEngine slicing backend") + "description": catalog.i18nc("CuraEngine backend plugin description", "Provides the link to the CuraEngine slicing backend"), + "api": 2 } } diff --git a/plugins/GCodeWriter/__init__.py b/plugins/GCodeWriter/__init__.py index 4f0b5dd82a..ecd63b02b5 100644 --- a/plugins/GCodeWriter/__init__.py +++ b/plugins/GCodeWriter/__init__.py @@ -8,7 +8,6 @@ catalog = i18nCatalog("cura") def getMetaData(): return { - "type": "mesh_writer", "plugin": { "name": "GCode Writer", "author": "Ultimaker", diff --git a/plugins/LayerView/__init__.py b/plugins/LayerView/__init__.py index eb3ba4cdbe..4bd9a61fb0 100644 --- a/plugins/LayerView/__init__.py +++ b/plugins/LayerView/__init__.py @@ -9,12 +9,12 @@ catalog = i18nCatalog("cura") def getMetaData(): return { - "type": "view", "plugin": { "name": "Layer View", "author": "Ultimaker", "version": "1.0", - "description": catalog.i18nc("Layer View plugin description", "Provides the Layer view.") + "description": catalog.i18nc("Layer View plugin description", "Provides the Layer view."), + "api": 2 }, "view": { "name": catalog.i18nc("Layers View mode", "Layers"), From 54f1ac7805a9512f496750f1f8f1f06bdcf25888 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Mon, 3 Aug 2015 13:20:17 +0200 Subject: [PATCH 21/26] Adds a version, author & manufacturer to the machine settings files --- resources/settings/fdmprinter.json | 2 ++ resources/settings/grr_neo.json | 1 + resources/settings/hephestos.json | 1 + resources/settings/hephestos_xl.json | 1 + resources/settings/prusa_i3.json | 1 + resources/settings/ultimaker2.json | 1 + resources/settings/ultimaker2_extended.json | 3 +++ resources/settings/ultimaker2_go.json | 3 +++ resources/settings/ultimaker_original.json | 1 + resources/settings/ultimaker_original_plus.json | 3 +++ resources/settings/witbox.json | 1 + 11 files changed, 18 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 9aae3d6dc4..a27fd4d675 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1,5 +1,7 @@ { "visible": false, + "version": 1, + "author": "other", "manufacturer": "other", "author": "other", diff --git a/resources/settings/grr_neo.json b/resources/settings/grr_neo.json index cb8898f2ff..66b1248017 100644 --- a/resources/settings/grr_neo.json +++ b/resources/settings/grr_neo.json @@ -1,5 +1,6 @@ { "id": "grr_neo", + "version": 1, "name": "German RepRap Neo", "manufacturer": "German RepRap", "author": "other", diff --git a/resources/settings/hephestos.json b/resources/settings/hephestos.json index 69af6b87ec..4158ddaf53 100644 --- a/resources/settings/hephestos.json +++ b/resources/settings/hephestos.json @@ -1,5 +1,6 @@ { "id": "hephestos", + "version": 1, "name": "BQ Prusa i3 Hephestos", "manufacturer": "BQ", "author": "other", diff --git a/resources/settings/hephestos_xl.json b/resources/settings/hephestos_xl.json index 9c4538ede0..74583d55d2 100644 --- a/resources/settings/hephestos_xl.json +++ b/resources/settings/hephestos_xl.json @@ -1,5 +1,6 @@ { "id": "hephestos_xl", + "version": 1, "name": "BQ Prusa i3 Hephestos XL", "manufacturer": "BQ", "author": "other", diff --git a/resources/settings/prusa_i3.json b/resources/settings/prusa_i3.json index af90a6f80f..e0ff59c073 100644 --- a/resources/settings/prusa_i3.json +++ b/resources/settings/prusa_i3.json @@ -1,5 +1,6 @@ { "id": "prusa_i3", + "version": 1, "name": "Prusa i3", "manufacturer": "Prusa", "author": "other", diff --git a/resources/settings/ultimaker2.json b/resources/settings/ultimaker2.json index f74b208cc8..6810a9a3d6 100644 --- a/resources/settings/ultimaker2.json +++ b/resources/settings/ultimaker2.json @@ -1,5 +1,6 @@ { "id": "ultimaker2", + "version": 1, "name": "Ultimaker 2", "manufacturer": "Ultimaker", "author": "Ultimaker", diff --git a/resources/settings/ultimaker2_extended.json b/resources/settings/ultimaker2_extended.json index b6156ec18e..ef4f2d5141 100644 --- a/resources/settings/ultimaker2_extended.json +++ b/resources/settings/ultimaker2_extended.json @@ -1,6 +1,9 @@ { "id": "ultimaker2_extended", + "version": 1, "name": "Ultimaker 2 Extended", + "manufacturer": "Ultimaker", + "author": "Ultimaker", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", "platform_texture": "Ultimaker2backplate.png", diff --git a/resources/settings/ultimaker2_go.json b/resources/settings/ultimaker2_go.json index 4d7a535553..708863f70d 100644 --- a/resources/settings/ultimaker2_go.json +++ b/resources/settings/ultimaker2_go.json @@ -1,6 +1,9 @@ { "id": "ultimaker2_go", + "version": 1, "name": "Ultimaker 2 Go", + "manufacturer": "Ultimaker", + "author": "Ultimaker", "icon": "icon_ultimaker2.png", "platform": "ultimaker2go_platform.obj", "platform_texture": "Ultimaker2backplate.png", diff --git a/resources/settings/ultimaker_original.json b/resources/settings/ultimaker_original.json index 14e9b9348a..81bb2bf178 100644 --- a/resources/settings/ultimaker_original.json +++ b/resources/settings/ultimaker_original.json @@ -1,5 +1,6 @@ { "id": "ultimaker_original", + "version": 1, "name": "Ultimaker Original", "manufacturer": "Ultimaker", "author": "Ultimaker", diff --git a/resources/settings/ultimaker_original_plus.json b/resources/settings/ultimaker_original_plus.json index 9e5cf6370d..73fdcc2370 100644 --- a/resources/settings/ultimaker_original_plus.json +++ b/resources/settings/ultimaker_original_plus.json @@ -1,6 +1,9 @@ { "id": "ultimaker_original_plus", + "version": 1, "name": "Ultimaker Original+", + "manufacturer": "Ultimaker", + "author": "Ultimaker", "icon": "icon_ultimaker.png", "platform": "ultimaker2_platform.obj", "platform_texture": "UltimakerPlusbackplate.png", diff --git a/resources/settings/witbox.json b/resources/settings/witbox.json index aee1030219..c79ee169b2 100644 --- a/resources/settings/witbox.json +++ b/resources/settings/witbox.json @@ -1,5 +1,6 @@ { "id": "bq_witbox", + "version": 1, "name": "BQ Witbox", "manufacturer": "BQ", "author": "other", From cb066686283d7ba062dbd909555675e348a3f581 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 3 Aug 2015 17:28:31 +0200 Subject: [PATCH 22/26] Use OutputDeviceManagerProxy for save menu and button --- resources/qml/Cura.qml | 6 +++--- resources/qml/SaveButton.qml | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f107121f34..637a4298eb 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -58,7 +58,7 @@ UM.MainWindow { text: "Save Selection to File"; enabled: UM.Selection.hasSelection; iconName: "document-save-as"; - onTriggered: devicesModel.requestWriteSelectionToDevice("local_file"); + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file"); } Menu { id: saveAllMenu @@ -70,8 +70,8 @@ UM.MainWindow { model: UM.OutputDevicesModel { id: devicesModel; } MenuItem { - text: model.description - onTriggered: devicesModel.requestWriteToDevice(model.id); + text: model.description; + onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id); } onObjectAdded: saveAllMenu.insertItem(index, object) onObjectRemoved: saveAllMenu.removeItem(object) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index f5a34b5fc2..ec2066713f 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -107,13 +107,13 @@ Rectangle { anchors.topMargin: UM.Theme.sizes.save_button_text_margin.height; anchors.left: parent.left anchors.leftMargin: UM.Theme.sizes.default_margin.width; - tooltip: devicesModel.activeDevice.description; + tooltip: UM.OutputDeviceManager.activeDeviceDescription; enabled: progress > 0.99 && base.activity == true width: infoBox.width/6*4.5 height: UM.Theme.sizes.save_button_save_to_button.height - text: devicesModel.activeDevice.short_description; + text: UM.OutputDeviceManager.activeDeviceShortDescription; style: ButtonStyle { background: Rectangle { @@ -128,7 +128,7 @@ Rectangle { } label: Item { } } - onClicked: devicesModel.requestWriteToDevice(devicesModel.activeDevice.id) + onClicked: UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice) } Button { @@ -143,7 +143,7 @@ Rectangle { width: infoBox.width/6*1.3 - UM.Theme.sizes.save_button_text_margin.height; height: UM.Theme.sizes.save_button_save_to_button.height - iconSource: UM.Theme.icons[devicesModel.activeDevice.icon_name]; + iconSource: UM.Theme.icons[UM.OutputDeviceManager.activeDeviceIconName]; style: ButtonStyle { background: Rectangle { @@ -188,10 +188,10 @@ Rectangle { MenuItem { text: model.description checkable: true; - checked: model.id == devicesModel.activeDevice.id; + checked: model.id == UM.OutputDeviceManager.activeDevice; exclusiveGroup: devicesMenuGroup; onTriggered: { - devicesModel.setActiveDevice(model.id); + UM.OutputDeviceManager.setActiveDevice(model.id); } } onObjectAdded: devicesMenu.insertItem(index, object) From 803b4fde8d552006eb230b06adc516fb0b7585ab Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 4 Aug 2015 10:30:34 +0200 Subject: [PATCH 23/26] Handle all uncaught exceptions through CrashHandler and gracefully fail if we have no QCoreApplication --- cura/CrashHandler.py | 12 +++++++++--- cura_app.py | 15 +++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 1df1ab773e..f6d389f518 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -3,10 +3,15 @@ import platform import traceback import webbrowser -from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR +from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, QCoreApplication from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QTextEdit -def show(): +def show(type, value, tb): + application = QCoreApplication.instance() + if not application: + traceback.print_exception(type, value, tb) + exit(1) + dialog = QDialog() dialog.setWindowTitle("Oops!") @@ -25,7 +30,7 @@ def show(): except: version = "Unknown" - trace = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) + trace = "".join(traceback.format_exception(type, value, tb)) crash_info = "Version: {0}\nPlatform: {1}\nQt: {2}\nPyQt: {3}\n\nException:\n{4}" crash_info = crash_info.format(version, platform.platform(), QT_VERSION_STR, PYQT_VERSION_STR, trace) @@ -39,3 +44,4 @@ def show(): buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues")) dialog.exec_() + exit(1) diff --git a/cura_app.py b/cura_app.py index 35ea0375b6..4bd49dc081 100755 --- a/cura_app.py +++ b/cura_app.py @@ -3,12 +3,15 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -try: - import cura.CuraApplication +import sys - app = cura.CuraApplication.CuraApplication.getInstance() - app.run() -except Exception as e: +def exceptHook(type, value, traceback): import cura.CrashHandler - cura.CrashHandler.show() + cura.CrashHandler.show(type, value, traceback) +sys.excepthook = exceptHook + +import cura.CuraApplication + +app = cura.CuraApplication.CuraApplication.getInstance() +app.run() From 543c58d57abb55101b2b81648e31262fe951610e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 4 Aug 2015 15:38:19 +0200 Subject: [PATCH 24/26] Use sys.exit() instead of exit() Apparently there is a difference... --- cura/CrashHandler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index f6d389f518..1771a5c548 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -10,7 +10,7 @@ def show(type, value, tb): application = QCoreApplication.instance() if not application: traceback.print_exception(type, value, tb) - exit(1) + sys.exit(1) dialog = QDialog() dialog.setWindowTitle("Oops!") @@ -44,4 +44,4 @@ def show(type, value, tb): buttons.helpRequested.connect(lambda: webbrowser.open("http://github.com/Ultimaker/Cura/issues")) dialog.exec_() - exit(1) + sys.exit(1) From 26aa78c3b9e369aa4f24f4dd0e8bf01d78c5d359 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 4 Aug 2015 15:39:58 +0200 Subject: [PATCH 25/26] Fix OSXRemovableDevicePlugin so it works properly on OSX --- .../RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py index c50443cb92..e02e0d65b6 100644 --- a/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/OSXRemovableDrivePlugin.py @@ -13,8 +13,8 @@ import os import plistlib ## Support for removable devices on Mac OSX -class OSXRemovableDrives(RemovableDrivePlugin.RemovableDrivePlugin): - def run(self): +class OSXRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin): + def checkRemovableDrives(self): drives = {} p = subprocess.Popen(["system_profiler", "SPUSBDataType", "-xml"], stdout=subprocess.PIPE) plist = plistlib.loads(p.communicate()[0]) @@ -41,6 +41,8 @@ class OSXRemovableDrives(RemovableDrivePlugin.RemovableDrivePlugin): volume = vol["mount_point"] drives[volume] = os.path.basename(volume) + return drives + def performEjectDevice(self, device): p = subprocess.Popen(["diskutil", "eject", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = p.communicate() From 9c625a7c41d48cd8641644154937240fd676920d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 4 Aug 2015 15:40:37 +0200 Subject: [PATCH 26/26] Remove obsolete __init__.py file --- src/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/__init__.py diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29bb2..0000000000