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