From 7c291e36c9ddd77cd5c2501ef9419ba5ae51de1a Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sat, 3 Feb 2018 23:54:18 +0100 Subject: [PATCH 01/11] Add warning for some bug that I saw Maybe one day someone will encounter it again and decide to fix it, but I'm lazy. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 2c6fc3f492..56cb69d9c1 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -121,7 +121,7 @@ class PostProcessingPlugin(QObject, Extension): spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py")) loaded_script = importlib.util.module_from_spec(spec) spec.loader.exec_module(loaded_script) - sys.modules[script_name] = loaded_script + sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name? loaded_class = getattr(loaded_script, script_name) temp_object = loaded_class() From 09f580de709c3e2da7da84b6b57c99015608c0ec Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 00:51:43 +0100 Subject: [PATCH 02/11] Store post-processing script list in global stack If you switch and such, it'll load these scripts back in. They are now persistent. --- .../PostProcessingPlugin.py | 140 +++++++++++++----- 1 file changed, 101 insertions(+), 39 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 56cb69d9c1..ab4fd4b123 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -8,6 +8,8 @@ from UM.Application import Application from UM.Extension import Extension from UM.Logger import Logger +import configparser #The script lists are stored in metadata as serialised config files. +import io #To allow configparser to write to a string. import os.path import pkgutil import sys @@ -35,6 +37,8 @@ class PostProcessingPlugin(QObject, Extension): self._selected_script_index = -1 Application.getInstance().getOutputDeviceManager().writeStarted.connect(self.execute) + Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) #When the current printer changes, update the list of scripts. + self.scriptListChanged.connect(self._writeScriptsToStack) #Whenever there is a change, save it into the stack. selectedIndexChanged = pyqtSignal() @pyqtProperty("QVariant", notify = selectedIndexChanged) @@ -113,32 +117,49 @@ class PostProcessingPlugin(QObject, Extension): ## Load all scripts from provided path. # This should probably only be done on init. # \param path Path to check for scripts. - def loadAllScripts(self, path): - scripts = pkgutil.iter_modules(path = [path]) - for loader, script_name, ispkg in scripts: - # Iterate over all scripts. - if script_name not in sys.modules: - spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py")) - loaded_script = importlib.util.module_from_spec(spec) - spec.loader.exec_module(loaded_script) - sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name? + def loadAllScripts(self): + if self._loaded_scripts: #Already loaded. + return + + ## Load all scripts in the scripts folders + for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Preferences)]: + try: + path = os.path.join(root, "scripts") + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError: + Logger.log("w", "Unable to create a folder for scripts: " + path) + continue + + scripts = pkgutil.iter_modules(path = [path]) + for loader, script_name, ispkg in scripts: + # Iterate over all scripts. + if script_name not in sys.modules: + spec = importlib.util.spec_from_file_location(__name__ + "." + script_name, os.path.join(path, script_name + ".py")) + loaded_script = importlib.util.module_from_spec(spec) + spec.loader.exec_module(loaded_script) + sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name? + + loaded_class = getattr(loaded_script, script_name) + temp_object = loaded_class() + Logger.log("d", "Begin loading of script: %s", script_name) + try: + setting_data = temp_object.getSettingData() + if "name" in setting_data and "key" in setting_data: + self._script_labels[setting_data["key"]] = setting_data["name"] + self._loaded_scripts[setting_data["key"]] = loaded_class + else: + Logger.log("w", "Script %s.py has no name or key", script_name) + self._script_labels[script_name] = script_name + self._loaded_scripts[script_name] = loaded_class + except AttributeError: + Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name) + except NotImplementedError: + Logger.log("e", "Script %s.py has no implemented settings", script_name) + except Exception as e: + Logger.logException("e", "Exception occurred while loading post processing plugin: {error_msg}".format(error_msg = str(e))) - loaded_class = getattr(loaded_script, script_name) - temp_object = loaded_class() - Logger.log("d", "Begin loading of script: %s", script_name) - try: - setting_data = temp_object.getSettingData() - if "name" in setting_data and "key" in setting_data: - self._script_labels[setting_data["key"]] = setting_data["name"] - self._loaded_scripts[setting_data["key"]] = loaded_class - else: - Logger.log("w", "Script %s.py has no name or key", script_name) - self._script_labels[script_name] = script_name - self._loaded_scripts[script_name] = loaded_class - except AttributeError: - Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name) - except NotImplementedError: - Logger.log("e", "Script %s.py has no implemented settings", script_name) self.loadedScriptListChanged.emit() loadedScriptListChanged = pyqtSignal() @@ -165,24 +186,65 @@ 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): + self.loadAllScripts() + new_stack = Application.getInstance().getGlobalContainerStack() + self._script_list.clear() + if not new_stack.getMetaDataEntry("post_processing_scripts"): #Missing or empty. + self.scriptListChanged.emit() #Even emit this if it didn't change. We want it to write the empty list to the stack's metadata. + return + + self._script_list.clear() + scripts_list_strs = new_stack.getMetaDataEntry("post_processing_scripts") + for script_str in scripts_list_strs.split("\n"): #Encoded config files should never contain three newlines in a row. At most 2, just before section headers. + if not script_str: #There were no scripts in this one (or a corrupt file caused more than 3 consecutive newlines here). + continue + script_str = script_str.replace("\\n", "\n").replace("\\\\", "\\") + script_parser = configparser.ConfigParser(interpolation = None) + script_parser.read_string(script_str) + for script_name, settings in script_parser.items(): #There should only be one, really! Otherwise we can't guarantee the order or allow multiple uses of the same script. + if script_name == "DEFAULT": #ConfigParser always has a DEFAULT section, but we don't fill it. Ignore this one. + continue + if script_name not in self._loaded_scripts: #Don't know this post-processing plug-in. + Logger.log("e", "Unknown post-processing script {script_name} was encountered in this global stack.".format(script_name = script_name)) + continue + new_script = self._loaded_scripts[script_name]() + for setting_key, setting_value in settings.items(): #Put all setting values into the script. + new_script._instance.setProperty(setting_key, "value", setting_value) + self._script_list.append(new_script) + + self.scriptListChanged.emit() + + def _writeScriptsToStack(self): + script_list_strs = [] + for script in self._script_list: + parser = configparser.ConfigParser(interpolation = None) #We'll encode the script as a config with one section. The section header is the key and its values are the settings. + script_name = script.getSettingData()["key"] + parser.add_section(script_name) + for key in script.getSettingData()["settings"]: + value = script.getSettingValueByKey(key) + parser[script_name][key] = str(value) + serialized = io.StringIO() #ConfigParser can only write to streams. Fine. + parser.write(serialized) + serialized.seek(0) + script_str = serialized.read() + script_str = script_str.replace("\\", "\\\\").replace("\n", "\\n") + script_list_strs.append(script_str) + + script_list_strs = "\n".join(script_list_strs) #ConfigParser should never output three newlines in a row when serialised, so it's a safe delimiter (and very human-readable). + + global_stack = Application.getInstance().getGlobalContainerStack() + if "post_processing_scripts" not in global_stack.getMetaData(): + global_stack.addMetaDataEntry("post_processing_scripts", "") + Application.getInstance().getGlobalContainerStack().setMetaDataEntry("post_processing_scripts", script_list_strs) + ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self): Logger.log("d", "Creating post processing plugin view.") - ## Load all scripts in the scripts folders - for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Preferences)]: - try: - path = os.path.join(root, "scripts") - if not os.path.isdir(path): - try: - os.makedirs(path) - except OSError: - Logger.log("w", "Unable to create a folder for scripts: " + path) - continue - - self.loadAllScripts(path) - except Exception as e: - Logger.logException("e", "Exception occurred while loading post processing plugin: {error_msg}".format(error_msg = str(e))) + self.loadAllScripts() # Create the plugin dialog component path = os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml") From 852513184993ed234e291b7d3601bb5b8a608f55 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:08:15 +0100 Subject: [PATCH 03/11] Create post-processing tool button on start-up This causes the button to display immediately on start-up if there were any post-processing scripts in the current machine. If there weren't, then no button will be shown (as specified in the QML). Previously the QML was only loaded first upon clicking the item in the extensions menu. Now it has to be loaded on start-up in order to display that button. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index ab4fd4b123..9e596277f7 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -38,6 +38,7 @@ class PostProcessingPlugin(QObject, Extension): Application.getInstance().getOutputDeviceManager().writeStarted.connect(self.execute) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) #When the current printer changes, update the list of scripts. + Application.getInstance().mainWindowChanged.connect(self._createView) #When the main window is created, create the view so that we can display the post-processing icon if necessary. self.scriptListChanged.connect(self._writeScriptsToStack) #Whenever there is a change, save it into the stack. selectedIndexChanged = pyqtSignal() From 91ab53e49c6f73b28491df406f57e7037b8c4ad5 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:10:35 +0100 Subject: [PATCH 04/11] Somewhat better documentation Just some stuff that I saw and thought were missing. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 9e596277f7..770169f210 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -190,7 +190,7 @@ class PostProcessingPlugin(QObject, Extension): ## When the global container stack is changed, swap out the list of active # scripts. def _onGlobalContainerStackChanged(self): - self.loadAllScripts() + self.loadAllScripts() #Make sure we have all scripts if we didn't have them yet. new_stack = Application.getInstance().getGlobalContainerStack() self._script_list.clear() if not new_stack.getMetaDataEntry("post_processing_scripts"): #Missing or empty. @@ -202,7 +202,7 @@ class PostProcessingPlugin(QObject, Extension): for script_str in scripts_list_strs.split("\n"): #Encoded config files should never contain three newlines in a row. At most 2, just before section headers. if not script_str: #There were no scripts in this one (or a corrupt file caused more than 3 consecutive newlines here). continue - script_str = script_str.replace("\\n", "\n").replace("\\\\", "\\") + script_str = script_str.replace("\\n", "\n").replace("\\\\", "\\") #Unescape escape sequences. script_parser = configparser.ConfigParser(interpolation = None) script_parser.read_string(script_str) for script_name, settings in script_parser.items(): #There should only be one, really! Otherwise we can't guarantee the order or allow multiple uses of the same script. @@ -231,10 +231,10 @@ class PostProcessingPlugin(QObject, Extension): parser.write(serialized) serialized.seek(0) script_str = serialized.read() - script_str = script_str.replace("\\", "\\\\").replace("\n", "\\n") + script_str = script_str.replace("\\", "\\\\").replace("\n", "\\n") #Escape newlines because configparser sees those as section delimiters. script_list_strs.append(script_str) - script_list_strs = "\n".join(script_list_strs) #ConfigParser should never output three newlines in a row when serialised, so it's a safe delimiter (and very human-readable). + script_list_strs = "\n".join(script_list_strs) #ConfigParser should never output three newlines in a row when serialised, so it's a safe delimiter. global_stack = Application.getInstance().getGlobalContainerStack() if "post_processing_scripts" not in global_stack.getMetaData(): From bebcd78db04fcd5cfee37d581d5ff804d4d09f90 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:13:34 +0100 Subject: [PATCH 05/11] Reset the selected script index upon changing printers So that you're not getting into a state of viewing an invalid index. If you switch to a printer with 0 scripts attached, the index is still invalid, but it won't display anything then so you're okay. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 770169f210..7f53ab4672 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -216,6 +216,7 @@ class PostProcessingPlugin(QObject, Extension): new_script._instance.setProperty(setting_key, "value", setting_value) self._script_list.append(new_script) + self.setSelectedScriptIndex(0) self.scriptListChanged.emit() def _writeScriptsToStack(self): From 927f19327044e4d0fa0205b76b9d4a007f5db7bc Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:26:34 +0100 Subject: [PATCH 06/11] Only write scripts to stack upon pressing close button This is more efficient, because we're not writing every time you modify the list of scripts. It's also more complete, since if you don't modify the list of scripts but just modify one of the settings, that setting change also gets written to the stack after closing the window. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 5 +++-- plugins/PostProcessingPlugin/PostProcessingPlugin.qml | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 7f53ab4672..9cc369a0d9 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -39,7 +39,7 @@ class PostProcessingPlugin(QObject, Extension): Application.getInstance().getOutputDeviceManager().writeStarted.connect(self.execute) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) #When the current printer changes, update the list of scripts. Application.getInstance().mainWindowChanged.connect(self._createView) #When the main window is created, create the view so that we can display the post-processing icon if necessary. - self.scriptListChanged.connect(self._writeScriptsToStack) #Whenever there is a change, save it into the stack. + #self.scriptListChanged.connect(self.writeScriptsToStack) #Whenever there is a change, save it into the stack. selectedIndexChanged = pyqtSignal() @pyqtProperty("QVariant", notify = selectedIndexChanged) @@ -219,7 +219,8 @@ class PostProcessingPlugin(QObject, Extension): self.setSelectedScriptIndex(0) self.scriptListChanged.emit() - def _writeScriptsToStack(self): + @pyqtSlot() + def writeScriptsToStack(self): script_list_strs = [] for script in self._script_list: parser = configparser.ConfigParser(interpolation = None) #We'll encode the script as a config with one section. The section header is the key and its values are the settings. diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index d64d60a04a..d2508f0cad 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -21,6 +21,14 @@ UM.Dialog minimumWidth: 400 * screenScaleFactor; minimumHeight: 250 * screenScaleFactor; + onVisibleChanged: + { + if(!visible) //Whenever the window is closed (either via the "Close" button or the X on the window frame), we want to update it in the stack. + { + manager.writeScriptsToStack(); + } + } + Item { UM.I18nCatalog{id: catalog; name:"cura"} From f9bee9ad3ea04184b14461a26a8e2228a02cdfd8 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:39:36 +0100 Subject: [PATCH 07/11] Change tabs into spaces I think this IDE isn't configured correctly yet. --- .../PostProcessingPlugin/PostProcessingPlugin.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index d2508f0cad..a7fd2ef9a2 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -21,13 +21,13 @@ UM.Dialog minimumWidth: 400 * screenScaleFactor; minimumHeight: 250 * screenScaleFactor; - onVisibleChanged: - { - if(!visible) //Whenever the window is closed (either via the "Close" button or the X on the window frame), we want to update it in the stack. - { - manager.writeScriptsToStack(); - } - } + onVisibleChanged: + { + if(!visible) //Whenever the window is closed (either via the "Close" button or the X on the window frame), we want to update it in the stack. + { + manager.writeScriptsToStack(); + } + } Item { From 77566180f12cef3d00ebe60391372809334e2060 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 4 Feb 2018 01:53:10 +0100 Subject: [PATCH 08/11] Remove commented code Oops, forgot about this one. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 9cc369a0d9..7339b379b5 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -39,7 +39,6 @@ class PostProcessingPlugin(QObject, Extension): Application.getInstance().getOutputDeviceManager().writeStarted.connect(self.execute) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) #When the current printer changes, update the list of scripts. Application.getInstance().mainWindowChanged.connect(self._createView) #When the main window is created, create the view so that we can display the post-processing icon if necessary. - #self.scriptListChanged.connect(self.writeScriptsToStack) #Whenever there is a change, save it into the stack. selectedIndexChanged = pyqtSignal() @pyqtProperty("QVariant", notify = selectedIndexChanged) From 14936fdf2117714dc579b566c455f0e8254740b5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Mar 2018 13:50:29 +0200 Subject: [PATCH 09/11] Move looping over script directories to separate function Removes some code repetition. --- .../PostProcessingPlugin.py | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 1740710e30..1aa5474e5e 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -114,10 +114,29 @@ 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, but it can be used to update + # the scripts list from files just as well. + def loadAllScripts(self): + #The PostProcessingPlugin path is for built-in scripts. + #The Resources path is where the user should store custom scripts. + #The Preferences path is legacy, where the user may previously have stored scripts. + for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]: + path = os.path.join(root, "scripts") + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError: + Logger.log("w", "Unable to create a folder for scripts: " + path) + continue + + 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 loadAllScripts(self, path): + def loadScripts(self, path): if self._loaded_scripts: #Already loaded. return @@ -178,20 +197,7 @@ class PostProcessingPlugin(QObject, Extension): ## When the global container stack is changed, swap out the list of active # scripts. def _onGlobalContainerStackChanged(self): - ## Load all scripts in the scripts folders - # The PostProcessingPlugin path is for built-in scripts. - # The Resources path is where the user should store custom scripts. - # The Preferences path is legacy, where the user may previously have stored scripts. - for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]: - path = os.path.join(root, "scripts") - if not os.path.isdir(path): - try: - os.makedirs(path) - except OSError: - Logger.log("w", "Unable to create a folder for scripts: " + path) - continue - - self.loadAllScripts(path) + self.loadAllScripts() new_stack = Application.getInstance().getGlobalContainerStack() self._script_list.clear() if not new_stack.getMetaDataEntry("post_processing_scripts"): #Missing or empty. @@ -248,20 +254,7 @@ class PostProcessingPlugin(QObject, Extension): def _createView(self): Logger.log("d", "Creating post processing plugin view.") - ## Load all scripts in the scripts folders - # The PostProcessingPlugin path is for built-in scripts. - # The Resources path is where the user should store custom scripts. - # The Preferences path is legacy, where the user may previously have stored scripts. - for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]: - path = os.path.join(root, "scripts") - if not os.path.isdir(path): - try: - os.makedirs(path) - except OSError: - Logger.log("w", "Unable to create a folder for scripts: " + path) - continue - - self.loadAllScripts(path) + self.loadAllScripts() # Create the plugin dialog component path = os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml") From 767789d394c4ebd15607f01c9fad2a0e3aa81d2b Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 26 Mar 2018 13:51:37 +0200 Subject: [PATCH 10/11] Filter generic list for Ultimaker printers. CURA-5149 --- cura/Machines/Models/GenericMaterialsModel.py | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py index 2fac919f3e..e6fd1aa545 100644 --- a/cura/Machines/Models/GenericMaterialsModel.py +++ b/cura/Machines/Models/GenericMaterialsModel.py @@ -1,8 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Dict from UM.Logger import Logger from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel +from cura.Machines.MaterialNode import MaterialNode class GenericMaterialsModel(BaseMaterialsModel): @@ -38,6 +40,24 @@ class GenericMaterialsModel(BaseMaterialsModel): self.setItems([]) return + #special case only for Ultimaker printers, filter the generic list + printer_name = global_stack.getMetaDataEntry("name", "empty") + filter_ultimaker_printers = False + if printer_name and printer_name[:9] == "Ultimaker": + filter_ultimaker_printers = True + + # Special case, Ultimaker generic list also should be filtered + if filter_ultimaker_printers is False: + item_list = self._getGenericProfiles(available_material_dict) + else: + item_list = self._getUltimakerGenericProfiles(available_material_dict) + + # Sort the item list by material name alphabetically + item_list = sorted(item_list, key = lambda d: d["name"].upper()) + + self.setItems(item_list) + + def _getGenericProfiles(self, available_material_dict): item_list = [] for root_material_id, container_node in available_material_dict.items(): metadata = container_node.metadata @@ -55,7 +75,42 @@ class GenericMaterialsModel(BaseMaterialsModel): } item_list.append(item) - # Sort the item list by material name alphabetically - item_list = sorted(item_list, key = lambda d: d["name"].upper()) + return item_list - self.setItems(item_list) + ## The method filters available materials by name. If material is not defined for Ultimaker printers + # then it will be removed + # \available_material_dict \type{dictionary} + # \return The filtered list + def _getUltimakerGenericProfiles(self, available_material_dict: Dict[str, MaterialNode]): + generic_item_list = [] + ultimaker_item_list = [] + + for root_material_id, container_node in available_material_dict.items(): + metadata = container_node.metadata + + is_ultimaker_brand = False + brand_name = metadata["brand"].lower() + + if brand_name != "generic": + if brand_name == 'ultimaker': + is_ultimaker_brand = True + else: + continue + + item = {"root_material_id": root_material_id, + "id": metadata["id"], + "name": metadata["name"], + "brand": metadata["brand"], + "material": metadata["material"], + "color_name": metadata["color_name"], + "container_node": container_node + } + if is_ultimaker_brand: + ultimaker_item_list.append(item['material']) + else: + generic_item_list.append(item) + + # If material is not in ultimaker list then remove it + item_list = [material for material in generic_item_list if material['material'] in ultimaker_item_list] + + return item_list From e2724f53ad84b4c544335eeef005adc848b83992 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Mar 2018 13:55:15 +0200 Subject: [PATCH 11/11] Only check for loaded plug-ins at initial call to loadAllScripts Not for every call to loadScripts. --- plugins/PostProcessingPlugin/PostProcessingPlugin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 1aa5474e5e..31bad0cfe8 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -116,9 +116,11 @@ class PostProcessingPlugin(QObject, Extension): ## Load all scripts from all paths where scripts can be found. # - # This should probably only be done on init, but it can be used to update - # the scripts list from files just as well. + # This should probably only be done on init. def loadAllScripts(self): + if self._loaded_scripts: #Already loaded. + return + #The PostProcessingPlugin path is for built-in scripts. #The Resources path is where the user should store custom scripts. #The Preferences path is legacy, where the user may previously have stored scripts. @@ -137,9 +139,6 @@ class PostProcessingPlugin(QObject, Extension): # This should probably only be done on init. # \param path Path to check for scripts. def loadScripts(self, path): - if self._loaded_scripts: #Already loaded. - return - ## Load all scripts in the scripts folders scripts = pkgutil.iter_modules(path = [path]) for loader, script_name, ispkg in scripts: