From d2e97154093657b0e75d695648bd3e5dd87dd8c1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:02:29 +0200 Subject: [PATCH 01/10] Add availableConfiguration property to the output model CURA-6732 --- .../Models/PrinterOutputModel.py | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index 13fe85e674..105ead96f5 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -7,6 +7,7 @@ from UM.Math.Vector import Vector from cura.PrinterOutput.Peripheral import Peripheral from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel from cura.PrinterOutput.Models.ExtruderOutputModel import ExtruderOutputModel +from UM.Logger import Logger if TYPE_CHECKING: from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel @@ -50,6 +51,8 @@ class PrinterOutputModel(QObject): self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in self._extruders] + self._available_printer_configurations = [] # type: List[PrinterConfigurationModel] + self._camera_url = QUrl() # type: QUrl @pyqtProperty(str, constant = True) @@ -290,7 +293,7 @@ class PrinterOutputModel(QObject): def _onControllerCanUpdateFirmwareChanged(self) -> None: self.canUpdateFirmwareChanged.emit() - # Returns the configuration (material, variant and buildplate) of the current printer + # Returns the active configuration (material, variant and buildplate) of the current printer @pyqtProperty(QObject, notify = configurationChanged) def printerConfiguration(self) -> Optional[PrinterConfigurationModel]: if self._printer_configuration.isValid(): @@ -309,4 +312,26 @@ class PrinterOutputModel(QObject): def removePeripheral(self, peripheral: Peripheral) -> None: self._peripherals.remove(peripheral) - self.peripheralsChanged.emit() \ No newline at end of file + self.peripheralsChanged.emit() + + availableConfigurationsChanged = pyqtSignal() + + @pyqtProperty("QVariantList", notify = availableConfigurationsChanged) + def availableConfigurations(self) -> List[PrinterConfigurationModel]: + return self._available_printer_configurations + + def addAvailableConfiguration(self, new_configuration: PrinterConfigurationModel) -> None: + self._available_printer_configurations.append(new_configuration) + self.availableConfigurationsChanged.emit() + + def removeAvailableConfiguration(self, config_to_remove: PrinterConfigurationModel) -> None: + try: + self._available_printer_configurations.remove(config_to_remove) + except ValueError: + Logger.log("w", "Unable to remove configuration that isn't in the list of available configurations") + else: + self.availableConfigurationsChanged.emit() + + def setAvailableConfigurations(self, new_configurations: List[PrinterConfigurationModel]) -> None: + self._available_printer_configurations = new_configurations + self.availableConfigurationsChanged.emit() From d1720db5ad92d9832cf4706d87caf96550c90fef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:07:44 +0200 Subject: [PATCH 02/10] Ensure that the available configurations are also used in the uniqueConfigurations CURA-6372 --- cura/PrinterOutput/PrinterOutputDevice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cura/PrinterOutput/PrinterOutputDevice.py b/cura/PrinterOutput/PrinterOutputDevice.py index d4a37b3d68..66a507ab9a 100644 --- a/cura/PrinterOutput/PrinterOutputDevice.py +++ b/cura/PrinterOutput/PrinterOutputDevice.py @@ -220,10 +220,12 @@ class PrinterOutputDevice(QObject, OutputDevice): return self._unique_configurations def _updateUniqueConfigurations(self) -> None: - self._unique_configurations = sorted( - {printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None}, - key=lambda config: config.printerType, - ) + all_configurations = set() + for printer in self._printers: + if printer.printerConfiguration is not None: + all_configurations.add(printer.printerConfiguration) + all_configurations.update(printer.availableConfigurations) + self._unique_configurations = sorted(all_configurations, key = lambda config: config.printerType) self.uniqueConfigurationsChanged.emit() # Returns the unique configurations of the printers within this output device @@ -234,6 +236,7 @@ class PrinterOutputDevice(QObject, OutputDevice): def _onPrintersChanged(self) -> None: for printer in self._printers: printer.configurationChanged.connect(self._updateUniqueConfigurations) + printer.availableConfigurationsChanged.connect(self._updateUniqueConfigurations) # At this point there may be non-updated configurations self._updateUniqueConfigurations() From 561a3e53e5e60230c8a848debac0cea74807767a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:09:39 +0200 Subject: [PATCH 03/10] Only add available configuration if it wasn't already in the list CURA-6732 --- cura/PrinterOutput/Models/PrinterOutputModel.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index 105ead96f5..ccdbb500b9 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -321,8 +321,9 @@ class PrinterOutputModel(QObject): return self._available_printer_configurations def addAvailableConfiguration(self, new_configuration: PrinterConfigurationModel) -> None: - self._available_printer_configurations.append(new_configuration) - self.availableConfigurationsChanged.emit() + if new_configuration not in self._available_printer_configurations: + self._available_printer_configurations.append(new_configuration) + self.availableConfigurationsChanged.emit() def removeAvailableConfiguration(self, config_to_remove: PrinterConfigurationModel) -> None: try: From 73c6676673d6a8f9411141277c99377ad96efcb1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:17:40 +0200 Subject: [PATCH 04/10] Added missing test for camera URL --- tests/PrinterOutput/Models/TestPrinterOutputModel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/PrinterOutput/Models/TestPrinterOutputModel.py b/tests/PrinterOutput/Models/TestPrinterOutputModel.py index 3fdb61adbd..577a27bd6f 100644 --- a/tests/PrinterOutput/Models/TestPrinterOutputModel.py +++ b/tests/PrinterOutput/Models/TestPrinterOutputModel.py @@ -10,6 +10,7 @@ from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel test_validate_data_get_set = [ {"attribute": "name", "value": "YAY"}, {"attribute": "targetBedTemperature", "value": 192}, + {"attribute": "cameraUrl", "value": "YAY!"} ] test_validate_data_get_update = [ @@ -22,6 +23,7 @@ test_validate_data_get_update = [ {"attribute": "targetBedTemperature", "value": 9001}, {"attribute": "activePrintJob", "value": PrintJobOutputModel(MagicMock())}, {"attribute": "state", "value": "BEEPBOOP"}, + ] From 34c3a0474464f621fa5d35e7f5a9bbe09f4503ac Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:24:19 +0200 Subject: [PATCH 05/10] Added missing tests for peripheral Not part of the ticket, but I'm boyscouting this. --- .../Models/PrinterOutputModel.py | 2 +- .../Models/TestPrinterOutputModel.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index ccdbb500b9..3927da0313 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -304,7 +304,7 @@ class PrinterOutputModel(QObject): @pyqtProperty(str, notify = peripheralsChanged) def peripherals(self) -> str: - return ", ".join(*[peripheral.name for peripheral in self._peripherals]) + return ", ".join([peripheral.name for peripheral in self._peripherals]) def addPeripheral(self, peripheral: Peripheral) -> None: self._peripherals.append(peripheral) diff --git a/tests/PrinterOutput/Models/TestPrinterOutputModel.py b/tests/PrinterOutput/Models/TestPrinterOutputModel.py index 577a27bd6f..8136e670b7 100644 --- a/tests/PrinterOutput/Models/TestPrinterOutputModel.py +++ b/tests/PrinterOutput/Models/TestPrinterOutputModel.py @@ -6,6 +6,7 @@ import pytest from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel +from cura.PrinterOutput.Peripheral import Peripheral test_validate_data_get_set = [ {"attribute": "name", "value": "YAY"}, @@ -81,3 +82,24 @@ def test_getAndUpdate(data): getattr(model, "update" + attribute)(data["value"]) # The signal should not fire again assert signal.emit.call_count == 1 + + +def test_peripherals(): + model = PrinterOutputModel(MagicMock()) + model.peripheralsChanged = MagicMock() + + peripheral = MagicMock(spec=Peripheral) + peripheral.name = "test" + peripheral2 = MagicMock(spec=Peripheral) + peripheral2.name = "test2" + + model.addPeripheral(peripheral) + assert model.peripheralsChanged.emit.call_count == 1 + model.addPeripheral(peripheral2) + assert model.peripheralsChanged.emit.call_count == 2 + + assert model.peripherals == "test, test2" + + model.removePeripheral(peripheral) + assert model.peripheralsChanged.emit.call_count == 3 + assert model.peripherals == "test2" From 89260891e6cc156358a2e0ce5fb8e0a2e31cfb74 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:32:33 +0200 Subject: [PATCH 06/10] Add tests for the available configurations CURA-6732 --- .../Models/TestPrinterOutputModel.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/PrinterOutput/Models/TestPrinterOutputModel.py b/tests/PrinterOutput/Models/TestPrinterOutputModel.py index 8136e670b7..9848e0a5fa 100644 --- a/tests/PrinterOutput/Models/TestPrinterOutputModel.py +++ b/tests/PrinterOutput/Models/TestPrinterOutputModel.py @@ -5,6 +5,7 @@ from unittest.mock import MagicMock import pytest from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel +from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.Peripheral import Peripheral @@ -103,3 +104,46 @@ def test_peripherals(): model.removePeripheral(peripheral) assert model.peripheralsChanged.emit.call_count == 3 assert model.peripherals == "test2" + + +def test_availableConfigurations_addConfiguration(): + model = PrinterOutputModel(MagicMock()) + + configuration = MagicMock(spec = PrinterConfigurationModel) + + model.addAvailableConfiguration(configuration) + assert model.availableConfigurations == [configuration] + + +def test_availableConfigurations_addConfigTwice(): + model = PrinterOutputModel(MagicMock()) + + configuration = MagicMock(spec=PrinterConfigurationModel) + + model.setAvailableConfigurations([configuration]) + assert model.availableConfigurations == [configuration] + + # Adding it again should not have any effect + model.addAvailableConfiguration(configuration) + assert model.availableConfigurations == [configuration] + + +def test_availableConfigurations_removeConfig(): + model = PrinterOutputModel(MagicMock()) + + configuration = MagicMock(spec=PrinterConfigurationModel) + + model.addAvailableConfiguration(configuration) + model.removeAvailableConfiguration(configuration) + assert model.availableConfigurations == [] + + +def test_removeAlreadyRemovedConfiguration(): + model = PrinterOutputModel(MagicMock()) + + configuration = MagicMock(spec=PrinterConfigurationModel) + model.availableConfigurationsChanged = MagicMock() + model.removeAvailableConfiguration(configuration) + assert model.availableConfigurationsChanged.emit.call_count == 0 + assert model.availableConfigurations == [] + From 1e2f5ddecd91433d0e62e8f009edbc2122173f43 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 14:53:13 +0200 Subject: [PATCH 07/10] Add test for unique printer configuration CURA-6732 --- .../PrinterOutput/TestPrinterOutputDevice.py | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/PrinterOutput/TestPrinterOutputDevice.py b/tests/PrinterOutput/TestPrinterOutputDevice.py index 4c12a34859..e0415295c1 100644 --- a/tests/PrinterOutput/TestPrinterOutputDevice.py +++ b/tests/PrinterOutput/TestPrinterOutputDevice.py @@ -1,6 +1,10 @@ from unittest.mock import MagicMock import pytest +from unittest.mock import patch + +from cura.PrinterOutput.Models.PrinterConfigurationModel import PrinterConfigurationModel +from cura.PrinterOutput.Models.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice test_validate_data_get_set = [ @@ -8,10 +12,15 @@ test_validate_data_get_set = [ {"attribute": "connectionState", "value": 1}, ] +@pytest.fixture() +def printer_output_device(): + with patch("UM.Application.Application.getInstance"): + return PrinterOutputDevice("whatever") + @pytest.mark.parametrize("data", test_validate_data_get_set) -def test_getAndSet(data): - model = PrinterOutputDevice("whatever") +def test_getAndSet(data, printer_output_device): + model = printer_output_device # Convert the first letter into a capital attribute = list(data["attribute"]) @@ -35,3 +44,21 @@ def test_getAndSet(data): getattr(model, "set" + attribute)(data["value"]) # The signal should not fire again assert signal.emit.call_count == 1 + + +def test_uniqueConfigurations(printer_output_device): + printer = PrinterOutputModel(MagicMock()) + # Add a printer and fire the signal that ensures they get hooked up correctly. + printer_output_device._printers = [printer] + printer_output_device._onPrintersChanged() + + assert printer_output_device.uniqueConfigurations == [] + configuration = PrinterConfigurationModel() + printer.addAvailableConfiguration(configuration) + + assert printer_output_device.uniqueConfigurations == [configuration] + + # Once the type of printer is set, it's active configuration counts as being set. + # In that case, that should also be added to the list of available configurations + printer.updateType("blarg!") + assert printer_output_device.uniqueConfigurations == [configuration, printer.printerConfiguration] \ No newline at end of file From 5cb485d4d39a91885f743947c2e04a205e1c11bc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 15:07:57 +0200 Subject: [PATCH 08/10] Only emit uniqueConfigurationsChanged signal if the set changed CURA-6732 --- cura/PrinterOutput/PrinterOutputDevice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutput/PrinterOutputDevice.py b/cura/PrinterOutput/PrinterOutputDevice.py index 66a507ab9a..bb4f9e79fb 100644 --- a/cura/PrinterOutput/PrinterOutputDevice.py +++ b/cura/PrinterOutput/PrinterOutputDevice.py @@ -225,8 +225,10 @@ class PrinterOutputDevice(QObject, OutputDevice): if printer.printerConfiguration is not None: all_configurations.add(printer.printerConfiguration) all_configurations.update(printer.availableConfigurations) - self._unique_configurations = sorted(all_configurations, key = lambda config: config.printerType) - self.uniqueConfigurationsChanged.emit() + new_configurations = sorted(all_configurations, key = lambda config: config.printerType) + if new_configurations != self._unique_configurations: + self._unique_configurations = new_configurations + self.uniqueConfigurationsChanged.emit() # Returns the unique configurations of the printers within this output device @pyqtProperty("QStringList", notify = uniqueConfigurationsChanged) From a1ca705de99e384362579fdf4798ebc6c9eac718 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 15:10:17 +0200 Subject: [PATCH 09/10] Add documentation CURA-6732 --- cura/PrinterOutput/Models/PrinterOutputModel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index 3927da0313..d04fccca1b 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -316,6 +316,8 @@ class PrinterOutputModel(QObject): availableConfigurationsChanged = pyqtSignal() + # The availableConfigurations are configuration options that a printer can switch to, but doesn't currently have + # active (eg; Automatic tool changes, material loaders, etc). @pyqtProperty("QVariantList", notify = availableConfigurationsChanged) def availableConfigurations(self) -> List[PrinterConfigurationModel]: return self._available_printer_configurations From bc4b2a596a4e4fbeaafea05a95a4b464c0866bed Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Aug 2019 15:58:12 +0200 Subject: [PATCH 10/10] Rename _printer_configuration to _active_printer_configuration CURA-6732 --- cura/PrinterOutput/Models/PrinterOutputModel.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/PrinterOutput/Models/PrinterOutputModel.py b/cura/PrinterOutput/Models/PrinterOutputModel.py index d04fccca1b..b8bea999c7 100644 --- a/cura/PrinterOutput/Models/PrinterOutputModel.py +++ b/cura/PrinterOutput/Models/PrinterOutputModel.py @@ -38,7 +38,7 @@ class PrinterOutputModel(QObject): self._controller = output_controller self._controller.canUpdateFirmwareChanged.connect(self._onControllerCanUpdateFirmwareChanged) self._extruders = [ExtruderOutputModel(printer = self, position = i) for i in range(number_of_extruders)] - self._printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer + self._active_printer_configuration = PrinterConfigurationModel() # Indicates the current configuration setup in this printer self._head_position = Vector(0, 0, 0) self._active_print_job = None # type: Optional[PrintJobOutputModel] self._firmware_version = firmware_version @@ -48,8 +48,8 @@ class PrinterOutputModel(QObject): self._buildplate = "" self._peripherals = [] # type: List[Peripheral] - self._printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in - self._extruders] + self._active_printer_configuration.extruderConfigurations = [extruder.extruderConfiguration for extruder in + self._extruders] self._available_printer_configurations = [] # type: List[PrinterConfigurationModel] @@ -84,7 +84,7 @@ class PrinterOutputModel(QObject): def updateType(self, printer_type: str) -> None: if self._printer_type != printer_type: self._printer_type = printer_type - self._printer_configuration.printerType = self._printer_type + self._active_printer_configuration.printerType = self._printer_type self.typeChanged.emit() self.configurationChanged.emit() @@ -95,7 +95,7 @@ class PrinterOutputModel(QObject): def updateBuildplate(self, buildplate: str) -> None: if self._buildplate != buildplate: self._buildplate = buildplate - self._printer_configuration.buildplateConfiguration = self._buildplate + self._active_printer_configuration.buildplateConfiguration = self._buildplate self.buildplateChanged.emit() self.configurationChanged.emit() @@ -296,8 +296,8 @@ class PrinterOutputModel(QObject): # Returns the active configuration (material, variant and buildplate) of the current printer @pyqtProperty(QObject, notify = configurationChanged) def printerConfiguration(self) -> Optional[PrinterConfigurationModel]: - if self._printer_configuration.isValid(): - return self._printer_configuration + if self._active_printer_configuration.isValid(): + return self._active_printer_configuration return None peripheralsChanged = pyqtSignal()