Example #1
0
class GenericOperationWidget(QWidget):
    type_mapping = {"int": {"widget": QSpinBox,
                            "display_func": "setValue",
                            "display_conversion": int,
                            "get_func": "value",
                            "get_conversion": int},
                    "float": {"widget": QLineEdit,
                              "display_func": "setText",
                              "display_conversion": str,
                              "get_func": "text",
                              "get_conversion": float},
                    "bool": {"widget": QCheckBox,
                             "display_func": "setChecked",
                             "get_func": "checkState"},
                    "slice": {"widget": QSliceInput,
                              "display_func": "setSlice",
                              "get_func": "getSlice"}}
    def __init__(self, parent=None, op=None):
        QWidget.__init__(self, parent)
        self.widget_layout = QVBoxLayout()
        self.minimumHeight = 200 # FIXME: hab noch nie verstanden warum das net einfach einfach ist...
        self.setLayout(self.widget_layout)

        self._op = None
        self._widgets = []
        self._par_names = []

    def _clear_widgets(self):
        """
        Remove any dynamically created widget
        """
        for i in reversed(range(self.widget_layout.count())):
            widget = self.widget_layout.takeAt(i).widget()
            if widget is not None:
                widget.deleteLater()
                widget.setParent(None)

        self._widgets = []
        self._par_names = []


    def _set_widgets(self):
        """
        Parse operation (function arguments) and create widgets according to
        the function annotations. Fill with default or current argument values
        and display the widgets.
        """
        self._clear_widgets()

        # Retreive operation (function detail), instantiate neccesary widgets
        package, fun = self._op["module"].rsplit('.', 1)
        module = import_module(package)
        self._op["function"] = getattr(module, fun)
        argspec = inspect.getfullargspec(self._op["function"])

        # Create widgets
        self._labels = []
        self._widgets = []
        self._none_checkbox_widgets = []
        for par_name, annotation in sorted(argspec.annotations.items()):
            label = QLabel(par_name)
            font = qtgui.QFont()
            font.setPointSize(10)
            label.setFont(font)

            # find associated widget and functions
            mapping = {}
            if "widget" in annotation:
                mapping["widget"] = widget = getattr(qtgui, annotation["widget"])
                mapping["display_func"] = annotation["display_func"]
                mapping["display_conversion"] = annotation["display_conversion"]
                mapping["get_func"] = annotation["get_func"]
            elif "type" in annotation:
                try:
                    mapping = dict(GenericOperationWidget.type_mapping[annotation["type"]])
                except KeyError:
                    print("Can't find wiget for type: {type} for parameter " +
                          "{par_name}".format(type=annotation["type"], par_name=par_name))
                    continue
            else:
                continue

            # instanciate widget and make display_function callable
            widget = mapping["widget"]()
            if not callable(mapping["display_func"]):
                mapping["display_func"] = widget.__getattribute__(mapping["display_func"])

            # save mapping and parameter name for later use
            widget.mapping = mapping
            widget.par_name = par_name

            # create checkbox to allow not to set this parameter
            checkbox = QCheckBox()
            checkbox.setChecked(True)
            checkbox.setToolTip("Set the \"%s\" parameter. If unchecked, the default value is used." % par_name)
            checkbox.par_name = widget.par_name

            # Set value of widget according to parameters of the operation
            try:
                if "display_conversion" in mapping:
                    display_value = mapping["display_conversion"](self._op["kwargs"][par_name])    
                else:
                    display_value = self._op["kwargs"][par_name]
                mapping["display_func"](display_value)
            except KeyError:
                checkbox.setChecked(False)
            
            self._labels.append(label)
            self._widgets.append(widget)
            self._none_checkbox_widgets.append(checkbox)

        # Add widgets to layout
        if not self._widgets:
            self.widget_layout.addWidget(QLabel("No operation parameters"))
        for i, widget in enumerate(self._widgets):
            self.widget_layout.addWidget(self._labels[i])
            self.widget_layout.addWidget(self._widgets[i])
            self.widget_layout.addWidget(self._none_checkbox_widgets[i])

    def set_operation(self, op):
        """
        Create widgets for operation. Accepts either a full dict for a
        operation or a dict with only package, module and function
        """
        self._op = op
        self._set_widgets()

    def get_operation(self):
        """
        Returns operation dict for the current state of the parameter widgets.
        """
        kwargs = {}
        for i, w in enumerate(self._widgets):
            if hasattr(w, "par_name"):
                if self._none_checkbox_widgets[i].isChecked():
                    kwargs[w.par_name] = self.get_operation_parameter(w)

        self._op["kwargs"] = kwargs

        package, fun = self._op["module"].rsplit('.', 1)
        module = import_module(package)
        self._op["function"] = getattr(module, fun)
        return self._op
    
    def get_operation_parameter(self, w):       
        """
        Extract a parameter from a operations widget using mapping["get_func"]. 
        If a get_conversion is provided the raw input will be converted using 
        the get_conversion.Converts "" and "None" input to None for the time 
        being.
        
        Params:
        =======
        w : OperationsWidget
            The widget containing the parameter
            
        Returns:
        ========
        param : 
            Parameter as expected for the operation which w. represents
        """         
        try:
            # Try to get as many parameters as possible. If conversion fails it
            # will be apparent when the widget is repopulated as the parameter
            # is simply not set
            parameter = w.__getattribute__(w.mapping["get_func"])()
            if parameter == "" or parameter == "None":
                # FIXME: Move this to a generic get_conversion function
                parameter= None
            if "get_conversion" in w.mapping:
                parameter = w.mapping["get_conversion"](parameter)
        except ValueError:
            parameter = None
        return parameter