Source code for sas.qtgui.Utilities.PluginManager

# global
import os
from shutil import copyfile
import logging

from PyQt5 import QtWidgets, QtCore

from sas.sascalc.fit import models
from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor
import sas.qtgui.Utilities.GuiUtils as GuiUtils

from sas.qtgui.Utilities.UI.PluginManagerUI import Ui_PluginManagerUI


[docs]class PluginManager(QtWidgets.QDialog, Ui_PluginManagerUI): """ Class describing the model plugin manager. This is a simple list widget allowing for viewing/adding/deleting custom models. """
[docs] def __init__(self, parent=None): super(PluginManager, self).__init__(parent._parent) self.setupUi(self) # disable the context help icon self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) self.parent = parent self.cmdDelete.setEnabled(False) self.cmdEdit.setEnabled(False) self.cmdDuplicate.setEnabled(False) # globals self.readModels() # internal representation of the parameter list # {<row>: (<parameter>, <value>)} self.plugin_dict = {} # Initialize signals self.addSignals()
[docs] def readModels(self): """ Read in custom models from the default location """ self.lstModels.clear() self.plugins = models.find_plugin_models() model_list = list(self.plugins.keys()) self.lstModels.addItems(model_list)
[docs] def addSignals(self): """ Define slots for widget signals """ self.cmdOK.clicked.connect(self.accept) self.cmdDelete.clicked.connect(self.onDelete) self.cmdAdd.clicked.connect(self.onAdd) self.cmdAddFile.clicked.connect(self.onAddFile) self.cmdDuplicate.clicked.connect(self.onDuplicate) self.cmdEdit.clicked.connect(self.onEdit) self.cmdHelp.clicked.connect(self.onHelp) self.lstModels.selectionModel().selectionChanged.connect(self.onSelectionChanged) self.parent.communicate.customModelDirectoryChanged.connect(self.readModels)
[docs] def onSelectionChanged(self): """ Respond to row selection """ rows = len(self.lstModels.selectionModel().selectedRows()) self.cmdDelete.setEnabled(rows>0) self.cmdEdit.setEnabled(rows==1) self.cmdDuplicate.setEnabled(rows>0)
[docs] def onDelete(self): """ Remove the file containing the selected plugin """ plugins_to_delete = [s.data() for s in self.lstModels.selectionModel().selectedRows()] delete_msg = "Are you sure you want to remove the selected plugins?" reply = QtWidgets.QMessageBox.question( self, 'Warning', delete_msg, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) # Exit if no if reply == QtWidgets.QMessageBox.No: return for plugin in plugins_to_delete: # get filename from the plugin name name = self.plugins[plugin].filename # if no filename defined, attempt plugin name as filename if not name: name = os.path.join(models.find_plugins_dir(), plugin + ".py") os.remove(name) self.parent.communicate.customModelDirectoryChanged.emit()
[docs] def onAdd(self): """ Show the add new model dialog """ self.add_widget = TabbedModelEditor(parent=self.parent) self.add_widget.show()
[docs] def onAddFile(self): """ Open system Load FIle dialog, load a plugin and put it in the plugin directory """ plugin_file = QtWidgets.QFileDialog.getOpenFileName( self, "Choose a plugin", "","Python (*.py)")[0] if not plugin_file: return plugin_dir = models.find_plugins_dir() file_name = os.path.basename(str(plugin_file)) # check if valid model try: model_results = GuiUtils.checkModel(plugin_file) logging.info(model_results) # We can't guarantee the type of the exception coming from # Sasmodels, so need the overreaching general Exception except Exception as ex: msg = "Invalid plugin: %s " % file_name msgbox = QtWidgets.QMessageBox() msgbox.setIcon(QtWidgets.QMessageBox.Critical) msgbox.setText(msg) msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok) retval = msgbox.exec_() return # check if file with the same name exists if file_name in os.listdir(plugin_dir): msg = "Plugin " + file_name + " already exists.\n" msg += "Do you wish to overwrite the file?" msgbox = QtWidgets.QMessageBox(self) msgbox.setIcon(QtWidgets.QMessageBox.Warning) msgbox.setText(msg) msgbox.setWindowTitle("Plugin Load") # custom buttons button_yes = QtWidgets.QPushButton("Yes") msgbox.addButton(button_yes, QtWidgets.QMessageBox.YesRole) button_no = QtWidgets.QPushButton("No") msgbox.addButton(button_no, QtWidgets.QMessageBox.RejectRole) retval = msgbox.exec_() if retval == QtWidgets.QMessageBox.RejectRole: # cancel copy return # Copy from origin to ~/.sasview/plugin_models from shutil import copy # no check on clash copy(plugin_file, plugin_dir) # Copy corresponding C file, if present c_file = plugin_file.replace(".py", ".c") if os.path.isfile(c_file): copy(c_file, plugin_dir) self.parent.communicate.customModelDirectoryChanged.emit() log_msg = "New plugin added: %s" % file_name logging.info(log_msg)
[docs] def onDuplicate(self): """ Creates a copy of the selected model(s) """ plugins_to_copy = [s.data() for s in self.lstModels.selectionModel().selectedRows()] plugin_dir = models.find_plugins_dir() for plugin in plugins_to_copy: src_filename = plugin + ".py" # get filename from the plugin name src_file = self.plugins[plugin].filename # if no filename defined, attempt plugin name as filename if not src_file: src_filename = plugin + ".py" src_file = os.path.join(plugin_dir, src_filename) else: src_filename = os.path.basename(src_file) dst_filename = GuiUtils.findNextFilename(src_filename, plugin_dir) if not dst_filename: logging.error("Could not find appropriate filename for "+src_file) dst_file = os.path.join(plugin_dir, dst_filename) copyfile(src_file, dst_file) self.parent.communicate.customModelDirectoryChanged.emit()
[docs] def onEdit(self): """ Show the edit existing model dialog """ plugin_location = models.find_plugins_dir() # GUI assured only one row selected. Pick up the only element in list. try: model_to_edit = self.lstModels.selectionModel().selectedRows()[0].data() except Exception: # Something wrong with model, return return # get filename from the plugin name name = self.plugins[model_to_edit].filename # if no filename defined, attempt plugin name as filename if not name: name = os.path.join(plugin_location, model_to_edit + ".py") self.edit_widget = TabbedModelEditor(parent=self.parent, edit_only=True) self.edit_widget.loadFile(name) self.edit_widget.show()
[docs] def onHelp(self): """ Show the help page in the default browser """ location = "/user/qtgui/Perspectives/Fitting/fitting_help.html#new-plugin-model" self.parent.showHelp(location)