diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 3d2a1c3f37..4e4c442b4c 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,72 +1,72 @@ -# Copyright (c) 2017 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -import os.path -from UM.Application import Application -from cura.Stages.CuraStage import CuraStage - - -## Stage for monitoring a 3D printing while it's printing. -class MonitorStage(CuraStage): - - def __init__(self, parent = None): - super().__init__(parent) - - # Wait until QML engine is created, otherwise creating the new QML components will fail - Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) - self._printer_output_device = None - - self._active_print_job = None - self._active_printer = None - - def _setActivePrintJob(self, print_job): - if self._active_print_job != print_job: - self._active_print_job = print_job - - def _setActivePrinter(self, printer): - if self._active_printer != printer: - if self._active_printer: - self._active_printer.activePrintJobChanged.disconnect(self._onActivePrintJobChanged) - self._active_printer = printer - if self._active_printer: - self._setActivePrintJob(self._active_printer.activePrintJob) - # Jobs might change, so we need to listen to it's changes. - self._active_printer.activePrintJobChanged.connect(self._onActivePrintJobChanged) - else: - self._setActivePrintJob(None) - - def _onActivePrintJobChanged(self): - self._setActivePrintJob(self._active_printer.activePrintJob) - - def _onActivePrinterChanged(self): - self._setActivePrinter(self._printer_output_device.activePrinter) - - def _onOutputDevicesChanged(self): - try: - # We assume that you are monitoring the device with the highest priority. - new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0] - if new_output_device != self._printer_output_device: - if self._printer_output_device: - try: - self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged) - except TypeError: - # Ignore stupid "Not connected" errors. - pass - - self._printer_output_device = new_output_device - - self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged) - self._setActivePrinter(self._printer_output_device.activePrinter) - except IndexError: - pass - - def _onEngineCreated(self): - # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) - Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) - self._onOutputDevicesChanged() - - plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId()) - if plugin_path is not None: - menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml") - main_component_path = os.path.join(plugin_path, "MonitorMain.qml") - self.addDisplayComponent("menu", menu_component_path) - self.addDisplayComponent("main", main_component_path) +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import os.path +from UM.Application import Application +from cura.Stages.CuraStage import CuraStage + + +class MonitorStage(CuraStage): + """Stage for monitoring a 3D printing while it's printing.""" + + def __init__(self, parent = None): + super().__init__(parent) + + # Wait until QML engine is created, otherwise creating the new QML components will fail + Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + self._printer_output_device = None + + self._active_print_job = None + self._active_printer = None + + def _setActivePrintJob(self, print_job): + if self._active_print_job != print_job: + self._active_print_job = print_job + + def _setActivePrinter(self, printer): + if self._active_printer != printer: + if self._active_printer: + self._active_printer.activePrintJobChanged.disconnect(self._onActivePrintJobChanged) + self._active_printer = printer + if self._active_printer: + self._setActivePrintJob(self._active_printer.activePrintJob) + # Jobs might change, so we need to listen to it's changes. + self._active_printer.activePrintJobChanged.connect(self._onActivePrintJobChanged) + else: + self._setActivePrintJob(None) + + def _onActivePrintJobChanged(self): + self._setActivePrintJob(self._active_printer.activePrintJob) + + def _onActivePrinterChanged(self): + self._setActivePrinter(self._printer_output_device.activePrinter) + + def _onOutputDevicesChanged(self): + try: + # We assume that you are monitoring the device with the highest priority. + new_output_device = Application.getInstance().getMachineManager().printerOutputDevices[0] + if new_output_device != self._printer_output_device: + if self._printer_output_device: + try: + self._printer_output_device.printersChanged.disconnect(self._onActivePrinterChanged) + except TypeError: + # Ignore stupid "Not connected" errors. + pass + + self._printer_output_device = new_output_device + + self._printer_output_device.printersChanged.connect(self._onActivePrinterChanged) + self._setActivePrinter(self._printer_output_device.activePrinter) + except IndexError: + pass + + def _onEngineCreated(self): + # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) + Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) + self._onOutputDevicesChanged() + + plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId()) + if plugin_path is not None: + menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml") + main_component_path = os.path.join(plugin_path, "MonitorMain.qml") + self.addDisplayComponent("menu", menu_component_path) + self.addDisplayComponent("main", main_component_path) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py index 78da0512f7..c44e4a4df3 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -15,9 +15,11 @@ from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherit from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator -## The per object setting visibility handler ensures that only setting -# definitions that have a matching instance Container are returned as visible. class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler.SettingVisibilityHandler): + """The per object setting visibility handler ensures that only setting + + definitions that have a matching instance Container are returned as visible. + """ def __init__(self, parent = None, *args, **kwargs): super().__init__(parent = parent, *args, **kwargs) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index 4f0d90a8e3..d23096c0a3 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -12,9 +12,11 @@ from UM.Settings.SettingInstance import SettingInstance from UM.Event import Event -## This tool allows the user to add & change settings per node in the scene. -# The settings per object are kept in a ContainerStack, which is linked to a node by decorator. class PerObjectSettingsTool(Tool): + """This tool allows the user to add & change settings per node in the scene. + + The settings per object are kept in a ContainerStack, which is linked to a node by decorator. + """ def __init__(self): super().__init__() self._model = None @@ -48,26 +50,31 @@ class PerObjectSettingsTool(Tool): except AttributeError: return "" - ## Gets the active extruder of the currently selected object. - # - # \return The active extruder of the currently selected object. def getSelectedActiveExtruder(self): + """Gets the active extruder of the currently selected object. + + :return: The active extruder of the currently selected object. + """ + selected_object = Selection.getSelectedObject(0) return selected_object.callDecoration("getActiveExtruder") - ## Changes the active extruder of the currently selected object. - # - # \param extruder_stack_id The ID of the extruder to print the currently - # selected object with. def setSelectedActiveExtruder(self, extruder_stack_id): + """Changes the active extruder of the currently selected object. + + :param extruder_stack_id: The ID of the extruder to print the currently + selected object with. + """ + selected_object = Selection.getSelectedObject(0) stack = selected_object.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. if not stack: selected_object.addDecorator(SettingOverrideDecorator()) selected_object.callDecoration("setActiveExtruder", extruder_stack_id) - ## Returns True when the mesh_type was changed, False when current mesh_type == mesh_type def setMeshType(self, mesh_type: str) -> bool: + """Returns True when the mesh_type was changed, False when current mesh_type == mesh_type""" + old_mesh_type = self.getMeshType() if old_mesh_type == mesh_type: return False diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 9bf8062ffd..c1135640dd 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -27,9 +27,8 @@ if TYPE_CHECKING: from .Script import Script -## The post processing plugin is an Extension type plugin that enables pre-written scripts to post process generated -# g-code files. class PostProcessingPlugin(QObject, Extension): + """Extension type plugin that enables pre-written scripts to post process g-code files.""" def __init__(self, parent = None) -> None: QObject.__init__(self, parent) Extension.__init__(self) @@ -69,8 +68,9 @@ class PostProcessingPlugin(QObject, Extension): except IndexError: return "" - ## Execute all post-processing scripts on the gcode. def execute(self, output_device) -> None: + """Execute all post-processing scripts on the gcode.""" + scene = Application.getInstance().getController().getScene() # If the scene does not have a gcode, do nothing if not hasattr(scene, "gcode_dict"): @@ -119,9 +119,10 @@ class PostProcessingPlugin(QObject, Extension): self.selectedIndexChanged.emit() #Ensure that settings are updated self._propertyChanged() - ## Remove a script from the active script list by index. @pyqtSlot(int) def removeScriptByIndex(self, index: int) -> None: + """Remove a script from the active script list by index.""" + self._script_list.pop(index) if len(self._script_list) - 1 < self._selected_script_index: self._selected_script_index = len(self._script_list) - 1 @@ -129,10 +130,12 @@ class PostProcessingPlugin(QObject, Extension): self.selectedIndexChanged.emit() # Ensure that settings are updated self._propertyChanged() - ## Load all scripts from all paths where scripts can be found. - # - # This should probably only be done on init. def loadAllScripts(self) -> None: + """Load all scripts from all paths where scripts can be found. + + This should probably only be done on init. + """ + if self._loaded_scripts: # Already loaded. return @@ -152,10 +155,12 @@ class PostProcessingPlugin(QObject, Extension): self.loadScripts(path) - ## Load all scripts from provided path. - # This should probably only be done on init. - # \param path Path to check for scripts. def loadScripts(self, path: str) -> None: + """Load all scripts from provided path. + + This should probably only be done on init. + :param path: Path to check for scripts. + """ if ApplicationMetadata.IsEnterpriseVersion: # Delete all __pycache__ not in installation folder, as it may present a security risk. @@ -173,8 +178,8 @@ class PostProcessingPlugin(QObject, Extension): if not is_in_installation_path: TrustBasics.removeCached(path) - ## Load all scripts in the scripts folders scripts = pkgutil.iter_modules(path = [path]) + """Load all scripts in the scripts folders""" for loader, script_name, ispkg in scripts: # Iterate over all scripts. if script_name not in sys.modules: @@ -278,9 +283,8 @@ class PostProcessingPlugin(QObject, Extension): self.scriptListChanged.emit() self._propertyChanged() - ## When the global container stack is changed, swap out the list of active - # scripts. def _onGlobalContainerStackChanged(self) -> None: + """When the global container stack is changed, swap out the list of active scripts.""" if self._global_container_stack: self._global_container_stack.metaDataChanged.disconnect(self._restoreScriptInforFromMetadata) @@ -323,8 +327,12 @@ class PostProcessingPlugin(QObject, Extension): # We do want to listen to other events. self._global_container_stack.metaDataChanged.connect(self._restoreScriptInforFromMetadata) - ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self) -> None: + """Creates the view used by show popup. + + The view is saved because of the fairly aggressive garbage collection. + """ + Logger.log("d", "Creating post processing plugin view.") self.loadAllScripts() @@ -340,8 +348,9 @@ class PostProcessingPlugin(QObject, Extension): # Create the save button component CuraApplication.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton")) - ## Show the (GUI) popup of the post processing plugin. def showPopup(self) -> None: + """Show the (GUI) popup of the post processing plugin.""" + if self._view is None: self._createView() if self._view is None: @@ -349,11 +358,13 @@ class PostProcessingPlugin(QObject, Extension): return self._view.show() - ## Property changed: trigger re-slice - # To do this we use the global container stack propertyChanged. - # Re-slicing is necessary for setting changes in this plugin, because the changes - # are applied only once per "fresh" gcode def _propertyChanged(self) -> None: + """Property changed: trigger re-slice + + To do this we use the global container stack propertyChanged. + Re-slicing is necessary for setting changes in this plugin, because the changes + are applied only once per "fresh" gcode + """ global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is not None: global_container_stack.propertyChanged.emit("post_processing_plugin", "value") diff --git a/plugins/PostProcessingPlugin/Script.py b/plugins/PostProcessingPlugin/Script.py index e502f107f9..d15b209478 100644 --- a/plugins/PostProcessingPlugin/Script.py +++ b/plugins/PostProcessingPlugin/Script.py @@ -23,9 +23,10 @@ if TYPE_CHECKING: from UM.Settings.Interfaces import DefinitionContainerInterface -## Base class for scripts. All scripts should inherit the script class. @signalemitter class Script: + """Base class for scripts. All scripts should inherit the script class.""" + def __init__(self) -> None: super().__init__() self._stack = None # type: Optional[ContainerStack] @@ -78,13 +79,15 @@ class Script: if global_container_stack is not None: global_container_stack.propertyChanged.emit(key, property_name) - ## Needs to return a dict that can be used to construct a settingcategory file. - # See the example script for an example. - # It follows the same style / guides as the Uranium settings. - # Scripts can either override getSettingData directly, or use getSettingDataString - # to return a string that will be parsed as json. The latter has the benefit over - # returning a dict in that the order of settings is maintained. def getSettingData(self) -> Dict[str, Any]: + """Needs to return a dict that can be used to construct a settingcategory file. + + See the example script for an example. + It follows the same style / guides as the Uranium settings. + Scripts can either override getSettingData directly, or use getSettingDataString + to return a string that will be parsed as json. The latter has the benefit over + returning a dict in that the order of settings is maintained. + """ setting_data_as_string = self.getSettingDataString() setting_data = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict) return setting_data @@ -104,15 +107,18 @@ class Script: return self._stack.getId() return None - ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key: str) -> Any: + """Convenience function that retrieves value of a setting from the stack.""" + if self._stack is not None: return self._stack.getProperty(key, "value") return None - ## Convenience function that finds the value in a line of g-code. - # When requesting key = x from line "G1 X100" the value 100 is returned. def getValue(self, line: str, key: str, default = None) -> Any: + """Convenience function that finds the value in a line of g-code. + + When requesting key = x from line "G1 X100" the value 100 is returned. + """ if not key in line or (';' in line and line.find(key) > line.find(';')): return default sub_part = line[line.find(key) + 1:] @@ -127,20 +133,23 @@ class Script: except ValueError: #Not a number at all. return default - ## Convenience function to produce a line of g-code. - # - # You can put in an original g-code line and it'll re-use all the values - # in that line. - # All other keyword parameters are put in the result in g-code's format. - # For instance, if you put ``G=1`` in the parameters, it will output - # ``G1``. If you put ``G=1, X=100`` in the parameters, it will output - # ``G1 X100``. The parameters G and M will always be put first. The - # parameters T and S will be put second (or first if there is no G or M). - # The rest of the parameters will be put in arbitrary order. - # \param line The original g-code line that must be modified. If not - # provided, an entirely new g-code line will be produced. - # \return A line of g-code with the desired parameters filled in. def putValue(self, line: str = "", **kwargs) -> str: + """Convenience function to produce a line of g-code. + + You can put in an original g-code line and it'll re-use all the values + in that line. + All other keyword parameters are put in the result in g-code's format. + For instance, if you put ``G=1`` in the parameters, it will output + ``G1``. If you put ``G=1, X=100`` in the parameters, it will output + ``G1 X100``. The parameters G and M will always be put first. The + parameters T and S will be put second (or first if there is no G or M). + The rest of the parameters will be put in arbitrary order. + + :param line: The original g-code line that must be modified. If not + provided, an entirely new g-code line will be produced. + :return: A line of g-code with the desired parameters filled in. + """ + #Strip the comment. comment = "" if ";" in line: @@ -179,7 +188,9 @@ class Script: return result - ## This is called when the script is executed. - # It gets a list of g-code strings and needs to return a (modified) list. def execute(self, data: List[str]) -> List[str]: + """This is called when the script is executed. + + It gets a list of g-code strings and needs to return a (modified) list. + """ raise NotImplementedError() diff --git a/plugins/PostProcessingPlugin/scripts/FilamentChange.py b/plugins/PostProcessingPlugin/scripts/FilamentChange.py index 943ca30f2e..351b985d49 100644 --- a/plugins/PostProcessingPlugin/scripts/FilamentChange.py +++ b/plugins/PostProcessingPlugin/scripts/FilamentChange.py @@ -63,10 +63,12 @@ class FilamentChange(Script): } }""" - ## Inserts the filament change g-code at specific layer numbers. - # \param data A list of layers of g-code. - # \return A similar list, with filament change commands inserted. def execute(self, data: List[str]): + """Inserts the filament change g-code at specific layer numbers. + + :param data: A list of layers of g-code. + :return: A similar list, with filament change commands inserted. + """ layer_nums = self.getSettingValueByKey("layer_number") initial_retract = self.getSettingValueByKey("initial_retract") later_retract = self.getSettingValueByKey("later_retract") diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index 1ba8b8213b..766677af2a 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -134,9 +134,8 @@ class PauseAtHeight(Script): } }""" - ## Get the X and Y values for a layer (will be used to get X and Y of the - # layer after the pause). def getNextXY(self, layer: str) -> Tuple[float, float]: + """Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause).""" lines = layer.split("\n") for line in lines: if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None: @@ -145,10 +144,12 @@ class PauseAtHeight(Script): return x, y return 0, 0 - ## Inserts the pause commands. - # \param data: List of layers. - # \return New list of layers. def execute(self, data: List[str]) -> List[str]: + """Inserts the pause commands. + + :param data: List of layers. + :return: New list of layers. + """ pause_at = self.getSettingValueByKey("pause_at") pause_height = self.getSettingValueByKey("pause_height") pause_layer = self.getSettingValueByKey("pause_layer") diff --git a/plugins/PostProcessingPlugin/scripts/RetractContinue.py b/plugins/PostProcessingPlugin/scripts/RetractContinue.py index e437439287..3e095bd395 100644 --- a/plugins/PostProcessingPlugin/scripts/RetractContinue.py +++ b/plugins/PostProcessingPlugin/scripts/RetractContinue.py @@ -5,8 +5,10 @@ import math from ..Script import Script -## Continues retracting during all travel moves. + class RetractContinue(Script): + """Continues retracting during all travel moves.""" + def getSettingDataString(self): return """{ "name": "Retract Continue", diff --git a/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py b/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py index 68d697e470..b601a037da 100644 --- a/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py +++ b/plugins/PostProcessingPlugin/scripts/SearchAndReplace.py @@ -5,11 +5,14 @@ import re #To perform the search and replace. from ..Script import Script -## Performs a search-and-replace on all g-code. -# -# Due to technical limitations, the search can't cross the border between -# layers. + class SearchAndReplace(Script): + """Performs a search-and-replace on all g-code. + + Due to technical limitations, the search can't cross the border between + layers. + """ + def getSettingDataString(self): return """{ "name": "Search and Replace",