Ejemplo n.º 1
0
        def _add_line():
            n_lines = len(self.combos) + 1

            # add delete symbol
            button = gui.button(
                None,
                self,
                label="×",
                flat=True,
                height=20,
                styleSheet="* {font-size: 16pt; color: silver}"
                "*:hover {color: black}",
                autoDefault=False,
                callback=self._remove_row,
            )
            button.setMinimumSize(QSize(12, 20))
            self.rules_box.addWidget(button, n_lines, 0)
            self.remove_buttons.append(button)

            # add statistics type dropdown
            combo = QComboBox()
            combo.addItems(STATISTICS_NAMES)
            combo.currentIndexChanged.connect(self._sync_edit_combo)
            self.rules_box.addWidget(combo, n_lines, 1)
            self.combos.append(combo)

            # add line edit for patern
            line_edit = QLineEdit()
            self.rules_box.addWidget(line_edit, n_lines, 2)
            line_edit.textChanged.connect(self._sync_edit_line)
            self.line_edits.append(line_edit)
Ejemplo n.º 2
0
def _(values: List[str], value: str, key: KeyType, signal: Callable) \
        -> QComboBox:
    combo = QComboBox()
    combo.addItems(values)
    combo.setCurrentText(value)
    combo.currentTextChanged.connect(lambda text: signal.emit(key, text))
    return combo
Ejemplo n.º 3
0
class DiscreteVariableEditor(VariableEditor):
    def __init__(self, parent: QWidget, items: Tuple[str], callback: Callable):
        super().__init__(parent, callback)
        self._combo = QComboBox(
            parent,
            maximumWidth=180,
            sizePolicy=QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        )
        self._combo.addItems(items + ("?",))
        self._combo.currentIndexChanged.connect(self.__on_index_changed)
        self.layout().addWidget(self._combo)

    @property
    def value(self) -> Union[int, float]:
        return self._map_to_var_values()

    @value.setter
    def value(self, value: float):
        if np.isnan(value):
            value = self._combo.model().rowCount() - 1
        assert value == int(value)
        self._combo.setCurrentIndex(int(value))

    def __on_index_changed(self):
        self.valueChanged.emit(self._map_to_var_values())

    def _map_to_var_values(self) -> Union[int, float]:
        n_values = self._combo.model().rowCount() - 1
        current = self._combo.currentIndex()
        return current if current < n_values else np.nan
Ejemplo n.º 4
0
        def _add_line():
            n_lines = len(self.combos) + 1

            # add delete symbol
            button = gui.button(None,
                                self,
                                "×",
                                callback=self._remove_row,
                                addToLayout=False,
                                autoDefault=False,
                                width=34,
                                sizePolicy=(QSizePolicy.Maximum,
                                            QSizePolicy.Maximum))
            self.rules_box.addWidget(button, n_lines, 0)
            self.remove_buttons.append(button)

            # add statistics type dropdown
            combo = QComboBox()
            combo.addItems(STATISTICS_NAMES)
            combo.currentIndexChanged.connect(self._sync_edit_combo)
            self.rules_box.addWidget(combo, n_lines, 1)
            self.combos.append(combo)

            # add line edit for patern
            line_edit = QLineEdit()
            self.rules_box.addWidget(line_edit, n_lines, 2)
            line_edit.textChanged.connect(self._sync_edit_line)
            self.line_edits.append(line_edit)
Ejemplo n.º 5
0
 def set_new_operators(self,
                       attr_combo,
                       adding_all,
                       selected_index=None,
                       selected_values=None):
     old_combo = self.cond_list.cellWidget(attr_combo.row, 1)
     prev_text = old_combo.currentText() if old_combo else ""
     oper_combo = QComboBox()
     oper_combo.row = attr_combo.row
     oper_combo.attr_combo = attr_combo
     attr_name = attr_combo.currentText()
     if attr_name in self.AllTypes:
         oper_combo.addItems(self.operator_names[attr_name])
     else:
         var = self.data.domain[attr_name]
         oper_combo.addItems(self.operator_names[type(var)])
     if selected_index is None:
         selected_index = oper_combo.findText(prev_text)
         if selected_index == -1:
             selected_index = 0
     oper_combo.setCurrentIndex(selected_index)
     self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
     self.set_new_values(oper_combo, adding_all, selected_values)
     oper_combo.currentIndexChanged.connect(
         lambda _: self.set_new_values(oper_combo, False))
def _(value: str, values: List[str], parent: QGroupBox, key: KeyType,
      signal: Callable) -> QComboBox:
    combo = QComboBox()
    combo.addItems(values)
    combo.setCurrentText(value)
    parent.layout().addWidget(combo)
    combo.currentTextChanged.connect(lambda text: signal.emit(key, text))
    return combo
Ejemplo n.º 7
0
 def set_new_operators(self, attr_combo, adding_all,
                       selected_index=None, selected_values=None):
     oper_combo = QComboBox()
     oper_combo.row = attr_combo.row
     oper_combo.attr_combo = attr_combo
     var = self.data.domain[attr_combo.currentText()]
     oper_combo.addItems(self.operator_names[type(var)])
     oper_combo.setCurrentIndex(selected_index or 0)
     self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
     self.set_new_values(oper_combo, adding_all, selected_values)
     oper_combo.currentIndexChanged.connect(
         lambda _: self.set_new_values(oper_combo, False))
Ejemplo n.º 8
0
 def set_new_operators(self, attr_combo, adding_all,
                       selected_index=None, selected_values=None):
     oper_combo = QComboBox()
     oper_combo.row = attr_combo.row
     oper_combo.attr_combo = attr_combo
     var = self.data.domain[attr_combo.currentText()]
     oper_combo.addItems(self.operator_names[type(var)])
     oper_combo.setCurrentIndex(selected_index or 0)
     self.cond_list.setCellWidget(oper_combo.row, 1, oper_combo)
     self.set_new_values(oper_combo, adding_all, selected_values)
     oper_combo.currentIndexChanged.connect(
         lambda _: self.set_new_values(oper_combo, False))
Ejemplo n.º 9
0
class Scale(BaseEditor):
    NoCentering, CenterMean, CenterMedian = 0, 1, 2
    NoScaling, ScaleBySD, ScaleBySpan = 0, 1, 2

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.__centercb = QComboBox()
        self.__centercb.addItems(
            ["No Centering", "Center by Mean", "Center by Median"])

        self.__scalecb = QComboBox()
        self.__scalecb.addItems(["No scaling", "Scale by SD", "Scale by span"])

        form.addRow("Center:", self.__centercb)
        form.addRow("Scale:", self.__scalecb)
        self.layout().addLayout(form)
        self.__centercb.currentIndexChanged.connect(self.changed)
        self.__scalecb.currentIndexChanged.connect(self.changed)
        self.__centercb.activated.connect(self.edited)
        self.__scalecb.activated.connect(self.edited)

    def setParameters(self, params):
        center = params.get("center", _Scale.CenteringType.Mean)
        scale = params.get("scale", _Scale.ScalingType.Std)
        self.__centercb.setCurrentIndex(
            _enum_to_index(_Scale.CenteringType, center))
        self.__scalecb.setCurrentIndex(
            _enum_to_index(_Scale.ScalingType, scale))

    def parameters(self):
        return {
            "center":
            _index_to_enum(_Scale.CenteringType,
                           self.__centercb.currentIndex()),
            "scale":
            _index_to_enum(_Scale.ScalingType, self.__scalecb.currentIndex())
        }

    @staticmethod
    def createinstance(params):
        center = params.get("center", _Scale.CenteringType.Mean)
        scale = params.get("scale", _Scale.ScalingType.Std)
        return _Scale(center=center, scale=scale)

    def __repr__(self):
        return "{}, {}".format(self.__centercb.currentText(),
                               self.__scalecb.currentText())
Ejemplo n.º 10
0
class BinarizeEditor(ScBaseEditor):
    DEFAULT_CONDITION = Binarize.GreaterOrEqual
    DEFAULT_THRESHOLD = 1

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self._threshold = self.DEFAULT_THRESHOLD

        self.setLayout(QVBoxLayout())
        form = QFormLayout()
        self.cond_cb = QComboBox()
        self.cond_cb.addItems(["Greater or Equal", "Greater"])
        self.cond_cb.currentIndexChanged.connect(self.changed)
        self.cond_cb.activated.connect(self.edited)

        self.thr_spin = QDoubleSpinBox(minimum=0,
                                       singleStep=0.5,
                                       decimals=1,
                                       value=self._threshold)
        self.thr_spin.valueChanged[float].connect(self._set_threshold)
        self.thr_spin.editingFinished.connect(self.edited)

        form.addRow("Condition:", self.cond_cb)
        form.addRow("Threshold:", self.thr_spin)
        self.layout().addLayout(form)

    def _set_threshold(self, t):
        if self._threshold != t:
            self._threshold = t
            self.thr_spin.setValue(t)
            self.changed.emit()

    def setParameters(self, params):
        cond = params.get("condition", self.DEFAULT_CONDITION)
        self.cond_cb.setCurrentIndex(enum_to_index(Binarize.Condition, cond))
        self._set_threshold(params.get("threshold", self.DEFAULT_THRESHOLD))

    def parameters(self):
        cond = index_to_enum(Binarize.Condition, self.cond_cb.currentIndex())
        return {"condition": cond, "threshold": self._threshold}

    @staticmethod
    def createinstance(params):
        condition = params.get("condition", BinarizeEditor.DEFAULT_CONDITION)
        threshold = params.get("threshold", BinarizeEditor.DEFAULT_THRESHOLD)
        return Binarize(condition, threshold)

    def __repr__(self):
        return "Condition: {}, Threshold: {}".format(
            self.cond_cb.currentText(), self.thr_spin.value())
class Randomize(BaseEditor):
    RandomizeClasses, RandomizeAttributes, RandomizeMetas = _Randomize.Type

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.__rand_type_cb = QComboBox()
        self.__rand_type_cb.addItems(["Classes", "Features", "Meta data"])

        self.__rand_type_cb.currentIndexChanged.connect(self.changed)
        self.__rand_type_cb.activated.connect(self.edited)

        self.__rand_seed_ch = QCheckBox()
        self.__rand_seed_ch.clicked.connect(self.edited)

        form.addRow("Randomize:", self.__rand_type_cb)
        form.addRow("Replicable shuffling:", self.__rand_seed_ch)
        self.layout().addLayout(form)

    def setParameters(self, params):
        rand_type = params.get("rand_type", Randomize.RandomizeClasses)
        self.__rand_type_cb.setCurrentIndex(
            _enum_to_index(_Randomize.Type, rand_type))
        self.__rand_seed_ch.setChecked(params.get("rand_seed", 1) or 0)

    def parameters(self):
        return {
            "rand_type":
            _index_to_enum(_Randomize.Type,
                           self.__rand_type_cb.currentIndex()),
            "rand_seed":
            1 if self.__rand_seed_ch.isChecked() else None,
        }

    @staticmethod
    def createinstance(params):
        rand_type = params.get("rand_type", Randomize.RandomizeClasses)
        rand_seed = params.get("rand_seed", 1)
        return _Randomize(rand_type=rand_type, rand_seed=rand_seed)

    def __repr__(self):
        return "{}, {}".format(
            self.__rand_type_cb.currentText(),
            "Replicable"
            if self.__rand_seed_ch.isChecked() else "Not replicable",
        )
Ejemplo n.º 12
0
class Scale(BaseEditor):
    NoCentering, CenterMean, CenterMedian = 0, 1, 2
    NoScaling, ScaleBySD, ScaleBySpan = 0, 1, 2

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.__centercb = QComboBox()
        self.__centercb.addItems(["No Centering", "Center by Mean",
                                  "Center by Median"])

        self.__scalecb = QComboBox()
        self.__scalecb.addItems(["No scaling", "Scale by SD",
                                 "Scale by span"])

        form.addRow("Center:", self.__centercb)
        form.addRow("Scale:", self.__scalecb)
        self.layout().addLayout(form)
        self.__centercb.currentIndexChanged.connect(self.changed)
        self.__scalecb.currentIndexChanged.connect(self.changed)
        self.__centercb.activated.connect(self.edited)
        self.__scalecb.activated.connect(self.edited)

    def setParameters(self, params):
        center = params.get("center", _Scale.CenteringType.Mean)
        scale = params.get("scale", _Scale.ScalingType.Std)
        self.__centercb.setCurrentIndex(
            enum_to_index(_Scale.CenteringType, center))
        self.__scalecb.setCurrentIndex(
            enum_to_index(_Scale.ScalingType, scale))

    def parameters(self):
        return {"center": index_to_enum(_Scale.CenteringType,
                                        self.__centercb.currentIndex()),
                "scale": index_to_enum(_Scale.ScalingType,
                                       self.__scalecb.currentIndex())}

    @staticmethod
    def createinstance(params):
        center = params.get("center", _Scale.CenteringType.Mean)
        scale = params.get("scale", _Scale.ScalingType.Std)
        return _Scale(center=center, scale=scale)

    def __repr__(self):
        return "{}, {}".format(self.__centercb.currentText(),
                               self.__scalecb.currentText())
Ejemplo n.º 13
0
class Randomize(BaseEditor):
    RandomizeClasses, RandomizeAttributes, RandomizeMetas = _Randomize.Type

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.__rand_type_cb = QComboBox()
        self.__rand_type_cb.addItems(["Classes",
                                      "Features",
                                      "Meta data"])

        self.__rand_type_cb.currentIndexChanged.connect(self.changed)
        self.__rand_type_cb.activated.connect(self.edited)

        self.__rand_seed_ch = QCheckBox()
        self.__rand_seed_ch.clicked.connect(self.edited)

        form.addRow("Randomize:", self.__rand_type_cb)
        form.addRow("Replicable shuffling:", self.__rand_seed_ch)
        self.layout().addLayout(form)

    def setParameters(self, params):
        rand_type = params.get("rand_type", Randomize.RandomizeClasses)
        self.__rand_type_cb.setCurrentIndex(
            enum_to_index(_Randomize.Type, rand_type))
        self.__rand_seed_ch.setChecked(params.get("rand_seed", 1) or 0)

    def parameters(self):
        return {"rand_type": index_to_enum(_Randomize.Type,
                                           self.__rand_type_cb.currentIndex()),
                "rand_seed": 1 if self.__rand_seed_ch.isChecked() else None}

    @staticmethod
    def createinstance(params):
        rand_type = params.get("rand_type", Randomize.RandomizeClasses)
        rand_seed = params.get("rand_seed", 1)
        return _Randomize(rand_type=rand_type, rand_seed=rand_seed)

    def __repr__(self):
        return "{}, {}".format(self.__rand_type_cb.currentText(),
                               "Replicable" if self.__rand_seed_ch.isChecked()
                               else "Not replicable")
Ejemplo n.º 14
0
class DiscreteVariableEditor(VariableEditor):
    valueChanged = Signal(int)

    def __init__(self, parent: QWidget, items: List[str], callback: Callable):
        super().__init__(parent, callback)
        self._combo = QComboBox(parent,
                                maximumWidth=180,
                                sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                       QSizePolicy.Fixed))
        self._combo.addItems(items)
        self._combo.currentIndexChanged.connect(self.valueChanged)
        self.layout().addWidget(self._combo)

    @property
    def value(self) -> int:
        return self._combo.currentIndex()

    @value.setter
    def value(self, value: float):
        assert value == int(value)
        self._combo.setCurrentIndex(int(value))
Ejemplo n.º 15
0
class LogarithmicScaleEditor(ScBaseEditor):
    DEFAULT_BASE = LogarithmicScale.BinaryLog

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.base_cb = QComboBox()
        self.base_cb.addItems([
            "2 (Binary Logarithm)", "e (Natural Logarithm)",
            "10 (Common Logarithm)"
        ])
        self.base_cb.currentIndexChanged.connect(self.changed)
        self.base_cb.activated.connect(self.edited)

        form.addRow("Logarithm Base:", self.base_cb)
        self.layout().addLayout(form)

    def setParameters(self, params):
        base = params.get("base", self.DEFAULT_BASE)
        self.base_cb.setCurrentIndex(enum_to_index(LogarithmicScale.Base,
                                                   base))

    def parameters(self):
        return {
            "base":
            index_to_enum(LogarithmicScale.Base, self.base_cb.currentIndex())
        }

    @staticmethod
    def createinstance(params):
        base = params.get("base", LogarithmicScaleEditor.DEFAULT_BASE)
        return LogarithmicScale(base)

    def __repr__(self):
        return "Base: {}".format(self.base_cb.currentText())
Ejemplo n.º 16
0
class SignInForm(QDialog):
    def __init__(self, flags, *args, **kwargs):
        super().__init__(flags, *args, **kwargs)
        self.cm: CredentialManager = CredentialManager(CREDENTIAL_MANAGER_SERVICE)

        self.setWindowTitle('Sign in')
        self.setFixedSize(400, 250)

        self.server_cb_label = QLabel('Server *')
        self.server_cb = QComboBox(self)
        self.server_cb.addItems(RESOLWE_URLS)
        self.server_cb.setEditable(True)

        self.username_label = QLabel('Username *')
        self.username_line_edit = QLineEdit(self)
        self.username_line_edit.setPlaceholderText('Enter correct username')
        self.username_line_edit.returnPressed.connect(self.sign_in)
        self.username_line_edit.textChanged.connect(self.handle_sign_in_btn)

        self.password_label = QLabel('Password *')
        self.password_line_edit = QLineEdit(self)
        self.password_line_edit.setPlaceholderText('Enter correct password')
        self.password_line_edit.returnPressed.connect(self.sign_in)
        self.password_line_edit.textChanged.connect(self.handle_sign_in_btn)
        self.password_line_edit.setEchoMode(QLineEdit.Password)

        self.sign_in_btn = QPushButton('Sign in', self)
        self.sign_in_btn.setDisabled(True)
        self.sign_in_btn.clicked.connect(self.sign_in)

        self.error_msg = QLabel('Unable to log in with provided credentials.')
        self.error_msg.setStyleSheet('color:red')
        self.error_msg.hide()

        layout = QVBoxLayout(self)
        layout.addWidget(self.server_cb_label)
        layout.addWidget(self.server_cb)
        layout.addWidget(self.username_label)
        layout.addWidget(self.username_line_edit)
        layout.addWidget(self.password_label)
        layout.addWidget(self.password_line_edit)
        layout.addWidget(self.error_msg)
        layout.addStretch()
        layout.addWidget(self.sign_in_btn)

        self.resolwe_instance = None

    def handle_sign_in_btn(self):
        self.sign_in_btn.setEnabled(
            True if self.username_line_edit.text() and self.password_line_edit.text() else False
        )

    def sign_in(self):
        self.server_cb_label.setStyleSheet(None)
        self.username_label.setStyleSheet(None)
        self.password_label.setStyleSheet(None)
        self.error_msg.hide()

        server = self.server_cb.currentText()
        username = self.cm.username if self.cm.username else self.username_line_edit.text()
        password = self.cm.password if self.cm.password else self.password_line_edit.text()

        if not server:
            self.server_cb_label.setStyleSheet('color:red')
            return

        if not username:
            self.username_label.setStyleSheet('color:red')
            return

        if not password:
            self.password_label.setStyleSheet('color:red')
            return

        try:
            self.resolwe_instance = connect(username, password, url=server)
        except ResolweAuthException:
            self.error_msg.show()
            return

        self.cm.username = username
        self.cm.password = password
        self.accept()
Ejemplo n.º 17
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130

    class Inputs:
        data = Input("Data", Orange.data.Table)
        learner = Input("Learner", Learner)

    class Outputs:
        data = Output("Data", Orange.data.Table)

    class Error(OWWidget.Error):
        imputation_failed = Msg("Imputation failed for '{}'")
        model_based_imputer_sparse = Msg("Model based imputer does not work for sparse data")

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(int(Method.Leave))  # type: int
    # Per-variable imputation state (synced in storeSpecificSettings)
    _variable_imputation_state = settings.ContextSetting({})  # type: VariableState

    autocommit = settings.Setting(True)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        self.data = None  # type: Optional[Orange.data.Table]
        self.learner = None  # type: Optional[Learner]
        self.default_learner = SimpleTreeLearner()
        self.modified = False
        self.executor = qconcurrent.ThreadExecutor(self)
        self.__task = None

        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QVBoxLayout(box)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)

        for method, _ in list(METHODS.items())[1:-1]:
            imputer = self.create_imputer(method)
            button = QRadioButton(imputer.name)
            button.setChecked(method == self.default_method_index)
            button_group.addButton(button, method)
            box_layout.addWidget(button)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = QListView(
            selectionMode=QListView.ExtendedSelection,
            uniformItemSizes=True
        )
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed
        )
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for method in Method:
            imputer = self.create_imputer(method)
            button = QRadioButton(text=imputer.name)
            button_group.addButton(button, method)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected
            )
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000., maximum=1000., singleStep=.1, decimals=3,
            )
        self.value_stack = value_stack = QStackedWidget()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addWidget(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection
        )

        method_layout.addStretch(2)

        reset_button = QPushButton(
                "Restore All to Default", checked=False, checkable=False,
                clicked=self.reset_variable_state, default=False,
                autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_commit(
            self.controlArea, self, "autocommit", "Apply",
            orientation=Qt.Horizontal,
            checkbox_label="Apply automatically")
        box.button.setFixedWidth(180)
        box.layout().insertStretch(0)

    def create_imputer(self, method, *args):
        # type: (Method, ...) -> impute.BaseImputeMethod
        if method == Method.Model:
            if self.learner is not None:
                return impute.Model(self.learner)
            else:
                return impute.Model(self.default_learner)
        elif method == Method.AsAboveSoBelow:
            assert self.default_method_index != Method.AsAboveSoBelow
            default = self.create_imputer(Method(self.default_method_index))
            m = AsDefault()
            m.method = default
            return m
        else:
            return METHODS[method](*args)

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            assert index != Method.AsAboveSoBelow
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            # update variable view
            self.update_varview()
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.varmodel[:] = []
        self._variable_imputation_state = {}  # type: VariableState
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)
            # restore per variable imputation state
            self._restore_state(self._variable_imputation_state)

        self.update_varview()
        self.unconditional_commit()

    @Inputs.learner
    def set_learner(self, learner):
        self.learner = learner or self.default_learner
        imputer = self.create_imputer(Method.Model)
        button = self.default_button_group.button(Method.Model)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(Method.Model)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = Method.Model

        self.update_varview()
        self.commit()

    def get_method_for_column(self, column_index):
        # type: (int) -> impute.BaseImputeMethod
        """
        Return the imputation method for column by its index.
        """
        assert 0 <= column_index < len(self.varmodel)
        idx = self.varmodel.index(column_index, 0)
        state = idx.data(StateRole)
        if state is None:
            state = (Method.AsAboveSoBelow, ())
        return self.create_imputer(state[0], *state[1])

    def _invalidate(self):
        self.modified = True
        if self.__task is not None:
            self.cancel()
        self.commit()

    def commit(self):
        self.cancel()
        self.warning()
        self.Error.imputation_failed.clear()
        self.Error.model_based_imputer_sparse.clear()

        if self.data is None or len(self.data) == 0 or len(self.varmodel) == 0:
            self.Outputs.data.send(self.data)
            self.modified = False
            return

        data = self.data
        impute_state = [
            (i, var, self.get_method_for_column(i))
            for i, var in enumerate(self.varmodel)
        ]
        # normalize to the effective method bypasing AsDefault
        impute_state = [
            (i, var, m.method if isinstance(m, AsDefault) else m)
            for i, var, m in impute_state
        ]

        def impute_one(method, var, data):
            # type: (impute.BaseImputeMethod, Variable, Table) -> Any
            if isinstance(method, impute.Model) and data.is_sparse():
                raise SparseNotSupported()
            elif isinstance(method, impute.DropInstances):
                return RowMask(method(data, var))
            elif not method.supports_variable(var):
                raise VariableNotSupported(var)
            else:
                return method(data, var)

        futures = []
        for _, var, method in impute_state:
            f = self.executor.submit(
                impute_one, copy.deepcopy(method), var, data)
            futures.append(f)

        w = qconcurrent.FutureSetWatcher(futures)
        w.doneAll.connect(self.__commit_finish)
        w.progressChanged.connect(self.__progress_changed)
        self.__task = Task(futures, w)
        self.progressBarInit(processEvents=False)
        self.setBlocking(True)

    @Slot()
    def __commit_finish(self):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        futures = self.__task.futures
        assert len(futures) == len(self.varmodel)
        assert self.data is not None

        self.__task = None
        self.setBlocking(False)
        self.progressBarFinished()

        data = self.data
        attributes = []
        class_vars = []
        drop_mask = np.zeros(len(self.data), bool)

        for i, (var, fut) in enumerate(zip(self.varmodel, futures)):
            assert fut.done()
            newvar = []
            try:
                res = fut.result()
            except SparseNotSupported:
                self.Error.model_based_imputer_sparse()
                # ?? break
            except VariableNotSupported:
                self.warning("Default method can not handle '{}'".
                             format(var.name))
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error for %s", var, exc_info=True)
                self.Error.imputation_failed(var.name)
                attributes = class_vars = None
                break
            else:
                if isinstance(res, RowMask):
                    drop_mask |= res.mask
                    newvar = var
                else:
                    newvar = res

            if isinstance(newvar, Orange.data.Variable):
                newvar = [newvar]

            if i < len(data.domain.attributes):
                attributes.extend(newvar)
            else:
                class_vars.extend(newvar)

        if attributes is None:
            data = None
        else:
            domain = Orange.data.Domain(attributes, class_vars,
                                        data.domain.metas)
            try:
                data = self.data.from_table(domain, data[~drop_mask])
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error", exc_info=True)
                self.Error.imputation_failed("Unknown")
                data = None

        self.Outputs.data.send(data)
        self.modified = False

    @Slot(int, int)
    def __progress_changed(self, n, d):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        self.progressBarSet(100. * n / d)

    def cancel(self):
        if self.__task is not None:
            task, self.__task = self.__task, None
            task.cancel()
            task.watcher.doneAll.disconnect(self.__commit_finish)
            task.watcher.progressChanged.disconnect(self.__progress_changed)
            concurrent.futures.wait(task.futures)
            task.watcher.flush()
            self.progressBarFinished()
            self.setBlocking(False)

    def onDeleteWidget(self):
        self.cancel()
        super().onDeleteWidget()

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.get_method_for_column(i)
            if not isinstance(method, AsDefault):
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.create_imputer(Method.AsAboveSoBelow)
        if specific:
            self.report_items((
                ("Default method", default.name),
                ("Specific imputers", ", ".join(specific))
            ))
        else:
            self.report_items((("Method", default.name),))

    def _on_var_selection_changed(self):
        indexes = self.selection.selectedIndexes()
        defmethod = (Method.AsAboveSoBelow, ())
        methods = [index.data(StateRole) for index in indexes]
        methods = [m if m is not None else defmethod for m in methods]
        methods = set(methods)
        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)
        fixed_value = None
        value_stack_enabled = False
        current_value_widget = None

        if len(methods) == 1:
            method_type, parameters = methods.pop()
            for m in Method:
                if method_type == m:
                    self.variable_button_group.button(m).setChecked(True)

            if method_type == Method.Default:
                (fixed_value,) = parameters

        elif self.variable_button_group.checkedButton() is not None:
            # Uncheck the current button
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)
            assert self.variable_button_group.checkedButton() is None

        # Update variable methods GUI enabled state based on selection.
        for method in Method:
            # use a default constructed imputer to query support
            imputer = self.create_imputer(method)
            enabled = all(imputer.supports_variable(var) for var in
                          selected_vars)
            button = self.variable_button_group.button(method)
            button.setEnabled(enabled)

        # Update the "Value" edit GUI.
        if not has_discrete:
            # no discrete variables -> allow mass edit for all (continuous vars)
            value_stack_enabled = True
            current_value_widget = self.value_double
        elif len(selected_vars) == 1:
            # single discrete var -> enable and fill the values combo
            value_stack_enabled = True
            current_value_widget = self.value_combo
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
        else:
            # mixed type selection -> disable
            value_stack_enabled = False
            current_value_widget = None
            self.variable_button_group.button(Method.Default).setEnabled(False)

        self.value_stack.setEnabled(value_stack_enabled)
        if current_value_widget is not None:
            self.value_stack.setCurrentWidget(current_value_widget)
            if fixed_value is not None:
                # set current value
                if current_value_widget is self.value_combo:
                    self.value_combo.setCurrentIndex(fixed_value)
                elif current_value_widget is self.value_double:
                    self.value_double.setValue(fixed_value)
                else:
                    assert False

    def set_method_for_current_selection(self, method_index):
        # type: (Method) -> None
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        # type: (List[QModelIndex], Method) -> None
        if method_index == Method.AsAboveSoBelow:
            for index in indexes:
                self.varmodel.setData(index, None, StateRole)
        elif method_index == Method.Default:
            current = self.value_stack.currentWidget()
            if current is self.value_combo:
                value = self.value_combo.currentIndex()
            else:
                value = self.value_double.value()
            for index in indexes:
                state = (int(Method.Default), (value,))
                self.varmodel.setData(index, state, StateRole)
        else:
            state = (int(method_index), ())
            for index in indexes:
                self.varmodel.setData(index, state, StateRole)

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(
                index, self.get_method_for_column(index.row()),
                DisplayMethodRole)

    def _on_value_selected(self):
        # The fixed 'Value' in the widget has been changed by the user.
        self.variable_button_group.button(Method.Default).setChecked(True)
        self.set_method_for_current_selection(Method.Default)

    def reset_variable_state(self):
        indexes = list(map(self.varmodel.index, range(len(self.varmodel))))
        self.set_method_for_indexes(indexes, Method.AsAboveSoBelow)
        self.variable_button_group.button(Method.AsAboveSoBelow).setChecked(True)

    def _store_state(self):
        # type: () -> VariableState
        """
        Save the current variable imputation state
        """
        state = {}  # type: VariableState
        for i, var in enumerate(self.varmodel):
            index = self.varmodel.index(i)
            m = index.data(StateRole)
            if m is not None:
                state[var_key(var)] = m
        return state

    def _restore_state(self, state):
        # type: (VariableState) -> None
        """
        Restore the variable imputation state from the saved state
        """
        def check(state):
            # check if state is a proper State
            if isinstance(state, tuple) and len(state) == 2:
                m, p = state
                if isinstance(m, int) and isinstance(p, tuple) and \
                        0 <= m < len(Method):
                    return True
            return False

        for i, var in enumerate(self.varmodel):
            m = state.get(var_key(var), None)
            if check(m):
                self.varmodel.setData(self.varmodel.index(i), m, StateRole)

    def storeSpecificSettings(self):
        self._variable_imputation_state = self._store_state()
        super().storeSpecificSettings()
Ejemplo n.º 18
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar()

            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction)

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        self.stack = central

        self.setCentralWidget(central)

        # General Tab
        tab = QWidget()
        self.addTab(tab,
                    self.tr("General"),
                    toolTip=self.tr("General Options"))

        form = QFormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(self.tr("Enable node animations"),
                            objectName="enable-node-animations",
                            toolTip=self.tr(
                                "Enable shadow and ping animations for nodes "
                                "in the workflow."))
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("Nodes"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(self.tr("Show channel names between widgets"),
                            objectName="show-channel-names",
                            toolTip=self.tr(
                                "Show source and sink channel names "
                                "over the links."))

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("Links"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("On double click"),
                        toolTip=self.tr("Open quick menu on a double click "
                                        "on an empty spot in the canvas"))

        cb2 = QCheckBox(self.tr("On right click"),
                        toolTip=self.tr("Open quick menu on a right click "
                                        "on an empty spot in the canvas"))

        cb3 = QCheckBox(self.tr("On space key press"),
                        toolTip=self.tr("On Space key press while the mouse"
                                        "is hovering over the canvas."))

        cb4 = QCheckBox(self.tr("On any key press"),
                        toolTip=self.tr("On any key press while the mouse"
                                        "is hovering over the canvas."))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)

        form.addRow(self.tr("Open quick menu on"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("Show splash screen"),
                              self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("Show welcome screen"),
                               self,
                               objectName="show-welcome-screen")

        cb_updates = QCheckBox(self.tr("Check for updates"),
                               self,
                               objectName="check-updates")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")
        self.bind(cb_updates, "checked", "startup/check-updates")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)
        startup.layout().addWidget(cb_updates)

        form.addRow(self.tr("On startup"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("Only one tab can be open at a time"))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("Tool box"), toolbox)
        tab.setLayout(form)

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Output"), toolTip="Output Redirection")

        form = QFormLayout()

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        combo = QComboBox()
        combo.addItems([
            self.tr("Critical"),
            self.tr("Error"),
            self.tr("Warn"),
            self.tr("Info"),
            self.tr("Debug")
        ])
        self.bind(combo, "currentIndex", "logging/level")
        layout.addWidget(combo)
        box.setLayout(layout)
        form.addRow(self.tr("Logging"), box)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("Open in external browser"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("Help window"), box)

        tab.setLayout(form)

        # Error Reporting Tab
        tab = QWidget()
        self.addTab(tab,
                    self.tr("Error Reporting"),
                    toolTip="Settings related to error reporting")

        form = QFormLayout()
        line_edit_mid = QLineEdit()
        self.bind(line_edit_mid, "text", "error-reporting/machine-id")
        form.addRow("Machine ID:", line_edit_mid)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
Ejemplo n.º 19
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar(
                floatable=False, movable=False, allowedAreas=Qt.TopToolBarArea,
            )
            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction
            )

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        # Add a close button to the bottom of the dialog
        # (to satisfy GNOME 3 which shows the dialog  without a title bar).
        container = container_widget_helper()
        container.layout().addWidget(central)
        buttonbox = QDialogButtonBox(QDialogButtonBox.Close)
        buttonbox.rejected.connect(self.close)
        container.layout().addWidget(buttonbox)

        self.setCentralWidget(container)

        self.stack = central

        # General Tab
        tab = QWidget()
        self.addTab(tab, self.tr("General"),
                    toolTip=self.tr("General Options"))

        form = QFormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(
            self.tr("Enable node animations"),
            objectName="enable-node-animations",
            toolTip=self.tr("Enable shadow and ping animations for nodes "
                            "in the workflow.")
        )
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("Nodes"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(
            self.tr("Show channel names between widgets"),
            objectName="show-channel-names",
            toolTip=self.tr("Show source and sink channel names "
                            "over the links.")
        )

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("Links"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("On double click"),
                        toolTip=self.tr("Open quick menu on a double click "
                                        "on an empty spot in the canvas"))

        cb2 = QCheckBox(self.tr("On right click"),
                        toolTip=self.tr("Open quick menu on a right click "
                                        "on an empty spot in the canvas"))

        cb3 = QCheckBox(self.tr("On space key press"),
                        toolTip=self.tr("On Space key press while the mouse"
                                        "is hovering over the canvas."))

        cb4 = QCheckBox(self.tr("On any key press"),
                        toolTip=self.tr("On any key press while the mouse"
                                        "is hovering over the canvas."))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)

        form.addRow(self.tr("Open quick menu on"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("Show splash screen"), self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("Show welcome screen"), self,
                                objectName="show-welcome-screen")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)

        form.addRow(self.tr("On startup"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("Only one tab can be open at a time"))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("Tool box"), toolbox)
        tab.setLayout(form)

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Output"),
                    toolTip="Output Redirection")

        form = QFormLayout()

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        combo = QComboBox()
        combo.addItems([self.tr("Critical"),
                        self.tr("Error"),
                        self.tr("Warn"),
                        self.tr("Info"),
                        self.tr("Debug")])
        self.bind(combo, "currentIndex", "logging/level")
        layout.addWidget(combo)
        box.setLayout(layout)
        form.addRow(self.tr("Logging"), box)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("Open in external browser"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("Help window"), box)

        tab.setLayout(form)

        # Categories Tab
        tab = QWidget()
        layout = QVBoxLayout()
        view = QListView(
            editTriggers=QListView.NoEditTriggers
        )
        from .. import registry
        reg = registry.global_registry()
        model = QStandardItemModel()
        settings = QSettings()
        for cat in reg.categories():
            item = QStandardItem()
            item.setText(cat.name)
            item.setCheckable(True)
            visible, _ = category_state(cat, settings)
            item.setCheckState(Qt.Checked if visible else Qt.Unchecked)
            model.appendRow([item])

        view.setModel(model)
        layout.addWidget(view)
        tab.setLayout(layout)
        model.itemChanged.connect(
            lambda item:
                save_category_state(
                    reg.category(str(item.text())),
                    _State(item.checkState() == Qt.Checked, -1),
                    settings
                )
        )

        self.addTab(tab, "Categories")

        # Add-ons Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Add-ons"),
                    toolTip="Settings related to add-on installation")

        form = QFormLayout()
        conda = QWidget(self, objectName="conda-group")
        conda.setLayout(QVBoxLayout())
        conda.layout().setContentsMargins(0, 0, 0, 0)

        cb_conda_install = QCheckBox(self.tr("Install add-ons with conda"), self,
                                     objectName="allow-conda-experimental")
        self.bind(cb_conda_install, "checked", "add-ons/allow-conda-experimental")
        conda.layout().addWidget(cb_conda_install)

        form.addRow(self.tr("Conda"), conda)

        form.addRow(self.tr("Pip"), QLabel("Pip install arguments:"))
        line_edit_pip = QLineEdit()
        self.bind(line_edit_pip, "text", "add-ons/pip-install-arguments")
        form.addRow("", line_edit_pip)

        tab.setLayout(form)

        # Network Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Network"),
                    toolTip="Settings related to networking")

        form = QFormLayout()
        line_edit_http_proxy = QLineEdit()
        self.bind(line_edit_http_proxy, "text", "network/http-proxy")
        form.addRow("HTTP proxy:", line_edit_http_proxy)
        line_edit_https_proxy = QLineEdit()
        self.bind(line_edit_https_proxy, "text", "network/https-proxy")
        form.addRow("HTTPS proxy:", line_edit_https_proxy)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
Ejemplo n.º 20
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar(
                floatable=False, movable=False, allowedAreas=Qt.TopToolBarArea,
            )
            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction
            )

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        # Add a close button to the bottom of the dialog
        # (to satisfy GNOME 3 which shows the dialog  without a title bar).
        container = container_widget_helper()
        container.layout().addWidget(central)
        buttonbox = QDialogButtonBox(QDialogButtonBox.Close)
        buttonbox.rejected.connect(self.close)
        container.layout().addWidget(buttonbox)

        self.setCentralWidget(container)

        self.stack = central

        # General Tab
        tab = QWidget()
        self.addTab(tab, self.tr("常规"),
                    toolTip=self.tr("常规选项"))

        form = FormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(
            self.tr("启用节点动画"),
            objectName="enable-node-animations",
            toolTip=self.tr("为工作流中的节点启用阴影和ping动画。")
        )
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("结点"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(
            self.tr("在窗口小部件之间显示通道名称"),
            objectName="show-channel-names",
            toolTip=self.tr("在链接上显示源和接收器通道名称。")
        )

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("链接"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("双击时打开"),
                        toolTip=self.tr("双击画布中的空白位置打开快捷菜单"))

        cb2 = QCheckBox(self.tr("单击鼠标右键时打开"),
                        toolTip=self.tr("右键单击画布中的空白处打开快捷菜单"))

        cb3 = QCheckBox(self.tr("按空格键时打开"),
                        toolTip=self.tr("当鼠标悬停在画布上时,按空格键。"))

        cb4 = QCheckBox(self.tr("按任意按键时打开"),
                        toolTip=self.tr("当鼠标悬停在画布上时,按任意键。"))

        cb5 = QCheckBox(self.tr("显示分类"),
                        toolTip=self.tr("In addition to searching, allow filtering "
                                        "by categories."))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")
        self.bind(cb5, "checked", "quickmenu/show-categories")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)
        quickmenu.layout().addWidget(cb5)

        form.addRow(self.tr("快捷菜单"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("显示启动画面"), self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("显示欢迎界面"), self,
                               objectName="show-welcome-screen")

        cb_crash = QCheckBox(self.tr("加载崩溃的工作流"), self,
                             objectName="load-crashed-workflows")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")
        self.bind(cb_crash, "checked", "startup/load-crashed-workflows")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)
        startup.layout().addWidget(cb_crash)

        form.addRow(self.tr("启动时"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("一次只能打开一个选项卡"))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("工具箱"), toolbox)
        tab.setLayout(form)

        # Style tab
        tab = StyleConfigWidget()
        self.addTab(tab, self.tr("&Style"), toolTip="Application style")
        self.bind(tab, "selectedStyle_", "application-style/style-name")
        self.bind(tab, "selectedPalette_", "application-style/palette")

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("输出"),
                    toolTip="输出重定向")

        form = FormLayout()

        combo = QComboBox()
        combo.addItems([self.tr("关键"),
                        self.tr("错误"),
                        self.tr("警告"),
                        self.tr("信息"),
                        self.tr("调试")])
        self.bind(combo, "currentIndex", "logging/level")
        form.addRow(self.tr("记录"), combo)
        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("在外部浏览器中打开"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("帮助窗口"), box)

        tab.setLayout(form)

        # Categories Tab
        tab = QWidget()
        layout = QVBoxLayout()
        view = QListView(
            editTriggers=QListView.NoEditTriggers
        )
        from .. import registry
        reg = registry.global_registry()
        model = QStandardItemModel()
        settings = QSettings()
        for cat in reg.categories():
            item = QStandardItem()
            item.setText(cat.name)
            item.setCheckable(True)
            visible, _ = category_state(cat, settings)
            item.setCheckState(Qt.Checked if visible else Qt.Unchecked)
            model.appendRow([item])

        view.setModel(model)
        layout.addWidget(view)
        tab.setLayout(layout)
        model.itemChanged.connect(
            lambda item:
                save_category_state(
                    reg.category(str(item.text())),
                    _State(item.checkState() == Qt.Checked, -1),
                    settings
                )
        )

        self.addTab(tab, "类别")

        # Add-ons Tab
        tab = QWidget()
        self.addTab(tab, self.tr("插件"),
                    toolTip="与插件安装相关的设置")

        form = FormLayout()
        conda = QWidget(self, objectName="conda-group")
        conda.setLayout(QVBoxLayout())
        conda.layout().setContentsMargins(0, 0, 0, 0)

        mirror_install = QCheckBox(self.tr("使用国内镜像安装"), self,
                                     objectName="allow-conda")
        self.bind(mirror_install, "checked", "add-ons/allow-conda")
        conda.layout().addWidget(mirror_install)

        form.addRow(self.tr("镜像"), conda)

        form.addRow(self.tr("Pip"), QLabel("Pip 安装参数:"))
        line_edit_pip = QLineEdit()
        # line_edit_pip.setText('-i https://mirrors.aliyun.com/pypi/simple')
        self.bind(line_edit_pip, "text", "add-ons/pip-install-arguments")
        form.addRow("", line_edit_pip)

        tab.setLayout(form)

        # Network Tab
        tab = QWidget()
        self.addTab(tab, self.tr("网络"),
                    toolTip="与网络相关的设置")

        form = FormLayout()
        line_edit_http_proxy = QLineEdit()
        self.bind(line_edit_http_proxy, "text", "network/http-proxy")
        form.addRow("HTTP 代理:", line_edit_http_proxy)
        line_edit_https_proxy = QLineEdit()
        self.bind(line_edit_https_proxy, "text", "network/https-proxy")
        form.addRow("HTTPS 代理:", line_edit_https_proxy)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
class FileUploadHelper(QDialog):

    # settings
    kegg_domain = 'KEGG'

    supported_domains = OrderedDict({
        'Gene Ontology': gene_ontology_domain,
        'Gene Sets': gene_sets_domain
    })

    supported_organisms = [
        common_taxid_to_name(tax_id) for tax_id in common_taxids()
    ]

    hierarchies = {
        'GO - Biological Process': ('GO', 'biological_process'),
        'GO - Molecular Function': ('GO', 'molecular_function'),
        'GO - Cellular Component': ('GO', 'cellular_component'),
        'KEGG - Pathways': ('KEGG', 'pathways'),
        'KEGG - Orthologs': ('KEGG', 'orthologs')
    }

    def __init__(self, parent=None):
        super(FileUploadHelper, self).__init__(
            parent, Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint
            | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle('Add new file')

        self.info_state = INFO_FILE_SCHEMA
        self.layout = QVBoxLayout(self)

        # domain selection combobox
        self.domain_selection = QComboBox()
        self.domain_selection.addItems(self.supported_domains.keys())
        self.domain_selection.currentIndexChanged.connect(
            self.__on_domain_selection)
        self.__create_selection_row('Domain: ', self.domain_selection)

        # domain selection combobox
        self.hierarchy_selection = QComboBox()
        self.hierarchy_selection.addItems(self.hierarchies.keys())
        self.layout.addWidget(self.hierarchy_selection,
                              alignment=Qt.AlignVCenter)
        self.__on_domain_selection()

        # select organism
        self.organism_selection = QComboBox()
        self.organism_selection.addItems(self.supported_organisms)
        self.__create_selection_row('Organism: ', self.organism_selection)

        # title
        self.line_edit_title = QLineEdit()
        self.__create_selection_row('Title: ', self.line_edit_title)

        # tags
        self.line_edit_tags = QLineEdit()
        self.__create_selection_row('Tags (comma-separated): ',
                                    self.line_edit_tags)

        # file selector
        self.file_info = QLabel()
        self.file_select_btn = QPushButton('Select File', self)
        self.file_select_btn.clicked.connect(self.__handle_file_selector)
        self.__create_selection_row(' ', self.file_select_btn)

        # add file info section
        self.layout.addWidget(self.file_info, alignment=Qt.AlignCenter)

        self.layout.addStretch(1)

        # Ok and Cancel buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        self.layout.addWidget(self.buttons, alignment=Qt.AlignJustify)

        self.buttons.accepted.connect(self.__accept)
        self.buttons.rejected.connect(self.__close)

        # path to a selected file
        self.file_path = None

    def __on_domain_selection(self):
        selected = self.__get_selected_domain() == gene_sets_domain
        self.hierarchy_selection.setVisible(selected)

    def __get_selected_domain(self):
        domain_label = list(self.supported_domains.keys())[
            self.domain_selection.currentIndex()]
        return self.supported_domains[domain_label]

    def __get_selected_hier(self):
        hier_label = list(
            self.hierarchies.keys())[self.hierarchy_selection.currentIndex()]
        return self.hierarchies[hier_label]

    def __create_selection_row(self, label, widget):
        self.layout.addWidget(QLabel(label), alignment=Qt.AlignLeft)
        self.layout.addWidget(widget, alignment=Qt.AlignVCenter)

    def __accept(self):
        if self.file_path:
            self.info_state = self.__parse_selection()
            self.__move_to_serverfiles_folder(self.file_path)

            self.parent().initialize_files_view()
            self.close()

    def __close(self):
        self.close()

    def closeEvent(self, event):
        # clean-up
        self.parent()._dialog = None

    def __filename(self, domain, organism):
        """ Create filename based od domain name and organism.
        """

        if domain in self.supported_domains.values(
        ) and domain == gene_ontology_domain and organism:
            return FILENAME_ANNOTATION.format(organism)

        elif domain in self.supported_domains.values(
        ) and domain == gene_sets_domain and organism:
            return filename((self.__get_selected_hier()), organism)

    def __parse_selection(self):
        try:
            domain = self.__get_selected_domain()
            organism = taxname_to_taxid(self.supported_organisms[
                self.organism_selection.currentIndex()])
        except KeyError as e:
            raise e

        return {
            'domain': domain,
            'organism': organism,
            'filename': self.__filename(domain, organism),
            'title': self.line_edit_title.text(),
            'tags': self.line_edit_tags.text().split(','),
            'source': SOURCE_USER
        }

    def __move_to_serverfiles_folder(self, selected_file_path):
        domain_path = serverfiles.localpath(self.info_state['domain'])
        file_path = os.path.join(domain_path, self.info_state['filename'])
        create_folder(domain_path)

        try:
            copyfile(selected_file_path, file_path)
        except IOError as e:
            # TODO: handle error properly
            raise e

        # if copy successful create .info file
        create_info_file(file_path, **self.info_state)

    def __handle_file_selector(self):
        self.file_path = QFileDialog.getOpenFileName(self, 'Open File')[0]
        self.file_info.setText('Selected File: {}'.format(
            os.path.basename(self.file_path)))
Ejemplo n.º 22
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar(
                floatable=False,
                movable=False,
                allowedAreas=Qt.TopToolBarArea,
            )
            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction)

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        # Add a close button to the bottom of the dialog
        # (to satisfy GNOME 3 which shows the dialog  without a title bar).
        container = container_widget_helper()
        container.layout().addWidget(central)
        buttonbox = QDialogButtonBox(QDialogButtonBox.Close)
        buttonbox.rejected.connect(self.close)
        container.layout().addWidget(buttonbox)

        self.setCentralWidget(container)

        self.stack = central

        # General Tab
        tab = QWidget()
        self.addTab(tab,
                    self.tr("General"),
                    toolTip=self.tr("General Options"))

        form = FormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(self.tr("Enable node animations"),
                            objectName="enable-node-animations",
                            toolTip=self.tr(
                                "Enable shadow and ping animations for nodes "
                                "in the workflow."))
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("Nodes"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(self.tr("Show channel names between widgets"),
                            objectName="show-channel-names",
                            toolTip=self.tr(
                                "Show source and sink channel names "
                                "over the links."))

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("Links"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("Open on double click"),
                        toolTip=self.tr("Open quick menu on a double click "
                                        "on an empty spot in the canvas"))

        cb2 = QCheckBox(self.tr("Open on right click"),
                        toolTip=self.tr("Open quick menu on a right click "
                                        "on an empty spot in the canvas"))

        cb3 = QCheckBox(self.tr("Open on space key press"),
                        toolTip=self.tr(
                            "Open quick menu on Space key press "
                            "while the mouse is hovering over the canvas."))

        cb4 = QCheckBox(self.tr("Open on any key press"),
                        toolTip=self.tr(
                            "Open quick menu on any key press "
                            "while the mouse is hovering over the canvas."))

        cb5 = QCheckBox(self.tr("Show categories"),
                        toolTip=self.tr(
                            "In addition to searching, allow filtering "
                            "by categories."))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")
        self.bind(cb5, "checked", "quickmenu/show-categories")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)
        quickmenu.layout().addWidget(cb5)

        form.addRow(self.tr("Quick menu"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("Show splash screen"),
                              self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("Show welcome screen"),
                               self,
                               objectName="show-welcome-screen")

        cb_crash = QCheckBox(self.tr("Load crashed scratch workflows"),
                             self,
                             objectName="load-crashed-workflows")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")
        self.bind(cb_crash, "checked", "startup/load-crashed-workflows")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)
        startup.layout().addWidget(cb_crash)

        form.addRow(self.tr("On startup"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("Only one tab can be open at a time"))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("Tool box"), toolbox)
        tab.setLayout(form)

        # Style tab
        tab = StyleConfigWidget()
        self.addTab(tab, self.tr("&Style"), toolTip="Application style")
        self.bind(tab, "selectedStyle_", "application-style/style-name")
        self.bind(tab, "selectedPalette_", "application-style/palette")

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Output"), toolTip="Output Redirection")

        form = FormLayout()

        combo = QComboBox()
        combo.addItems([
            self.tr("Critical"),
            self.tr("Error"),
            self.tr("Warn"),
            self.tr("Info"),
            self.tr("Debug")
        ])
        self.bind(combo, "currentIndex", "logging/level")
        form.addRow(self.tr("Logging"), combo)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("Open in external browser"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("Help window"), box)

        tab.setLayout(form)

        # Categories Tab
        tab = QWidget()
        layout = QVBoxLayout()
        view = QListView(editTriggers=QListView.NoEditTriggers)
        from .. import registry
        reg = registry.global_registry()
        model = QStandardItemModel()
        settings = QSettings()
        for cat in reg.categories():
            item = QStandardItem()
            item.setText(cat.name)
            item.setCheckable(True)
            visible, _ = category_state(cat, settings)
            item.setCheckState(Qt.Checked if visible else Qt.Unchecked)
            model.appendRow([item])

        view.setModel(model)
        layout.addWidget(view)
        tab.setLayout(layout)
        model.itemChanged.connect(lambda item: save_category_state(
            reg.category(str(item.text())),
            _State(item.checkState() == Qt.Checked, -1), settings))

        self.addTab(tab, "Categories")

        # Add-ons Tab
        tab = QWidget()
        self.addTab(tab,
                    self.tr("Add-ons"),
                    toolTip="Settings related to add-on installation")

        form = FormLayout()
        conda = QWidget(self, objectName="conda-group")
        conda.setLayout(QVBoxLayout())
        conda.layout().setContentsMargins(0, 0, 0, 0)

        cb_conda_install = QCheckBox(self.tr("Install add-ons with conda"),
                                     self,
                                     objectName="allow-conda")
        self.bind(cb_conda_install, "checked", "add-ons/allow-conda")
        conda.layout().addWidget(cb_conda_install)

        form.addRow(self.tr("Conda"), conda)

        form.addRow(self.tr("Pip"), QLabel("Pip install arguments:"))
        line_edit_pip = QLineEdit()
        self.bind(line_edit_pip, "text", "add-ons/pip-install-arguments")
        form.addRow("", line_edit_pip)

        tab.setLayout(form)

        # Network Tab
        tab = QWidget()
        self.addTab(tab,
                    self.tr("Network"),
                    toolTip="Settings related to networking")

        form = FormLayout()
        line_edit_http_proxy = QLineEdit()
        self.bind(line_edit_http_proxy, "text", "network/http-proxy")
        form.addRow("HTTP proxy:", line_edit_http_proxy)
        line_edit_https_proxy = QLineEdit()
        self.bind(line_edit_https_proxy, "text", "network/https-proxy")
        form.addRow("HTTPS proxy:", line_edit_https_proxy)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
Ejemplo n.º 23
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130

    class Inputs:
        data = Input("Data", Orange.data.Table)
        learner = Input("Learner", Learner)

    class Outputs:
        data = Output("Data", Orange.data.Table)

    class Error(OWWidget.Error):
        imputation_failed = Msg("Imputation failed for '{}'")
        model_based_imputer_sparse = Msg("Model based imputer does not work for sparse data")

    DEFAULT_LEARNER = SimpleTreeLearner()
    METHODS = [AsDefault(), impute.DoNotImpute(), impute.Average(),
               impute.AsValue(), impute.Model(DEFAULT_LEARNER), impute.Random(),
               impute.DropInstances(), impute.Default()]
    DEFAULT, DO_NOT_IMPUTE, MODEL_BASED_IMPUTER, AS_INPUT = 0, 1, 4, 7

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(DO_NOT_IMPUTE)
    variable_methods = settings.ContextSetting({})
    autocommit = settings.Setting(True)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        # copy METHODS (some are modified by the widget)
        self.methods = copy.deepcopy(OWImpute.METHODS)

        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QVBoxLayout(box)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)
        for i, method in enumerate(self.methods):
            if not method.columns_only:
                button = QRadioButton(method.name)
                button.setChecked(i == self.default_method_index)
                button_group.addButton(button, i)
                box_layout.addWidget(button)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = QListView(
            selectionMode=QListView.ExtendedSelection
        )
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed
        )
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for i, method in enumerate(self.methods):
            button = QRadioButton(text=method.name)
            button_group.addButton(button, i)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected
            )
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000., maximum=1000., singleStep=.1, decimals=3,
            )
        self.value_stack = value_stack = QStackedWidget()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addWidget(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection
        )

        method_layout.addStretch(2)

        reset_button = QPushButton(
                "Restore All to Default", checked=False, checkable=False,
                clicked=self.reset_variable_methods, default=False,
                autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_commit(
            self.controlArea, self, "autocommit", "Apply",
            orientation=Qt.Horizontal, checkbox_label="Apply automatically")
        box.layout().insertSpacing(0, 80)
        box.layout().insertWidget(0, self.report_button)

        self.data = None
        self.learner = None
        self.modified = False
        self.default_method = self.methods[self.default_method_index]
        self.executor = qconcurrent.ThreadExecutor(self)
        self.__task = None

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            self.default_method = self.methods[self.default_method_index]
            self.methods[self.DEFAULT].method = self.default_method

            # update variable view
            for index in map(self.varmodel.index, range(len(self.varmodel))):
                method = self.variable_methods.get(
                    index.row(), self.methods[self.DEFAULT])
                self.varmodel.setData(index, method, Qt.UserRole)
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.varmodel[:] = []
        self.variable_methods = {}
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)

        self.update_varview()
        self.unconditional_commit()

    @Inputs.learner
    def set_learner(self, learner):
        self.learner = learner or self.DEFAULT_LEARNER
        imputer = self.methods[self.MODEL_BASED_IMPUTER]
        imputer.learner = self.learner

        button = self.default_button_group.button(self.MODEL_BASED_IMPUTER)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(self.MODEL_BASED_IMPUTER)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = self.MODEL_BASED_IMPUTER

        self.update_varview()
        self.commit()

    def get_method_for_column(self, column_index):
        """Returns the imputation method for column by its index.
        """
        if not isinstance(column_index, int):
            column_index = column_index.row()

        return self.variable_methods.get(column_index,
                                         self.methods[self.DEFAULT])

    def _invalidate(self):
        self.modified = True
        if self.__task is not None:
            self.cancel()
        self.commit()

    def commit(self):
        self.cancel()
        self.warning()
        self.Error.imputation_failed.clear()
        self.Error.model_based_imputer_sparse.clear()

        if self.data is None or len(self.data) == 0 or len(self.varmodel) == 0:
            self.Outputs.data.send(self.data)
            self.modified = False
            return

        data = self.data
        impute_state = [
            (i, var, self.variable_methods.get(i, self.default_method))
            for i, var in enumerate(self.varmodel)
        ]

        def impute_one(method, var, data):
            # type: (impute.BaseImputeMethod, Variable, Table) -> Any
            if isinstance(method, impute.Model) and data.is_sparse():
                raise SparseNotSupported()
            elif isinstance(method, impute.DropInstances):
                return RowMask(method(data, var))
            elif not method.supports_variable(var):
                raise VariableNotSupported(var)
            else:
                return method(data, var)

        futures = []
        for _, var, method in impute_state:
            f = self.executor.submit(
                impute_one, copy.deepcopy(method), var, data)
            futures.append(f)

        w = qconcurrent.FutureSetWatcher(futures)
        w.doneAll.connect(self.__commit_finish)
        w.progressChanged.connect(self.__progress_changed)
        self.__task = Task(futures, w)
        self.progressBarInit(processEvents=False)
        self.setBlocking(True)

    @Slot()
    def __commit_finish(self):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        futures = self.__task.futures
        assert len(futures) == len(self.varmodel)
        assert self.data is not None

        self.__task = None
        self.setBlocking(False)
        self.progressBarFinished()

        data = self.data
        attributes = []
        class_vars = []
        drop_mask = np.zeros(len(self.data), bool)

        for i, (var, fut) in enumerate(zip(self.varmodel, futures)):
            assert fut.done()
            newvar = []
            try:
                res = fut.result()
            except SparseNotSupported:
                self.Error.model_based_imputer_sparse()
                # ?? break
            except VariableNotSupported:
                self.warning("Default method can not handle '{}'".
                             format(var.name))
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error for %s", var, exc_info=True)
                self.Error.imputation_failed(var.name)
                attributes = class_vars = None
                break
            else:
                if isinstance(res, RowMask):
                    drop_mask |= res.mask
                    newvar = var
                else:
                    newvar = res

            if isinstance(newvar, Orange.data.Variable):
                newvar = [newvar]

            if i < len(data.domain.attributes):
                attributes.extend(newvar)
            else:
                class_vars.extend(newvar)

        if attributes is None:
            data = None
        else:
            domain = Orange.data.Domain(attributes, class_vars,
                                        data.domain.metas)
            try:
                data = self.data.from_table(domain, data[~drop_mask])
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error", exc_info=True)
                self.Error.imputation_failed("Unknown")
                data = None

        self.Outputs.data.send(data)
        self.modified = False

    @Slot(int, int)
    def __progress_changed(self, n, d):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        self.progressBarSet(100. * n / d)

    def cancel(self):
        if self.__task is not None:
            task, self.__task = self.__task, None
            task.cancel()
            task.watcher.doneAll.disconnect(self.__commit_finish)
            task.watcher.progressChanged.disconnect(self.__progress_changed)
            concurrent.futures.wait(task.futures)
            task.watcher.flush()
            self.progressBarFinished()
            self.setBlocking(False)

    def onDeleteWidget(self):
        self.cancel()
        super().onDeleteWidget()

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.variable_methods.get(i, None)
            if method is not None:
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.default_method.name
        if specific:
            self.report_items((
                ("Default method", default),
                ("Specific imputers", ", ".join(specific))
            ))
        else:
            self.report_items((("Method", default),))

    def _on_var_selection_changed(self):
        indexes = self.selection.selectedIndexes()
        methods = [self.get_method_for_column(i.row()) for i in indexes]

        def method_key(method):
            """
            Decompose method into its type and parameters.
            """
            # The return value should be hashable and  __eq__ comparable
            if isinstance(method, AsDefault):
                return AsDefault, (method.method,)
            elif isinstance(method, impute.Model):
                return impute.Model, (method.learner,)
            elif isinstance(method, impute.Default):
                return impute.Default, (method.default,)
            else:
                return type(method), None

        methods = set(method_key(m) for m in methods)
        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)
        fixed_value = None
        value_stack_enabled = False
        current_value_widget = None

        if len(methods) == 1:
            method_type, parameters = methods.pop()
            for i, m in enumerate(self.methods):
                if method_type == type(m):
                    self.variable_button_group.button(i).setChecked(True)

            if method_type is impute.Default:
                (fixed_value,) = parameters

        elif self.variable_button_group.checkedButton() is not None:
            # Uncheck the current button
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)
            assert self.variable_button_group.checkedButton() is None

        for method, button in zip(self.methods,
                                  self.variable_button_group.buttons()):
            enabled = all(method.supports_variable(var) for var in
                          selected_vars)
            button.setEnabled(enabled)

        if not has_discrete:
            value_stack_enabled = True
            current_value_widget = self.value_double
        elif len(selected_vars) == 1:
            value_stack_enabled = True
            current_value_widget = self.value_combo
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
        else:
            value_stack_enabled = False
            current_value_widget = None
            self.variable_button_group.button(self.AS_INPUT).setEnabled(False)

        self.value_stack.setEnabled(value_stack_enabled)
        if current_value_widget is not None:
            self.value_stack.setCurrentWidget(current_value_widget)
            if fixed_value is not None:
                if current_value_widget is self.value_combo:
                    self.value_combo.setCurrentIndex(fixed_value)
                elif current_value_widget is self.value_double:
                    self.value_double.setValue(fixed_value)
                else:
                    assert False

    def set_method_for_current_selection(self, method_index):
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        if method_index == self.DEFAULT:
            for index in indexes:
                self.variable_methods.pop(index.row(), None)
        elif method_index == OWImpute.AS_INPUT:
            current = self.value_stack.currentWidget()
            if current is self.value_combo:
                value = self.value_combo.currentIndex()
            else:
                value = self.value_double.value()
            for index in indexes:
                method = impute.Default(default=value)
                self.variable_methods[index.row()] = method
        else:
            method = self.methods[method_index]
            for index in indexes:
                self.variable_methods[index.row()] = method

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(index, self.get_method_for_column(index.row()), Qt.UserRole)

    def _on_value_selected(self):
        # The fixed 'Value' in the widget has been changed by the user.
        self.variable_button_group.button(self.AS_INPUT).setChecked(True)
        self.set_method_for_current_selection(self.AS_INPUT)

    def reset_variable_methods(self):
        indexes = list(map(self.varmodel.index, range(len(self.varmodel))))
        self.set_method_for_indexes(indexes, self.DEFAULT)
        self.variable_button_group.button(self.DEFAULT).setChecked(True)
Ejemplo n.º 24
0
    def set_new_values(self, oper_combo, adding_all, selected_values=None):
        # def remove_children():
        #     for child in box.children()[1:]:
        #         box.layout().removeWidget(child)
        #         child.setParent(None)

        def add_textual(contents):
            le = gui.lineEdit(box,
                              self,
                              None,
                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                     QSizePolicy.Expanding))
            if contents:
                le.setText(contents)
            le.setAlignment(Qt.AlignRight)
            le.editingFinished.connect(self.conditions_changed)
            return le

        def add_numeric(contents):
            le = add_textual(contents)
            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())
            return le

        def add_datetime(contents):
            le = add_textual(contents)
            le.setValidator(QRegExpValidator(QRegExp(TimeVariable.REGEX)))
            return le

        var = self.data.domain[oper_combo.attr_combo.currentText()]
        box = self.cond_list.cellWidget(oper_combo.row, 2)
        if selected_values is not None:
            lc = list(selected_values) + ["", ""]
            lc = [str(x) for x in lc[:2]]
        else:
            lc = ["", ""]
        if box and vartype(var) == box.var_type:
            lc = self._get_lineedit_contents(box) + lc
        oper = oper_combo.currentIndex()

        if oper_combo.currentText() == "is defined":
            label = QLabel()
            label.var_type = vartype(var)
            self.cond_list.setCellWidget(oper_combo.row, 2, label)
        elif var.is_discrete:
            if oper_combo.currentText() == "is one of":
                if selected_values:
                    lc = [x for x in list(selected_values)]
                button = DropDownToolButton(self, var, lc)
                button.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, button)
            else:
                combo = QComboBox()
                combo.addItems([""] + var.values)
                if lc[0]:
                    combo.setCurrentIndex(int(lc[0]))
                else:
                    combo.setCurrentIndex(0)
                combo.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, combo)
                combo.currentIndexChanged.connect(self.conditions_changed)
        else:
            box = gui.hBox(self, addToLayout=False)
            box.var_type = vartype(var)
            self.cond_list.setCellWidget(oper_combo.row, 2, box)
            if var.is_continuous:
                validator = add_datetime if isinstance(
                    var, TimeVariable) else add_numeric
                box.controls = [validator(lc[0])]
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(validator(lc[1]))
            elif var.is_string:
                box.controls = [add_textual(lc[0])]
                if oper in [6, 7]:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_textual(lc[1]))
            else:
                box.controls = []
        if not adding_all:
            self.conditions_changed()
Ejemplo n.º 25
0
class UserPrefUI(QWidget):
    def __init__(self, prefs, parent):
        super(UserPrefUI, self).__init__()
        self.title = 'User Preferences'
        self.setLayout(QVBoxLayout(self))
        self.parent = parent
        self.prefs = prefs

        # Init logger
        self.logger = Logger('userPrefsUI', 'UI : User Preferences')

        # Set up container
        container = QWidget()
        container.setLayout(QVBoxLayout())

        # Set up scroll area
        scrollArea = QScrollArea()
        scrollArea.setWidget(container)
        scrollArea.setWidgetResizable(True)
        self.layout().addWidget(scrollArea)

        # Permanent controls
        self._saveButton = QPushButton('Save', self)
        self._saveButton.clicked.connect(self.__save)
        self._startMaximized = QCheckBox('Start Maximized', self)
        self._allowDuplicatePins = QCheckBox('Allow Duplicate Output Pins',
                                             self)
        self._enableOBA = QCheckBox('OnBoard Air', self)
        self._enableLighting = QCheckBox('Lighting', self)
        self._enableTracControl = QCheckBox('Traction Control', self)
        self._enableCamViewer = QCheckBox('Camera Viewer', self)
        self._enableGyro = QCheckBox('Inclinometer', self)
        self._panelLabel = QLabel(
            '<html><center>Enable Modules:<br>\
            <em>Restart TacOS for changes to take effect.</em></center></html>',
            self)
        self._i2cBus = QComboBox(self)
        self._i2cBus.addItems(Config.busList)
        self._i2cBusLabel = QLabel('I2C Bus', self)
        self._i2cAddress = QLineEdit('I2C Address', self)
        self._i2cLabel = QLabel(
            '<html><center>I2C Parameters:<br>\
            <em>Restart TacOS for changes to take effect.</em></center></html>',
            self)
        self._i2cDebug = QCheckBox('I2C Debug Mode', self)
        self._i2cDebugLabel = QLabel(
            '<html><center>I2C Debug Mode:<br>\
            <em>Disables all I2C comms.<br>\
            Restart TacOS for changes to take effect.</em></center></html>',
            self)
        self._debugLogging = QCheckBox('Enable Debug Logs', self)
        self._restartButton = QPushButton('Restart TacOS', self)
        self._restartButton.clicked.connect(self.__restart)

        # Set initial values
        for control in [
                'startMaximized', 'allowDuplicatePins', 'enableOBA',
                'enableLighting', 'enableTracControl', 'enableCamViewer',
                'enableGyro', 'i2cDebug', 'debugLogging'
        ]:
            if control in prefs.keys():
                exec('self._%s.setChecked(prefs["%s"])' % (control, control))
            else:
                if control in [
                        'enableOBA', 'enableLighting', 'enableTracControl',
                        'enableCamViewer', 'enableGyro'
                ]:
                    exec('self._%s.setChecked(True)' % control)
                else:
                    exec('self._%s.setChecked(False)' % control)
        if 'i2cAddress' in prefs.keys():
            self._i2cAddress.setText(prefs['i2cAddress'])
        else:
            self._i2cAddress.setText('0x20')
        if 'i2cBus' in prefs.keys():
            self._i2cBus.setCurrentText(str(prefs['i2cBus']))
        else:
            self._i2cBus.setCurrentIndex(0)

        layoutList = [[
            '_startMaximized', '_allowDuplicatePins', '_debugLogging'
        ], ['_panelLabel'],
                      ['_enableOBA', '_enableLighting', '_enableTracControl'],
                      ['_enableCamViewer', '_enableGyro'], ['_i2cLabel'],
                      ['_i2cBusLabel', '_i2cBus', '_i2cAddress'],
                      ['_i2cDebugLabel'], ['_i2cDebug'],
                      ['_saveButton', '_restartButton']]
        for i in layoutList:
            panel = QWidget()
            panel.setLayout(QHBoxLayout(panel))
            panel.layout().setSpacing(20)
            panel.layout().setAlignment(Qt.AlignCenter)
            for c in i:
                panel.layout().addWidget(eval('self.%s' % c))
            container.layout().addWidget(panel)

    def refresh(self):
        self.__init__(self.window().prefs, self.parent)

    def __save(self):
        self.parent.prefs = self.__getPrefs()
        self.parent.savePrefs()

    def __restart(self):
        os.system("sudo sh /home/pi/TacOS/launcher.sh")
        sys.exit()

    def __getPrefs(self):
        _updates = {
            'startMaximized': self._startMaximized.isChecked(),
            'allowDuplicatePins': self._allowDuplicatePins.isChecked(),
            'enableOBA': self._enableOBA.isChecked(),
            'enableLighting': self._enableLighting.isChecked(),
            'enableTracControl': self._enableTracControl.isChecked(),
            'enableCamViewer': self._enableCamViewer.isChecked(),
            'enableGyro': self._enableGyro.isChecked(),
            'i2cBus': self._i2cBus.currentText(),
            'i2cAddress': hex(int(self._i2cAddress.text(), 16)),
            'i2cDebug': self._i2cDebug.isChecked(),
            'debugLogging': self._debugLogging.isChecked()
        }
        for _key in _updates.keys():
            self.prefs[_key] = _updates[_key]
        return self.prefs
Ejemplo n.º 26
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130

    inputs = [("Data", Orange.data.Table, "set_data"),
              ("Learner", Learner, "set_learner")]
    outputs = [("Data", Orange.data.Table)]

    DEFAULT_LEARNER = SimpleTreeLearner()
    METHODS = [
        AsDefault(),
        impute.DoNotImpute(),
        impute.Average(),
        impute.AsValue(),
        impute.Model(DEFAULT_LEARNER),
        impute.Random(),
        impute.DropInstances(),
        impute.Default()
    ]
    DEFAULT, DO_NOT_IMPUTE, MODEL_BASED_IMPUTER, AS_INPUT = 0, 1, 4, 7

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(DO_NOT_IMPUTE)
    variable_methods = settings.ContextSetting({})
    autocommit = settings.Setting(False)
    default_value = settings.Setting(0.)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QVBoxLayout(box)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)
        for i, method in enumerate(self.METHODS):
            if not method.columns_only:
                button = QRadioButton(method.name)
                button.setChecked(i == self.default_method_index)
                button_group.addButton(button, i)
                box_layout.addWidget(button)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = QListView(selectionMode=QListView.ExtendedSelection)
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed)
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for i, method in enumerate(self.METHODS):
            button = QRadioButton(text=method.name)
            button_group.addButton(button, i)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected)
        self.value_combo.currentIndexChanged.connect(self._on_value_changed)
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000.,
            maximum=1000.,
            singleStep=.1,
            decimals=3,
            value=self.default_value)
        self.value_stack = value_stack = QStackedLayout()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addLayout(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection)

        method_layout.addStretch(2)

        reset_button = QPushButton("Restore All to Default",
                                   checked=False,
                                   checkable=False,
                                   clicked=self.reset_variable_methods,
                                   default=False,
                                   autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_commit(self.controlArea,
                              self,
                              "autocommit",
                              "Apply",
                              orientation=Qt.Horizontal,
                              checkbox_label="Apply automatically")
        box.layout().insertSpacing(0, 80)
        box.layout().insertWidget(0, self.report_button)

        self.data = None
        self.modified = False
        self.default_method = self.METHODS[self.default_method_index]
        self.update_varview()

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            self.default_method = self.METHODS[self.default_method_index]
            self.METHODS[self.DEFAULT].method = self.default_method

            # update variable view
            for index in map(self.varmodel.index, range(len(self.varmodel))):
                self.varmodel.setData(
                    index,
                    self.variable_methods.get(index.row(),
                                              self.METHODS[self.DEFAULT]),
                    Qt.UserRole)
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.varmodel[:] = []
        self.variable_methods = {}
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)

        self.update_varview()
        self.unconditional_commit()

    def set_learner(self, learner):
        self.learner = learner or self.DEFAULT_LEARNER
        imputer = self.METHODS[self.MODEL_BASED_IMPUTER]
        imputer.learner = self.learner

        button = self.default_button_group.button(self.MODEL_BASED_IMPUTER)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(
            self.MODEL_BASED_IMPUTER)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = self.MODEL_BASED_IMPUTER

        self.commit()

    def get_method_for_column(self, column_index):
        """Returns the imputation method for column by its index.
        """
        if not isinstance(column_index, int):
            column_index = column_index.row()

        return self.variable_methods.get(column_index,
                                         self.METHODS[self.DEFAULT])

    def _invalidate(self):
        self.modified = True
        self.commit()

    def commit(self):
        data = self.data

        if self.data is not None:
            drop_mask = np.zeros(len(self.data), bool)

            attributes = []
            class_vars = []

            self.warning()
            with self.progressBar(len(self.varmodel)) as progress:
                for i, var in enumerate(self.varmodel):
                    method = self.variable_methods.get(i, self.default_method)

                    if not method.supports_variable(var):
                        self.warning(
                            "Default method can not handle '{}'".format(
                                var.name))
                    elif isinstance(method, impute.DropInstances):
                        drop_mask |= method(self.data, var)
                    else:
                        var = method(self.data, var)

                    if isinstance(var, Orange.data.Variable):
                        var = [var]

                    if i < len(self.data.domain.attributes):
                        attributes.extend(var)
                    else:
                        class_vars.extend(var)

                    progress.advance()

            domain = Orange.data.Domain(attributes, class_vars,
                                        self.data.domain.metas)
            data = self.data.from_table(domain, self.data[~drop_mask])

        self.send("Data", data)
        self.modified = False

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.variable_methods.get(i, None)
            if method is not None:
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.default_method.name
        if specific:
            self.report_items((("Default method", default),
                               ("Specific imputers", ", ".join(specific))))
        else:
            self.report_items((("Method", default), ))

    def _on_var_selection_changed(self):
        indexes = self.selection.selectedIndexes()
        methods = set(
            self.get_method_for_column(i.row()).name for i in indexes)

        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)

        if len(methods) == 1:
            method = methods.pop()
            for i, m in enumerate(self.METHODS):
                if method == m.name:
                    self.variable_button_group.button(i).setChecked(True)
        elif self.variable_button_group.checkedButton() is not None:
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)

        for method, button in zip(self.METHODS,
                                  self.variable_button_group.buttons()):
            enabled = all(
                method.supports_variable(var) for var in selected_vars)
            button.setEnabled(enabled)

        if not has_discrete:
            self.value_stack.setEnabled(True)
            self.value_stack.setCurrentWidget(self.value_double)
            self._on_value_changed()
        elif len(selected_vars) == 1:
            self.value_stack.setEnabled(True)
            self.value_stack.setCurrentWidget(self.value_combo)
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
            self._on_value_changed()
        else:
            self.variable_button_group.button(self.AS_INPUT).setEnabled(False)
            self.value_stack.setEnabled(False)

    def set_method_for_current_selection(self, method_index):
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        if method_index == self.DEFAULT:
            for index in indexes:
                self.variable_methods.pop(index, None)
        else:
            method = self.METHODS[method_index].copy()
            for index in indexes:
                self.variable_methods[index.row()] = method

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(index,
                                  self.get_method_for_column(index.row()),
                                  Qt.UserRole)

    def _on_value_selected(self):
        self.variable_button_group.button(self.AS_INPUT).setChecked(True)
        self._on_value_changed()

    def _on_value_changed(self):
        widget = self.value_stack.currentWidget()
        if widget is self.value_combo:
            value = self.value_combo.currentText()
        else:
            value = self.value_double.value()
            self.default_value = value

        self.METHODS[self.AS_INPUT].default = value
        index = self.variable_button_group.checkedId()
        if index == self.AS_INPUT:
            self.set_method_for_current_selection(index)

    def reset_variable_methods(self):
        indexes = map(self.varmodel.index, range(len(self.varmodel)))
        self.set_method_for_indexes(indexes, self.DEFAULT)
        self.variable_button_group.button(self.DEFAULT).setChecked(True)
Ejemplo n.º 27
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130
    keywords = ["substitute", "missing"]

    class Inputs:
        data = Input("Data", Orange.data.Table)
        learner = Input("Learner", Learner)

    class Outputs:
        data = Output("Data", Orange.data.Table)

    class Error(OWWidget.Error):
        imputation_failed = Msg("Imputation failed for '{}'")
        model_based_imputer_sparse = \
            Msg("Model based imputer does not work for sparse data")

    class Warning(OWWidget.Warning):
        cant_handle_var = Msg("Default method can not handle '{}'")

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(int(Method.Leave))  # type: int
    # Per-variable imputation state (synced in storeSpecificSettings)
    _variable_imputation_state = settings.ContextSetting(
        {})  # type: VariableState

    autocommit = settings.Setting(True)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        self.data = None  # type: Optional[Orange.data.Table]
        self.learner = None  # type: Optional[Learner]
        self.default_learner = SimpleTreeLearner(min_instances=10,
                                                 max_depth=10)
        self.modified = False
        self.executor = qconcurrent.ThreadExecutor(self)
        self.__task = None

        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QGridLayout(box)
        box_layout.setContentsMargins(5, 0, 0, 0)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)

        for i, (method, _) in enumerate(list(METHODS.items())[1:-1]):
            imputer = self.create_imputer(method)
            button = QRadioButton(imputer.name)
            button.setChecked(method == self.default_method_index)
            button_group.addButton(button, method)
            box_layout.addWidget(button, i % 3, i // 3)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = ListViewSearch(
            selectionMode=QListView.ExtendedSelection, uniformItemSizes=True)
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed)
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for method in Method:
            imputer = self.create_imputer(method)
            button = QRadioButton(text=imputer.name)
            button_group.addButton(button, method)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected)
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000.,
            maximum=1000.,
            singleStep=.1,
            decimals=3,
        )
        self.value_stack = value_stack = QStackedWidget()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addWidget(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection)

        method_layout.addStretch(2)

        reset_button = QPushButton("Restore All to Default",
                                   checked=False,
                                   checkable=False,
                                   clicked=self.reset_variable_state,
                                   default=False,
                                   autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_apply(self.controlArea, self, "autocommit")
        box.button.setFixedWidth(180)
        box.layout().insertStretch(0)

        self.info.set_input_summary(self.info.NoInput)
        self.info.set_output_summary(self.info.NoOutput)

    def create_imputer(self, method, *args):
        # type: (Method, ...) -> impute.BaseImputeMethod
        if method == Method.Model:
            if self.learner is not None:
                return impute.Model(self.learner)
            else:
                return impute.Model(self.default_learner)
        elif method == Method.AsAboveSoBelow:
            assert self.default_method_index != Method.AsAboveSoBelow
            default = self.create_imputer(Method(self.default_method_index))
            m = AsDefault()
            m.method = default
            return m
        else:
            return METHODS[method](*args)

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            assert index != Method.AsAboveSoBelow
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            # update variable view
            self.update_varview()
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.cancel()
        self.closeContext()
        self.varmodel[:] = []
        self._variable_imputation_state = {}  # type: VariableState
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)
            # restore per variable imputation state
            self._restore_state(self._variable_imputation_state)

        summary = len(data) if data else self.info.NoInput
        details = format_summary_details(data) if data else ""
        self.info.set_input_summary(summary, details)

        self.update_varview()
        self.unconditional_commit()

    @Inputs.learner
    def set_learner(self, learner):
        self.cancel()
        self.learner = learner or self.default_learner
        imputer = self.create_imputer(Method.Model)
        button = self.default_button_group.button(Method.Model)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(Method.Model)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = Method.Model

        self.update_varview()
        self.commit()

    def get_method_for_column(self, column_index):
        # type: (int) -> impute.BaseImputeMethod
        """
        Return the imputation method for column by its index.
        """
        assert 0 <= column_index < len(self.varmodel)
        idx = self.varmodel.index(column_index, 0)
        state = idx.data(StateRole)
        if state is None:
            state = (Method.AsAboveSoBelow, ())
        return self.create_imputer(state[0], *state[1])

    def _invalidate(self):
        self.modified = True
        if self.__task is not None:
            self.cancel()
        self.commit()

    def commit(self):
        self.cancel()
        self.warning()
        self.Error.imputation_failed.clear()
        self.Error.model_based_imputer_sparse.clear()
        summary = len(self.data) if self.data else self.info.NoOutput
        detail = format_summary_details(self.data) if self.data else ""
        self.info.set_output_summary(summary, detail)

        if not self.data or not self.varmodel.rowCount():
            self.Outputs.data.send(self.data)
            self.modified = False
            return

        data = self.data
        impute_state = [(i, var, self.get_method_for_column(i))
                        for i, var in enumerate(self.varmodel)]
        # normalize to the effective method bypasing AsDefault
        impute_state = [(i, var, m.method if isinstance(m, AsDefault) else m)
                        for i, var, m in impute_state]

        def impute_one(method, var, data):
            # Readability counts, pylint: disable=no-else-raise
            # type: (impute.BaseImputeMethod, Variable, Table) -> Any
            if isinstance(method, impute.Model) and data.is_sparse():
                raise SparseNotSupported()
            elif isinstance(method, impute.DropInstances):
                return RowMask(method(data, var))
            elif not method.supports_variable(var):
                raise VariableNotSupported(var)
            else:
                return method(data, var)

        futures = []
        for _, var, method in impute_state:
            f = self.executor.submit(impute_one, copy.deepcopy(method), var,
                                     data)
            futures.append(f)

        w = qconcurrent.FutureSetWatcher(futures)
        w.doneAll.connect(self.__commit_finish)
        w.progressChanged.connect(self.__progress_changed)
        self.__task = Task(futures, w)
        self.progressBarInit()
        self.setInvalidated(True)

    @Slot()
    def __commit_finish(self):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        futures = self.__task.futures
        assert len(futures) == len(self.varmodel)
        assert self.data is not None

        def get_variable(variable, future, drop_mask) \
                -> Optional[List[Orange.data.Variable]]:
            # Returns a (potentially empty) list of variables,
            # or None on failure that should interrupt the imputation
            assert future.done()
            try:
                res = future.result()
            except SparseNotSupported:
                self.Error.model_based_imputer_sparse()
                return []  # None?
            except VariableNotSupported:
                self.Warning.cant_handle_var(variable.name)
                return []
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error for %s", variable.name, exc_info=True)
                self.Error.imputation_failed(variable.name)
                return None
            if isinstance(res, RowMask):
                drop_mask |= res.mask
                newvar = variable
            else:
                newvar = res
            if isinstance(newvar, Orange.data.Variable):
                newvar = [newvar]
            return newvar

        def create_data(attributes, class_vars):
            domain = Orange.data.Domain(attributes, class_vars,
                                        self.data.domain.metas)
            try:
                return self.data.from_table(domain, self.data[~drop_mask])
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error", exc_info=True)
                self.Error.imputation_failed("Unknown")
                return None

        self.__task = None
        self.setInvalidated(False)
        self.progressBarFinished()

        attributes = []
        class_vars = []
        drop_mask = np.zeros(len(self.data), bool)
        for i, (var, fut) in enumerate(zip(self.varmodel, futures)):
            newvar = get_variable(var, fut, drop_mask)
            if newvar is None:
                data = None
                break
            if i < len(self.data.domain.attributes):
                attributes.extend(newvar)
            else:
                class_vars.extend(newvar)
        else:
            data = create_data(attributes, class_vars)

        self.Outputs.data.send(data)
        self.modified = False
        summary = len(data) if data else self.info.NoOutput
        details = format_summary_details(data) if data else ""
        self.info.set_output_summary(summary, details)

    @Slot(int, int)
    def __progress_changed(self, n, d):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        self.progressBarSet(100. * n / d)

    def cancel(self):
        self.__cancel(wait=False)

    def __cancel(self, wait=False):
        if self.__task is not None:
            task, self.__task = self.__task, None
            task.cancel()
            task.watcher.doneAll.disconnect(self.__commit_finish)
            task.watcher.progressChanged.disconnect(self.__progress_changed)
            if wait:
                concurrent.futures.wait(task.futures)
                task.watcher.flush()
            self.progressBarFinished()
            self.setInvalidated(False)

    def onDeleteWidget(self):
        self.__cancel(wait=True)
        super().onDeleteWidget()

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.get_method_for_column(i)
            if not isinstance(method, AsDefault):
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.create_imputer(Method.AsAboveSoBelow)
        if specific:
            self.report_items((("Default method", default.name),
                               ("Specific imputers", ", ".join(specific))))
        else:
            self.report_items((("Method", default.name), ))

    def _on_var_selection_changed(self):
        # Method is well documented, splitting it is not needed for readability,
        # thus pylint: disable=too-many-branches
        indexes = self.selection.selectedIndexes()
        defmethod = (Method.AsAboveSoBelow, ())
        methods = [index.data(StateRole) for index in indexes]
        methods = [m if m is not None else defmethod for m in methods]
        methods = set(methods)
        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)
        fixed_value = None
        value_stack_enabled = False
        current_value_widget = None

        if len(methods) == 1:
            method_type, parameters = methods.pop()
            for m in Method:
                if method_type == m:
                    self.variable_button_group.button(m).setChecked(True)

            if method_type == Method.Default:
                (fixed_value, ) = parameters

        elif self.variable_button_group.checkedButton() is not None:
            # Uncheck the current button
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)
            assert self.variable_button_group.checkedButton() is None

        # Update variable methods GUI enabled state based on selection.
        for method in Method:
            # use a default constructed imputer to query support
            imputer = self.create_imputer(method)
            enabled = all(
                imputer.supports_variable(var) for var in selected_vars)
            button = self.variable_button_group.button(method)
            button.setEnabled(enabled)

        # Update the "Value" edit GUI.
        if not has_discrete:
            # no discrete variables -> allow mass edit for all (continuous vars)
            value_stack_enabled = True
            current_value_widget = self.value_double
        elif len(selected_vars) == 1:
            # single discrete var -> enable and fill the values combo
            value_stack_enabled = True
            current_value_widget = self.value_combo
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
        else:
            # mixed type selection -> disable
            value_stack_enabled = False
            current_value_widget = None
            self.variable_button_group.button(Method.Default).setEnabled(False)

        self.value_stack.setEnabled(value_stack_enabled)
        if current_value_widget is not None:
            self.value_stack.setCurrentWidget(current_value_widget)
            if fixed_value is not None:
                # set current value
                if current_value_widget is self.value_combo:
                    self.value_combo.setCurrentIndex(fixed_value)
                elif current_value_widget is self.value_double:
                    self.value_double.setValue(fixed_value)
                else:
                    assert False

    def set_method_for_current_selection(self, method_index):
        # type: (Method) -> None
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        # type: (List[QModelIndex], Method) -> None
        if method_index == Method.AsAboveSoBelow:
            for index in indexes:
                self.varmodel.setData(index, None, StateRole)
        elif method_index == Method.Default:
            current = self.value_stack.currentWidget()
            if current is self.value_combo:
                value = self.value_combo.currentIndex()
            else:
                value = self.value_double.value()
            for index in indexes:
                state = (int(Method.Default), (value, ))
                self.varmodel.setData(index, state, StateRole)
        else:
            state = (int(method_index), ())
            for index in indexes:
                self.varmodel.setData(index, state, StateRole)

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(index,
                                  self.get_method_for_column(index.row()),
                                  DisplayMethodRole)

    def _on_value_selected(self):
        # The fixed 'Value' in the widget has been changed by the user.
        self.variable_button_group.button(Method.Default).setChecked(True)
        self.set_method_for_current_selection(Method.Default)

    def reset_variable_state(self):
        indexes = list(map(self.varmodel.index, range(len(self.varmodel))))
        self.set_method_for_indexes(indexes, Method.AsAboveSoBelow)
        self.variable_button_group.button(
            Method.AsAboveSoBelow).setChecked(True)

    def _store_state(self):
        # type: () -> VariableState
        """
        Save the current variable imputation state
        """
        state = {}  # type: VariableState
        for i, var in enumerate(self.varmodel):
            index = self.varmodel.index(i)
            m = index.data(StateRole)
            if m is not None:
                state[var_key(var)] = m
        return state

    def _restore_state(self, state):
        # type: (VariableState) -> None
        """
        Restore the variable imputation state from the saved state
        """
        def check(state):
            # check if state is a proper State
            if isinstance(state, tuple) and len(state) == 2:
                m, p = state
                if isinstance(m, int) and isinstance(p, tuple) and \
                        0 <= m < len(Method):
                    return True
            return False

        for i, var in enumerate(self.varmodel):
            m = state.get(var_key(var), None)
            if check(m):
                self.varmodel.setData(self.varmodel.index(i), m, StateRole)

    def storeSpecificSettings(self):
        self._variable_imputation_state = self._store_state()
        super().storeSpecificSettings()
Ejemplo n.º 28
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar()

            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction)

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        self.stack = central

        self.setCentralWidget(central)

        # General Tab
        tab = QWidget()
        self.addTab(tab, self.tr("通用"), toolTip=self.tr("通用选项"))

        form = QFormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(self.tr("启用节点动画"),
                            objectName="enable-node-animations",
                            toolTip=self.tr("启用数据挖掘流程中节点的阴影和动画"))
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("节点"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(self.tr("显示部件之间的通道名称"),
                            objectName="show-channel-names",
                            toolTip=self.tr("在链接上显示来源和接收的通道名称。"))

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("连接"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("双击"), toolTip=self.tr("双击画布空白处打开快捷菜单"))

        cb2 = QCheckBox(self.tr("右击"), toolTip=self.tr("右击画布空白处打开快捷菜单"))

        cb3 = QCheckBox(self.tr("按空格键"), toolTip=self.tr("鼠标停在画布上,按空格键打开快捷菜单"))

        cb4 = QCheckBox(self.tr("按任意键"), toolTip=self.tr("鼠标停在画布上,按任意键打开快捷菜单"))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)

        form.addRow(self.tr("打开快捷菜单"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("显示初始屏幕"),
                              self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("显示欢迎屏幕"),
                               self,
                               objectName="show-welcome-screen")

        cb_updates = QCheckBox(self.tr("检查更新"),
                               self,
                               objectName="check-updates")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")
        self.bind(cb_updates, "checked", "startup/check-updates")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)
        startup.layout().addWidget(cb_updates)

        form.addRow(self.tr("启动"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("一次只能打开一个选项卡                          "))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("工具箱"), toolbox)
        tab.setLayout(form)

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("输出"), toolTip="输出重定向")

        form = QFormLayout()

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        combo = QComboBox()
        combo.addItems([
            self.tr("Critical"),
            self.tr("Error"),
            self.tr("Warn"),
            self.tr("Info"),
            self.tr("Debug")
        ])
        self.bind(combo, "currentIndex", "logging/level")
        layout.addWidget(combo)
        box.setLayout(layout)
        form.addRow(self.tr("日志"), box)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("用外部浏览器打开"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("帮助"), box)

        tab.setLayout(form)

        # Error Reporting Tab
        tab = QWidget()
        self.addTab(tab, self.tr("错误报告"), toolTip="错误报告相关的设置")

        form = QFormLayout()
        line_edit_mid = QLineEdit()
        self.bind(line_edit_mid, "text", "error-reporting/machine-id")
        form.addRow("Machine ID:", line_edit_mid)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(
            self.tr(""),
            toolTip=self.tr(
                "Share anonymous usage statistics to improve Orange"))
        self.bind(cb1, "checked", "error-reporting/send-statistics")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("共享匿名统计信息"), box)

        tab.setLayout(form)

        # Add-ons Tab
        tab = QWidget()
        self.addTab(tab, self.tr("附加"), toolTip="附加组件安装相关的设置")

        form = QFormLayout()
        conda = QWidget(self, objectName="conda-group")
        conda.setLayout(QVBoxLayout())
        conda.layout().setContentsMargins(0, 0, 0, 0)

        cb_conda_install = QCheckBox(self.tr("Conda安装附加组件"),
                                     self,
                                     objectName="allow-conda")
        self.bind(cb_conda_install, "checked", "add-ons/allow-conda")
        conda.layout().addWidget(cb_conda_install)

        form.addRow(self.tr("Conda"), conda)

        form.addRow(self.tr("Pip"), QLabel("Pip安装参数:"))
        line_edit_pip = QLineEdit()
        self.bind(line_edit_pip, "text", "add-ons/pip-install-arguments")
        form.addRow("", line_edit_pip)

        tab.setLayout(form)

        # Network Tab
        tab = QWidget()
        self.addTab(tab, self.tr("网络"), toolTip="网络相关的设置")

        form = QFormLayout()
        line_edit_http_proxy = QLineEdit()
        self.bind(line_edit_http_proxy, "text", "network/http-proxy")
        form.addRow("HTTP代理:", line_edit_http_proxy)
        line_edit_https_proxy = QLineEdit()
        self.bind(line_edit_https_proxy, "text", "network/https-proxy")
        form.addRow("HTTPS代理:", line_edit_https_proxy)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
Ejemplo n.º 29
0
    def set_new_values(self, oper_combo, adding_all, selected_values=None):
        # def remove_children():
        #     for child in box.children()[1:]:
        #         box.layout().removeWidget(child)
        #         child.setParent(None)

        def add_textual(contents):
            le = gui.lineEdit(box, self, None,
                              sizePolicy=QSizePolicy(QSizePolicy.Expanding,
                                                     QSizePolicy.Expanding))
            if contents:
                le.setText(contents)
            le.setAlignment(Qt.AlignRight)
            le.editingFinished.connect(self.conditions_changed)
            return le

        def add_numeric(contents):
            le = add_textual(contents)
            le.setValidator(OWSelectRows.QDoubleValidatorEmpty())
            return le

        def add_datetime(contents):
            le = add_textual(contents)
            le.setValidator(QRegExpValidator(QRegExp(TimeVariable.REGEX)))
            return le

        var = self.data.domain[oper_combo.attr_combo.currentText()]
        box = self.cond_list.cellWidget(oper_combo.row, 2)
        if selected_values is not None:
            lc = list(selected_values) + ["", ""]
            lc = [str(x) for x in lc[:2]]
        else:
            lc = ["", ""]
        if box and vartype(var) == box.var_type:
            lc = self._get_lineedit_contents(box) + lc
        oper = oper_combo.currentIndex()

        if oper_combo.currentText() == "is defined":
            label = QLabel()
            label.var_type = vartype(var)
            self.cond_list.setCellWidget(oper_combo.row, 2, label)
        elif var.is_discrete:
            if oper_combo.currentText() == "is one of":
                if selected_values:
                    lc = [x for x in list(selected_values)]
                button = DropDownToolButton(self, var, lc)
                button.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, button)
            else:
                combo = QComboBox()
                combo.addItems([""] + var.values)
                if lc[0]:
                    combo.setCurrentIndex(int(lc[0]))
                else:
                    combo.setCurrentIndex(0)
                combo.var_type = vartype(var)
                self.cond_list.setCellWidget(oper_combo.row, 2, combo)
                combo.currentIndexChanged.connect(self.conditions_changed)
        else:
            box = gui.hBox(self, addToLayout=False)
            box.var_type = vartype(var)
            self.cond_list.setCellWidget(oper_combo.row, 2, box)
            if var.is_continuous:
                validator = add_datetime if isinstance(var, TimeVariable) else add_numeric
                box.controls = [validator(lc[0])]
                if oper > 5:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(validator(lc[1]))
            elif var.is_string:
                box.controls = [add_textual(lc[0])]
                if oper in [6, 7]:
                    gui.widgetLabel(box, " and ")
                    box.controls.append(add_textual(lc[1]))
            else:
                box.controls = []
        if not adding_all:
            self.conditions_changed()
Ejemplo n.º 30
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130

    class Inputs:
        data = Input("Data", Orange.data.Table)
        learner = Input("Learner", Learner)

    class Outputs:
        data = Output("Data", Orange.data.Table)

    class Error(OWWidget.Error):
        imputation_failed = Msg("Imputation failed for '{}'")
        model_based_imputer_sparse = Msg("Model based imputer does not work for sparse data")

    DEFAULT_LEARNER = SimpleTreeLearner()
    METHODS = [AsDefault(), impute.DoNotImpute(), impute.Average(),
               impute.AsValue(), impute.Model(DEFAULT_LEARNER), impute.Random(),
               impute.DropInstances(), impute.Default()]
    DEFAULT, DO_NOT_IMPUTE, MODEL_BASED_IMPUTER, AS_INPUT = 0, 1, 4, 7

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(DO_NOT_IMPUTE)
    variable_methods = settings.ContextSetting({})
    autocommit = settings.Setting(True)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        # copy METHODS (some are modified by the widget)
        self.methods = copy.deepcopy(OWImpute.METHODS)

        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QVBoxLayout(box)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)
        for i, method in enumerate(self.methods):
            if not method.columns_only:
                button = QRadioButton(method.name)
                button.setChecked(i == self.default_method_index)
                button_group.addButton(button, i)
                box_layout.addWidget(button)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = QListView(
            selectionMode=QListView.ExtendedSelection
        )
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed
        )
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for i, method in enumerate(self.methods):
            button = QRadioButton(text=method.name)
            button_group.addButton(button, i)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected
            )
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000., maximum=1000., singleStep=.1, decimals=3,
            )
        self.value_stack = value_stack = QStackedWidget()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addWidget(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection
        )

        method_layout.addStretch(2)

        reset_button = QPushButton(
                "Restore All to Default", checked=False, checkable=False,
                clicked=self.reset_variable_methods, default=False,
                autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_commit(
            self.controlArea, self, "autocommit", "Apply",
            orientation=Qt.Horizontal,
            checkbox_label="Apply automatically")
        box.button.setFixedWidth(180)
        box.layout().insertStretch(0)

        self.data = None
        self.learner = None
        self.modified = False
        self.default_method = self.methods[self.default_method_index]
        self.executor = qconcurrent.ThreadExecutor(self)
        self.__task = None

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            self.default_method = self.methods[self.default_method_index]
            self.methods[self.DEFAULT].method = self.default_method

            # update variable view
            for index in map(self.varmodel.index, range(len(self.varmodel))):
                method = self.variable_methods.get(
                    index.row(), self.methods[self.DEFAULT])
                self.varmodel.setData(index, method, Qt.UserRole)
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @Inputs.data
    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.varmodel[:] = []
        self.variable_methods = {}
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)

        self.update_varview()
        self.unconditional_commit()

    @Inputs.learner
    def set_learner(self, learner):
        self.learner = learner or self.DEFAULT_LEARNER
        imputer = self.methods[self.MODEL_BASED_IMPUTER]
        imputer.learner = self.learner

        button = self.default_button_group.button(self.MODEL_BASED_IMPUTER)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(self.MODEL_BASED_IMPUTER)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = self.MODEL_BASED_IMPUTER

        self.update_varview()
        self.commit()

    def get_method_for_column(self, column_index):
        """Returns the imputation method for column by its index.
        """
        if not isinstance(column_index, int):
            column_index = column_index.row()

        return self.variable_methods.get(column_index,
                                         self.methods[self.DEFAULT])

    def _invalidate(self):
        self.modified = True
        if self.__task is not None:
            self.cancel()
        self.commit()

    def commit(self):
        self.cancel()
        self.warning()
        self.Error.imputation_failed.clear()
        self.Error.model_based_imputer_sparse.clear()

        if self.data is None or len(self.data) == 0 or len(self.varmodel) == 0:
            self.Outputs.data.send(self.data)
            self.modified = False
            return

        data = self.data
        impute_state = [
            (i, var, self.variable_methods.get(i, self.default_method))
            for i, var in enumerate(self.varmodel)
        ]

        def impute_one(method, var, data):
            # type: (impute.BaseImputeMethod, Variable, Table) -> Any
            if isinstance(method, impute.Model) and data.is_sparse():
                raise SparseNotSupported()
            elif isinstance(method, impute.DropInstances):
                return RowMask(method(data, var))
            elif not method.supports_variable(var):
                raise VariableNotSupported(var)
            else:
                return method(data, var)

        futures = []
        for _, var, method in impute_state:
            f = self.executor.submit(
                impute_one, copy.deepcopy(method), var, data)
            futures.append(f)

        w = qconcurrent.FutureSetWatcher(futures)
        w.doneAll.connect(self.__commit_finish)
        w.progressChanged.connect(self.__progress_changed)
        self.__task = Task(futures, w)
        self.progressBarInit(processEvents=False)
        self.setBlocking(True)

    @Slot()
    def __commit_finish(self):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        futures = self.__task.futures
        assert len(futures) == len(self.varmodel)
        assert self.data is not None

        self.__task = None
        self.setBlocking(False)
        self.progressBarFinished()

        data = self.data
        attributes = []
        class_vars = []
        drop_mask = np.zeros(len(self.data), bool)

        for i, (var, fut) in enumerate(zip(self.varmodel, futures)):
            assert fut.done()
            newvar = []
            try:
                res = fut.result()
            except SparseNotSupported:
                self.Error.model_based_imputer_sparse()
                # ?? break
            except VariableNotSupported:
                self.warning("Default method can not handle '{}'".
                             format(var.name))
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error for %s", var, exc_info=True)
                self.Error.imputation_failed(var.name)
                attributes = class_vars = None
                break
            else:
                if isinstance(res, RowMask):
                    drop_mask |= res.mask
                    newvar = var
                else:
                    newvar = res

            if isinstance(newvar, Orange.data.Variable):
                newvar = [newvar]

            if i < len(data.domain.attributes):
                attributes.extend(newvar)
            else:
                class_vars.extend(newvar)

        if attributes is None:
            data = None
        else:
            domain = Orange.data.Domain(attributes, class_vars,
                                        data.domain.metas)
            try:
                data = self.data.from_table(domain, data[~drop_mask])
            except Exception:  # pylint: disable=broad-except
                log = logging.getLogger(__name__)
                log.info("Error", exc_info=True)
                self.Error.imputation_failed("Unknown")
                data = None

        self.Outputs.data.send(data)
        self.modified = False

    @Slot(int, int)
    def __progress_changed(self, n, d):
        assert QThread.currentThread() is self.thread()
        assert self.__task is not None
        self.progressBarSet(100. * n / d)

    def cancel(self):
        if self.__task is not None:
            task, self.__task = self.__task, None
            task.cancel()
            task.watcher.doneAll.disconnect(self.__commit_finish)
            task.watcher.progressChanged.disconnect(self.__progress_changed)
            concurrent.futures.wait(task.futures)
            task.watcher.flush()
            self.progressBarFinished()
            self.setBlocking(False)

    def onDeleteWidget(self):
        self.cancel()
        super().onDeleteWidget()

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.variable_methods.get(i, None)
            if method is not None:
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.default_method.name
        if specific:
            self.report_items((
                ("Default method", default),
                ("Specific imputers", ", ".join(specific))
            ))
        else:
            self.report_items((("Method", default),))

    def _on_var_selection_changed(self):
        indexes = self.selection.selectedIndexes()
        methods = [self.get_method_for_column(i.row()) for i in indexes]

        def method_key(method):
            """
            Decompose method into its type and parameters.
            """
            # The return value should be hashable and  __eq__ comparable
            if isinstance(method, AsDefault):
                return AsDefault, (method.method,)
            elif isinstance(method, impute.Model):
                return impute.Model, (method.learner,)
            elif isinstance(method, impute.Default):
                return impute.Default, (method.default,)
            else:
                return type(method), None

        methods = set(method_key(m) for m in methods)
        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)
        fixed_value = None
        value_stack_enabled = False
        current_value_widget = None

        if len(methods) == 1:
            method_type, parameters = methods.pop()
            for i, m in enumerate(self.methods):
                if method_type == type(m):
                    self.variable_button_group.button(i).setChecked(True)

            if method_type is impute.Default:
                (fixed_value,) = parameters

        elif self.variable_button_group.checkedButton() is not None:
            # Uncheck the current button
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)
            assert self.variable_button_group.checkedButton() is None

        for method, button in zip(self.methods,
                                  self.variable_button_group.buttons()):
            enabled = all(method.supports_variable(var) for var in
                          selected_vars)
            button.setEnabled(enabled)

        if not has_discrete:
            value_stack_enabled = True
            current_value_widget = self.value_double
        elif len(selected_vars) == 1:
            value_stack_enabled = True
            current_value_widget = self.value_combo
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
        else:
            value_stack_enabled = False
            current_value_widget = None
            self.variable_button_group.button(self.AS_INPUT).setEnabled(False)

        self.value_stack.setEnabled(value_stack_enabled)
        if current_value_widget is not None:
            self.value_stack.setCurrentWidget(current_value_widget)
            if fixed_value is not None:
                if current_value_widget is self.value_combo:
                    self.value_combo.setCurrentIndex(fixed_value)
                elif current_value_widget is self.value_double:
                    self.value_double.setValue(fixed_value)
                else:
                    assert False

    def set_method_for_current_selection(self, method_index):
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        if method_index == self.DEFAULT:
            for index in indexes:
                self.variable_methods.pop(index.row(), None)
        elif method_index == OWImpute.AS_INPUT:
            current = self.value_stack.currentWidget()
            if current is self.value_combo:
                value = self.value_combo.currentIndex()
            else:
                value = self.value_double.value()
            for index in indexes:
                method = impute.Default(default=value)
                self.variable_methods[index.row()] = method
        else:
            method = self.methods[method_index]
            for index in indexes:
                self.variable_methods[index.row()] = method

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(index, self.get_method_for_column(index.row()), Qt.UserRole)

    def _on_value_selected(self):
        # The fixed 'Value' in the widget has been changed by the user.
        self.variable_button_group.button(self.AS_INPUT).setChecked(True)
        self.set_method_for_current_selection(self.AS_INPUT)

    def reset_variable_methods(self):
        indexes = list(map(self.varmodel.index, range(len(self.varmodel))))
        self.set_method_for_indexes(indexes, self.DEFAULT)
        self.variable_button_group.button(self.DEFAULT).setChecked(True)
Ejemplo n.º 31
0
class OWImpute(OWWidget):
    name = "Impute"
    description = "Impute missing values in the data table."
    icon = "icons/Impute.svg"
    priority = 2130

    inputs = [("Data", Orange.data.Table, "set_data"),
              ("Learner", Learner, "set_learner")]
    outputs = [("Data", Orange.data.Table)]

    DEFAULT_LEARNER = SimpleTreeLearner()
    METHODS = [AsDefault(), impute.DoNotImpute(), impute.Average(),
               impute.AsValue(), impute.Model(DEFAULT_LEARNER), impute.Random(),
               impute.DropInstances(), impute.Default()]
    DEFAULT, DO_NOT_IMPUTE, MODEL_BASED_IMPUTER, AS_INPUT = 0, 1, 4, 7

    settingsHandler = settings.DomainContextHandler()

    _default_method_index = settings.Setting(DO_NOT_IMPUTE)
    variable_methods = settings.ContextSetting({})
    autocommit = settings.Setting(False)
    default_value = settings.Setting(0.)

    want_main_area = False
    resizing_enabled = False

    def __init__(self):
        super().__init__()
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(10, 10, 10, 10)
        self.controlArea.layout().addLayout(main_layout)

        box = QGroupBox(title=self.tr("Default Method"), flat=False)
        box_layout = QVBoxLayout(box)
        main_layout.addWidget(box)

        button_group = QButtonGroup()
        button_group.buttonClicked[int].connect(self.set_default_method)
        for i, method in enumerate(self.METHODS):
            if not method.columns_only:
                button = QRadioButton(method.name)
                button.setChecked(i == self.default_method_index)
                button_group.addButton(button, i)
                box_layout.addWidget(button)

        self.default_button_group = button_group

        box = QGroupBox(title=self.tr("Individual Attribute Settings"),
                        flat=False)
        main_layout.addWidget(box)

        horizontal_layout = QHBoxLayout(box)
        main_layout.addWidget(box)

        self.varview = QListView(
            selectionMode=QListView.ExtendedSelection
        )
        self.varview.setItemDelegate(DisplayFormatDelegate())
        self.varmodel = itemmodels.VariableListModel()
        self.varview.setModel(self.varmodel)
        self.varview.selectionModel().selectionChanged.connect(
            self._on_var_selection_changed
        )
        self.selection = self.varview.selectionModel()

        horizontal_layout.addWidget(self.varview)

        method_layout = QVBoxLayout()
        horizontal_layout.addLayout(method_layout)

        button_group = QButtonGroup()
        for i, method in enumerate(self.METHODS):
            button = QRadioButton(text=method.name)
            button_group.addButton(button, i)
            method_layout.addWidget(button)

        self.value_combo = QComboBox(
            minimumContentsLength=8,
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength,
            activated=self._on_value_selected
            )
        self.value_combo.currentIndexChanged.connect(self._on_value_changed)
        self.value_double = QDoubleSpinBox(
            editingFinished=self._on_value_selected,
            minimum=-1000., maximum=1000., singleStep=.1, decimals=3,
            value=self.default_value
            )
        self.value_stack = value_stack = QStackedLayout()
        value_stack.addWidget(self.value_combo)
        value_stack.addWidget(self.value_double)
        method_layout.addLayout(value_stack)

        button_group.buttonClicked[int].connect(
            self.set_method_for_current_selection
        )

        method_layout.addStretch(2)

        reset_button = QPushButton(
                "Restore All to Default", checked=False, checkable=False,
                clicked=self.reset_variable_methods, default=False,
                autoDefault=False)
        method_layout.addWidget(reset_button)

        self.variable_button_group = button_group

        box = gui.auto_commit(
            self.controlArea, self, "autocommit", "Apply",
            orientation=Qt.Horizontal, checkbox_label="Apply automatically")
        box.layout().insertSpacing(0, 80)
        box.layout().insertWidget(0, self.report_button)

        self.data = None
        self.modified = False
        self.default_method = self.METHODS[self.default_method_index]
        self.update_varview()

    @property
    def default_method_index(self):
        return self._default_method_index

    @default_method_index.setter
    def default_method_index(self, index):
        if self._default_method_index != index:
            self._default_method_index = index
            self.default_button_group.button(index).setChecked(True)
            self.default_method = self.METHODS[self.default_method_index]
            self.METHODS[self.DEFAULT].method = self.default_method

            # update variable view
            for index in map(self.varmodel.index, range(len(self.varmodel))):
                self.varmodel.setData(index,
                                      self.variable_methods.get(index.row(), self.METHODS[self.DEFAULT]),
                                      Qt.UserRole)
            self._invalidate()

    def set_default_method(self, index):
        """Set the current selected default imputation method.
        """
        self.default_method_index = index

    @check_sql_input
    def set_data(self, data):
        self.closeContext()
        self.varmodel[:] = []
        self.variable_methods = {}
        self.modified = False
        self.data = data

        if data is not None:
            self.varmodel[:] = data.domain.variables
            self.openContext(data.domain)

        self.update_varview()
        self.unconditional_commit()

    def set_learner(self, learner):
        self.learner = learner or self.DEFAULT_LEARNER
        imputer = self.METHODS[self.MODEL_BASED_IMPUTER]
        imputer.learner = self.learner

        button = self.default_button_group.button(self.MODEL_BASED_IMPUTER)
        button.setText(imputer.name)

        variable_button = self.variable_button_group.button(self.MODEL_BASED_IMPUTER)
        variable_button.setText(imputer.name)

        if learner is not None:
            self.default_method_index = self.MODEL_BASED_IMPUTER

        self.commit()

    def get_method_for_column(self, column_index):
        """Returns the imputation method for column by its index.
        """
        if not isinstance(column_index, int):
            column_index = column_index.row()

        return self.variable_methods.get(column_index,
                                         self.METHODS[self.DEFAULT])

    def _invalidate(self):
        self.modified = True
        self.commit()

    def commit(self):
        data = self.data

        if self.data is not None:
            if not len(self.data):
                self.send("Data", self.data)
                self.modified = False
                return

            drop_mask = np.zeros(len(self.data), bool)

            attributes = []
            class_vars = []

            self.warning()
            with self.progressBar(len(self.varmodel)) as progress:
                for i, var in enumerate(self.varmodel):
                    method = self.variable_methods.get(i, self.default_method)

                    if not method.supports_variable(var):
                        self.warning("Default method can not handle '{}'".
                                     format(var.name))
                    elif isinstance(method, impute.DropInstances):
                        drop_mask |= method(self.data, var)
                    else:
                        var = method(self.data, var)

                    if isinstance(var, Orange.data.Variable):
                        var = [var]

                    if i < len(self.data.domain.attributes):
                        attributes.extend(var)
                    else:
                        class_vars.extend(var)

                    progress.advance()

            domain = Orange.data.Domain(attributes, class_vars,
                                        self.data.domain.metas)
            data = self.data.from_table(domain, self.data[~drop_mask])

        self.send("Data", data)
        self.modified = False

    def send_report(self):
        specific = []
        for i, var in enumerate(self.varmodel):
            method = self.variable_methods.get(i, None)
            if method is not None:
                specific.append("{} ({})".format(var.name, str(method)))

        default = self.default_method.name
        if specific:
            self.report_items((
                ("Default method", default),
                ("Specific imputers", ", ".join(specific))
            ))
        else:
            self.report_items((("Method", default),))

    def _on_var_selection_changed(self):
        indexes = self.selection.selectedIndexes()
        methods = set(self.get_method_for_column(i.row()).name for i in indexes)

        selected_vars = [self.varmodel[index.row()] for index in indexes]
        has_discrete = any(var.is_discrete for var in selected_vars)

        if len(methods) == 1:
            method = methods.pop()
            for i, m in enumerate(self.METHODS):
                if method == m.name:
                    self.variable_button_group.button(i).setChecked(True)
        elif self.variable_button_group.checkedButton() is not None:
            self.variable_button_group.setExclusive(False)
            self.variable_button_group.checkedButton().setChecked(False)
            self.variable_button_group.setExclusive(True)

        for method, button in zip(self.METHODS,
                                  self.variable_button_group.buttons()):
            enabled = all(method.supports_variable(var) for var in
                          selected_vars)
            button.setEnabled(enabled)

        if not has_discrete:
            self.value_stack.setEnabled(True)
            self.value_stack.setCurrentWidget(self.value_double)
            self._on_value_changed()
        elif len(selected_vars) == 1:
            self.value_stack.setEnabled(True)
            self.value_stack.setCurrentWidget(self.value_combo)
            self.value_combo.clear()
            self.value_combo.addItems(selected_vars[0].values)
            self._on_value_changed()
        else:
            self.variable_button_group.button(self.AS_INPUT).setEnabled(False)
            self.value_stack.setEnabled(False)

    def set_method_for_current_selection(self, method_index):
        indexes = self.selection.selectedIndexes()
        self.set_method_for_indexes(indexes, method_index)

    def set_method_for_indexes(self, indexes, method_index):
        if method_index == self.DEFAULT:
            for index in indexes:
                self.variable_methods.pop(index, None)
        else:
            method = self.METHODS[method_index].copy()
            for index in indexes:
                self.variable_methods[index.row()] = method

        self.update_varview(indexes)
        self._invalidate()

    def update_varview(self, indexes=None):
        if indexes is None:
            indexes = map(self.varmodel.index, range(len(self.varmodel)))

        for index in indexes:
            self.varmodel.setData(index, self.get_method_for_column(index.row()), Qt.UserRole)

    def _on_value_selected(self):
        self.variable_button_group.button(self.AS_INPUT).setChecked(True)
        self._on_value_changed()

    def _on_value_changed(self):
        widget = self.value_stack.currentWidget()
        if widget is self.value_combo:
            value = self.value_combo.currentText()
        else:
            value = self.value_double.value()
            self.default_value = value

        self.METHODS[self.AS_INPUT].default = value
        index = self.variable_button_group.checkedId()
        if index == self.AS_INPUT:
            self.set_method_for_current_selection(index)

    def reset_variable_methods(self):
        indexes = map(self.varmodel.index, range(len(self.varmodel)))
        self.set_method_for_indexes(indexes, self.DEFAULT)
        self.variable_button_group.button(self.DEFAULT).setChecked(True)
Ejemplo n.º 32
0
class OWFile(widget.OWWidget, RecentPathsWComboMixin):
    name = "File"
    id = "orange.widgets.data.file"
    description = "Read data from an input file or network " \
                  "and send a data table to the output."
    icon = "icons/File.svg"
    priority = 10
    category = "Data"
    keywords = ["file", "load", "read", "open"]

    class Outputs:
        data = Output("Data",
                      Table,
                      doc="Attribute-valued dataset read from the input file.")

    want_main_area = False
    buttons_area_orientation = None

    SEARCH_PATHS = [("sample-datasets", get_sample_datasets_dir())]
    SIZE_LIMIT = 1e7
    LOCAL_FILE, URL = range(2)

    settingsHandler = PerfectDomainContextHandler(
        match_values=PerfectDomainContextHandler.MATCH_VALUES_ALL)

    # pylint seems to want declarations separated from definitions
    recent_paths: List[RecentPath]
    recent_urls: List[str]
    variables: list

    # Overload RecentPathsWidgetMixin.recent_paths to set defaults
    recent_paths = Setting([
        RecentPath("", "sample-datasets", "iris.tab"),
        RecentPath("", "sample-datasets", "titanic.tab"),
        RecentPath("", "sample-datasets", "housing.tab"),
        RecentPath("", "sample-datasets", "heart_disease.tab"),
        RecentPath("", "sample-datasets", "brown-selected.tab"),
        RecentPath("", "sample-datasets", "zoo.tab"),
    ])
    recent_urls = Setting([])
    source = Setting(LOCAL_FILE)
    sheet_names = Setting({})
    url = Setting("")

    variables = ContextSetting([])

    domain_editor = SettingProvider(DomainEditor)

    class Information(widget.OWWidget.Information):
        no_file_selected = Msg("No file selected.")

    class Warning(widget.OWWidget.Warning):
        file_too_big = Msg("The file is too large to load automatically."
                           " Press Reload to load.")
        load_warning = Msg("Read warning:\n{}")
        performance_warning = Msg(
            "Categorical variables with >100 values may decrease performance.")
        renamed_vars = Msg("Some variables have been renamed "
                           "to avoid duplicates.\n{}")
        multiple_targets = Msg("Most widgets do not support multiple targets")

    class Error(widget.OWWidget.Error):
        file_not_found = Msg("File not found.")
        missing_reader = Msg("Missing reader.")
        sheet_error = Msg("Error listing available sheets.")
        unknown = Msg("Read error:\n{}")

    UserAdviceMessages = [
        widget.Message(
            "Use CSV File Import widget for advanced options "
            "for comma-separated files", "use-csv-file-import"),
        widget.Message(
            "This widget loads only tabular data. Use other widgets to load "
            "other data types like models, distance matrices and networks.",
            "other-data-types")
    ]

    def __init__(self):
        super().__init__()
        RecentPathsWComboMixin.__init__(self)
        self.domain = None
        self.data = None
        self.loaded_file = ""
        self.reader = None

        readers = [
            f for f in FileFormat.formats
            if getattr(f, 'read', None) and getattr(f, "EXTENSIONS", None)
        ]

        def group_readers_per_addon_key(w):
            # readers from Orange.data.io should go first
            def package(w):
                package = w.qualified_name().split(".")[:-1]
                package = package[:2]
                if ".".join(package) == "Orange.data":
                    return ["0"]  # force "Orange" to come first
                return package

            return package(w), w.DESCRIPTION

        self.available_readers = sorted(set(readers),
                                        key=group_readers_per_addon_key)

        layout = QGridLayout()
        layout.setSpacing(4)
        gui.widgetBox(self.controlArea, orientation=layout, box='Source')
        vbox = gui.radioButtons(None,
                                self,
                                "source",
                                box=True,
                                callback=self.load_data,
                                addToLayout=False)

        rb_button = gui.appendRadioButton(vbox, "File:", addToLayout=False)
        layout.addWidget(rb_button, 0, 0, Qt.AlignVCenter)

        box = gui.hBox(None, addToLayout=False, margin=0)
        box.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.file_combo.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.file_combo.activated[int].connect(self.select_file)
        box.layout().addWidget(self.file_combo)
        layout.addWidget(box, 0, 1)

        file_button = gui.button(None,
                                 self,
                                 '...',
                                 callback=self.browse_file,
                                 autoDefault=False)
        file_button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon))
        file_button.setSizePolicy(Policy.Maximum, Policy.Fixed)
        layout.addWidget(file_button, 0, 2)

        reload_button = gui.button(None,
                                   self,
                                   "Reload",
                                   callback=self.load_data,
                                   autoDefault=False)
        reload_button.setIcon(self.style().standardIcon(
            QStyle.SP_BrowserReload))
        reload_button.setSizePolicy(Policy.Fixed, Policy.Fixed)
        layout.addWidget(reload_button, 0, 3)

        self.sheet_box = gui.hBox(None, addToLayout=False, margin=0)
        self.sheet_combo = QComboBox()
        self.sheet_combo.activated[str].connect(self.select_sheet)
        self.sheet_combo.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.sheet_label = QLabel()
        self.sheet_label.setText('Sheet')
        self.sheet_label.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.sheet_box.layout().addWidget(self.sheet_label, Qt.AlignLeft)
        self.sheet_box.layout().addWidget(self.sheet_combo, Qt.AlignVCenter)
        layout.addWidget(self.sheet_box, 2, 1)
        self.sheet_box.hide()

        rb_button = gui.appendRadioButton(vbox, "URL:", addToLayout=False)
        layout.addWidget(rb_button, 3, 0, Qt.AlignVCenter)

        self.url_combo = url_combo = QComboBox()
        url_model = NamedURLModel(self.sheet_names)
        url_model.wrap(self.recent_urls)
        url_combo.setLineEdit(LineEditSelectOnFocus())
        url_combo.setModel(url_model)
        url_combo.setSizePolicy(Policy.Ignored, Policy.Fixed)
        url_combo.setEditable(True)
        url_combo.setInsertPolicy(url_combo.InsertAtTop)
        url_edit = url_combo.lineEdit()
        l, t, r, b = url_edit.getTextMargins()
        url_edit.setTextMargins(l + 5, t, r, b)
        layout.addWidget(url_combo, 3, 1, 1, 3)
        url_combo.activated.connect(self._url_set)
        # whit completer we set that combo box is case sensitive when
        # matching the history
        completer = QCompleter()
        completer.setCaseSensitivity(Qt.CaseSensitive)
        url_combo.setCompleter(completer)

        layout = QGridLayout()
        layout.setSpacing(4)
        gui.widgetBox(self.controlArea, orientation=layout, box='File Type')

        box = gui.hBox(None, addToLayout=False, margin=0)
        box.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.reader_combo = QComboBox(self)
        self.reader_combo.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed)
        self.reader_combo.activated[int].connect(self.select_reader)

        box.layout().addWidget(self.reader_combo)
        layout.addWidget(box, 0, 1)

        box = gui.vBox(self.controlArea, "Info")
        self.infolabel = gui.widgetLabel(box, 'No data loaded.')

        box = gui.widgetBox(self.controlArea, "Columns (Double click to edit)")
        self.domain_editor = DomainEditor(self)
        self.editor_model = self.domain_editor.model()
        box.layout().addWidget(self.domain_editor)

        box = gui.hBox(box)
        gui.button(box,
                   self,
                   "Reset",
                   callback=self.reset_domain_edit,
                   autoDefault=False)
        gui.rubber(box)
        self.apply_button = gui.button(box,
                                       self,
                                       "Apply",
                                       callback=self.apply_domain_edit)
        self.apply_button.setEnabled(False)
        self.apply_button.setFixedWidth(170)
        self.editor_model.dataChanged.connect(
            lambda: self.apply_button.setEnabled(True))

        hBox = gui.hBox(self.controlArea)
        gui.rubber(hBox)
        gui.button(hBox,
                   self,
                   "Browse documentation datasets",
                   callback=lambda: self.browse_file(True),
                   autoDefault=False)
        gui.rubber(hBox)

        self.set_file_list()
        # Must not call open_file from within __init__. open_file
        # explicitly re-enters the event loop (by a progress bar)

        self.setAcceptDrops(True)

        if self.source == self.LOCAL_FILE:
            last_path = self.last_path()
            if last_path and os.path.exists(last_path) and \
                    os.path.getsize(last_path) > self.SIZE_LIMIT:
                self.Warning.file_too_big()
                return

        QTimer.singleShot(0, self.load_data)

    @staticmethod
    def sizeHint():
        return QSize(600, 550)

    def select_file(self, n):
        assert n < len(self.recent_paths)
        super().select_file(n)
        if self.recent_paths:
            self.source = self.LOCAL_FILE
            self.load_data()
            self.set_file_list()

    def select_sheet(self):
        self.recent_paths[0].sheet = self.sheet_combo.currentText()
        self.load_data()

    def select_reader(self, n):
        if self.source != self.LOCAL_FILE:
            return  # ignore for URL's

        if self.recent_paths:
            path = self.recent_paths[0]
            if n == 0:  # default
                path.file_format = None
                self.load_data()
            elif n <= len(self.available_readers):
                reader = self.available_readers[n - 1]
                path.file_format = reader.qualified_name()
                self.load_data()
            else:  # the rest include just qualified names
                path.file_format = self.reader_combo.itemText(n)
                self.load_data()

    def _url_set(self):
        url = self.url_combo.currentText()
        pos = self.recent_urls.index(url)
        url = url.strip()

        if not urlparse(url).scheme:
            url = 'http://' + url
            self.url_combo.setItemText(pos, url)
            self.recent_urls[pos] = url

        self.source = self.URL
        self.load_data()

    def browse_file(self, in_demos=False):
        if in_demos:
            start_file = get_sample_datasets_dir()
            if not os.path.exists(start_file):
                QMessageBox.information(
                    None, "File",
                    "Cannot find the directory with documentation datasets")
                return
        else:
            start_file = self.last_path() or os.path.expanduser("~/")

        filename, reader, _ = open_filename_dialog(start_file, None,
                                                   self.available_readers)
        if not filename:
            return
        self.add_path(filename)
        if reader is not None:
            self.recent_paths[0].file_format = reader.qualified_name()

        self.source = self.LOCAL_FILE
        self.load_data()

    # Open a file, create data from it and send it over the data channel
    def load_data(self):
        # We need to catch any exception type since anything can happen in
        # file readers
        self.closeContext()
        self.domain_editor.set_domain(None)
        self.apply_button.setEnabled(False)
        self.clear_messages()
        self.set_file_list()

        error = self._try_load()
        if error:
            error()
            self.data = None
            self.sheet_box.hide()
            self.Outputs.data.send(None)
            self.infolabel.setText("No data.")

    def _try_load(self):
        self._initialize_reader_combo()

        # pylint: disable=broad-except
        if self.source == self.LOCAL_FILE:
            if self.last_path() is None:
                return self.Information.no_file_selected
            elif not os.path.exists(self.last_path()):
                return self.Error.file_not_found
        else:
            url = self.url_combo.currentText().strip()
            if not url:
                return self.Information.no_file_selected

        def mark_problematic_reader():
            self.reader_combo.setItemData(self.reader_combo.currentIndex(),
                                          QBrush(Qt.red), Qt.ForegroundRole)

        try:
            self.reader = self._get_reader()  # also sets current reader index
            assert self.reader is not None
        except MissingReaderException:
            mark_problematic_reader()
            return self.Error.missing_reader
        except Exception as ex:
            mark_problematic_reader()
            log.exception(ex)
            return lambda x=ex: self.Error.unknown(str(x))

        try:
            self._update_sheet_combo()
        except Exception:
            return self.Error.sheet_error

        with log_warnings() as warnings:
            try:
                data = self.reader.read()
            except Exception as ex:
                mark_problematic_reader()
                log.exception(ex)
                return lambda x=ex: self.Error.unknown(str(x))
            if warnings:
                self.Warning.load_warning(warnings[-1].message.args[0])

        self.infolabel.setText(self._describe(data))

        self.loaded_file = self.last_path()
        add_origin(data, self.loaded_file)
        self.data = data
        self.openContext(data.domain)
        self.apply_domain_edit()  # sends data
        return None

    def _get_reader(self) -> FileFormat:
        if self.source == self.LOCAL_FILE:
            path = self.last_path()
            self.reader_combo.setEnabled(True)
            if self.recent_paths and self.recent_paths[0].file_format:
                qname = self.recent_paths[0].file_format
                qname_index = {
                    r.qualified_name(): i
                    for i, r in enumerate(self.available_readers)
                }
                if qname in qname_index:
                    self.reader_combo.setCurrentIndex(qname_index[qname] + 1)
                else:
                    # reader may be accessible, but not in self.available_readers
                    # (perhaps its code was moved)
                    self.reader_combo.addItem(qname)
                    self.reader_combo.setCurrentIndex(
                        len(self.reader_combo) - 1)
                try:
                    reader_class = class_from_qualified_name(qname)
                except Exception as ex:
                    raise MissingReaderException(
                        f'Can not find reader "{qname}"') from ex
                reader = reader_class(path)
            else:
                self.reader_combo.setCurrentIndex(0)
                reader = FileFormat.get_reader(path)
            if self.recent_paths and self.recent_paths[0].sheet:
                reader.select_sheet(self.recent_paths[0].sheet)
            return reader
        else:
            url = self.url_combo.currentText().strip()
            return UrlReader(url)

    def _update_sheet_combo(self):
        if len(self.reader.sheets) < 2:
            self.sheet_box.hide()
            self.reader.select_sheet(None)
            return

        self.sheet_combo.clear()
        self.sheet_combo.addItems(self.reader.sheets)
        self._select_active_sheet()
        self.sheet_box.show()

    def _select_active_sheet(self):
        try:
            idx = self.reader.sheets.index(self.reader.sheet)
            self.sheet_combo.setCurrentIndex(idx)
        except ValueError:
            # Requested sheet does not exist in this file
            self.reader.select_sheet(None)
            self.sheet_combo.setCurrentIndex(0)

    def _initialize_reader_combo(self):
        self.reader_combo.clear()
        filters = [format_filter(f) for f in self.available_readers]
        self.reader_combo.addItems([DEFAULT_READER_TEXT] + filters)
        self.reader_combo.setCurrentIndex(0)
        self.reader_combo.setDisabled(True)
        # additional readers may be added in self._get_reader()

    @staticmethod
    def _describe(table):
        def missing_prop(prop):
            if prop:
                return f"({prop * 100:.1f}% missing values)"
            else:
                return "(no missing values)"

        domain = table.domain
        text = ""

        attrs = getattr(table, "attributes", {})
        descs = [
            attrs[desc] for desc in ("Name", "Description") if desc in attrs
        ]
        if len(descs) == 2:
            descs[0] = f"<b>{descs[0]}</b>"
        if descs:
            text += f"<p>{'<br/>'.join(descs)}</p>"

        text += f"<p>{len(table)} instance(s)"

        missing_in_attr = missing_prop(table.has_missing_attribute()
                                       and table.get_nan_frequency_attribute())
        missing_in_class = missing_prop(table.has_missing_class()
                                        and table.get_nan_frequency_class())
        text += f"<br/>{len(domain.attributes)} feature(s) {missing_in_attr}"
        if domain.has_continuous_class:
            text += f"<br/>Regression; numerical class {missing_in_class}"
        elif domain.has_discrete_class:
            text += "<br/>Classification; categorical class " \
                f"with {len(domain.class_var.values)} values {missing_in_class}"
        elif table.domain.class_vars:
            text += "<br/>Multi-target; " \
                f"{len(table.domain.class_vars)} target variables " \
                f"{missing_in_class}"
        else:
            text += "<br/>Data has no target variable."
        text += f"<br/>{len(domain.metas)} meta attribute(s)"
        text += "</p>"

        if 'Timestamp' in table.domain:
            # Google Forms uses this header to timestamp responses
            text += f"<p>First entry: {table[0, 'Timestamp']}<br/>" \
                f"Last entry: {table[-1, 'Timestamp']}</p>"
        return text

    def storeSpecificSettings(self):
        self.current_context.modified_variables = self.variables[:]

    def retrieveSpecificSettings(self):
        if hasattr(self.current_context, "modified_variables"):
            self.variables[:] = self.current_context.modified_variables

    def reset_domain_edit(self):
        self.domain_editor.reset_domain()
        self.apply_domain_edit()

    def _inspect_discrete_variables(self, domain):
        for var in chain(domain.variables, domain.metas):
            if var.is_discrete and len(var.values) > 100:
                self.Warning.performance_warning()

    def apply_domain_edit(self):
        self.Warning.performance_warning.clear()
        self.Warning.renamed_vars.clear()
        if self.data is None:
            table = None
        else:
            domain, cols, renamed = \
                self.domain_editor.get_domain(self.data.domain, self.data,
                                              deduplicate=True)
            if not (domain.variables or domain.metas):
                table = None
            elif domain is self.data.domain:
                table = self.data
            else:
                X, y, m = cols
                table = Table.from_numpy(domain, X, y, m, self.data.W)
                table.name = self.data.name
                table.ids = np.array(self.data.ids)
                table.attributes = getattr(self.data, 'attributes', {})
                self._inspect_discrete_variables(domain)
            if renamed:
                self.Warning.renamed_vars(f"Renamed: {', '.join(renamed)}")

        self.Warning.multiple_targets(
            shown=table is not None and len(table.domain.class_vars) > 1)
        self.Outputs.data.send(table)
        self.apply_button.setEnabled(False)

    def get_widget_name_extension(self):
        _, name = os.path.split(self.loaded_file)
        return os.path.splitext(name)[0]

    def send_report(self):
        def get_ext_name(filename):
            try:
                return FileFormat.names[os.path.splitext(filename)[1]]
            except KeyError:
                return "unknown"

        if self.data is None:
            self.report_paragraph("File", "No file.")
            return

        if self.source == self.LOCAL_FILE:
            home = os.path.expanduser("~")
            if self.loaded_file.startswith(home):
                # os.path.join does not like ~
                name = "~" + os.path.sep + \
                       self.loaded_file[len(home):].lstrip("/").lstrip("\\")
            else:
                name = self.loaded_file
            if self.sheet_combo.isVisible():
                name += f" ({self.sheet_combo.currentText()})"
            self.report_items("File", [("File name", name),
                                       ("Format", get_ext_name(name))])
        else:
            self.report_items("Data", [("Resource", self.url),
                                       ("Format", get_ext_name(self.url))])

        self.report_data("Data", self.data)

    @staticmethod
    def dragEnterEvent(event):
        """Accept drops of valid file urls"""
        urls = event.mimeData().urls()
        if urls:
            try:
                FileFormat.get_reader(urls[0].toLocalFile())
                event.acceptProposedAction()
            except MissingReaderException:
                pass

    def dropEvent(self, event):
        """Handle file drops"""
        urls = event.mimeData().urls()
        if urls:
            self.add_path(urls[0].toLocalFile())  # add first file
            self.source = self.LOCAL_FILE
            self.load_data()

    def workflowEnvChanged(self, key, value, oldvalue):
        """
        Function called when environment changes (e.g. while saving the scheme)
        It make sure that all environment connected values are modified
        (e.g. relative file paths are changed)
        """
        self.update_file_list(key, value, oldvalue)
Ejemplo n.º 33
0
    def __setupUi(self):
        """Set up the UI.
        """
        if self.__macUnified:
            self.tab = QToolBar()

            self.addToolBar(Qt.TopToolBarArea, self.tab)
            self.setUnifiedTitleAndToolBarOnMac(True)

            # This does not seem to work
            self.setWindowFlags(self.windowFlags() & \
                                ~Qt.MacWindowToolBarButtonHint)

            self.tab.actionTriggered[QAction].connect(
                self.__macOnToolBarAction
            )

            central = QStackedWidget()

            central.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        else:
            self.tab = central = QTabWidget(self)

        self.stack = central

        self.setCentralWidget(central)

        # General Tab
        tab = QWidget()
        self.addTab(tab, self.tr("General"),
                    toolTip=self.tr("General Options"))

        form = QFormLayout()
        tab.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        nodes = QWidget(self, objectName="nodes")
        nodes.setLayout(QVBoxLayout())
        nodes.layout().setContentsMargins(0, 0, 0, 0)

        cb_anim = QCheckBox(
            self.tr("Enable node animations"),
            objectName="enable-node-animations",
            toolTip=self.tr("Enable shadow and ping animations for nodes "
                            "in the workflow.")
        )
        self.bind(cb_anim, "checked", "schemeedit/enable-node-animations")
        nodes.layout().addWidget(cb_anim)

        form.addRow(self.tr("Nodes"), nodes)

        links = QWidget(self, objectName="links")
        links.setLayout(QVBoxLayout())
        links.layout().setContentsMargins(0, 0, 0, 0)

        cb_show = QCheckBox(
            self.tr("Show channel names between widgets"),
            objectName="show-channel-names",
            toolTip=self.tr("Show source and sink channel names "
                            "over the links.")
        )

        self.bind(cb_show, "checked", "schemeedit/show-channel-names")

        links.layout().addWidget(cb_show)

        form.addRow(self.tr("Links"), links)

        quickmenu = QWidget(self, objectName="quickmenu-options")
        quickmenu.setLayout(QVBoxLayout())
        quickmenu.layout().setContentsMargins(0, 0, 0, 0)

        cb1 = QCheckBox(self.tr("On double click"),
                        toolTip=self.tr("Open quick menu on a double click "
                                        "on an empty spot in the canvas"))

        cb2 = QCheckBox(self.tr("On right click"),
                        toolTip=self.tr("Open quick menu on a right click "
                                        "on an empty spot in the canvas"))

        cb3 = QCheckBox(self.tr("On space key press"),
                        toolTip=self.tr("On Space key press while the mouse"
                                        "is hovering over the canvas."))

        cb4 = QCheckBox(self.tr("On any key press"),
                        toolTip=self.tr("On any key press while the mouse"
                                        "is hovering over the canvas."))

        self.bind(cb1, "checked", "quickmenu/trigger-on-double-click")
        self.bind(cb2, "checked", "quickmenu/trigger-on-right-click")
        self.bind(cb3, "checked", "quickmenu/trigger-on-space-key")
        self.bind(cb4, "checked", "quickmenu/trigger-on-any-key")

        quickmenu.layout().addWidget(cb1)
        quickmenu.layout().addWidget(cb2)
        quickmenu.layout().addWidget(cb3)
        quickmenu.layout().addWidget(cb4)

        form.addRow(self.tr("Open quick menu on"), quickmenu)

        startup = QWidget(self, objectName="startup-group")
        startup.setLayout(QVBoxLayout())
        startup.layout().setContentsMargins(0, 0, 0, 0)

        cb_splash = QCheckBox(self.tr("Show splash screen"), self,
                              objectName="show-splash-screen")

        cb_welcome = QCheckBox(self.tr("Show welcome screen"), self,
                               objectName="show-welcome-screen")

        cb_updates = QCheckBox(self.tr("Check for updates"), self,
                               objectName="check-updates")

        self.bind(cb_splash, "checked", "startup/show-splash-screen")
        self.bind(cb_welcome, "checked", "startup/show-welcome-screen")
        self.bind(cb_updates, "checked", "startup/check-updates")

        startup.layout().addWidget(cb_splash)
        startup.layout().addWidget(cb_welcome)
        startup.layout().addWidget(cb_updates)

        form.addRow(self.tr("On startup"), startup)

        toolbox = QWidget(self, objectName="toolbox-group")
        toolbox.setLayout(QVBoxLayout())
        toolbox.layout().setContentsMargins(0, 0, 0, 0)

        exclusive = QCheckBox(self.tr("Only one tab can be open at a time"))

        self.bind(exclusive, "checked", "mainwindow/toolbox-dock-exclusive")

        toolbox.layout().addWidget(exclusive)

        form.addRow(self.tr("Tool box"), toolbox)
        tab.setLayout(form)

        # Output Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Output"),
                    toolTip="Output Redirection")

        form = QFormLayout()

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        combo = QComboBox()
        combo.addItems([self.tr("Critical"),
                        self.tr("Error"),
                        self.tr("Warn"),
                        self.tr("Info"),
                        self.tr("Debug")])
        self.bind(combo, "currentIndex", "logging/level")
        layout.addWidget(combo)
        box.setLayout(layout)
        form.addRow(self.tr("Logging"), box)

        box = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        cb1 = QCheckBox(self.tr("Open in external browser"),
                        objectName="open-in-external-browser")
        self.bind(cb1, "checked", "help/open-in-external-browser")
        layout.addWidget(cb1)
        box.setLayout(layout)
        form.addRow(self.tr("Help window"), box)

        tab.setLayout(form)

        # Error Reporting Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Error Reporting"),
                    toolTip="Settings related to error reporting")

        form = QFormLayout()
        line_edit_mid = QLineEdit()
        self.bind(line_edit_mid, "text", "error-reporting/machine-id")
        form.addRow("Machine ID:", line_edit_mid)
        tab.setLayout(form)

        # Add-ons Tab
        tab = QWidget()
        self.addTab(tab, self.tr("Add-ons"),
                    toolTip="Settings related to add-on installation")

        form = QFormLayout()
        conda = QWidget(self, objectName="conda-group")
        conda.setLayout(QVBoxLayout())
        conda.layout().setContentsMargins(0, 0, 0, 0)

        cb_conda_install = QCheckBox(self.tr("Install add-ons with conda"), self,
                                     objectName="allow-conda-experimental")
        self.bind(cb_conda_install, "checked", "add-ons/allow-conda-experimental")
        conda.layout().addWidget(cb_conda_install)

        form.addRow(self.tr("Conda"), conda)
        tab.setLayout(form)

        if self.__macUnified:
            # Need some sensible size otherwise mac unified toolbar 'takes'
            # the space that should be used for layout of the contents
            self.adjustSize()
Ejemplo n.º 34
0
class Scale(BaseEditor):
    NoCentering, CenterMean, CenterMedian = 0, 1, 2
    NoScaling, ScaleBySD, ScaleBySpan = 0, 1, 2

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setLayout(QVBoxLayout())

        form = QFormLayout()
        self.__centercb = QComboBox()
        self.__centercb.addItems(
            ["No Centering", "Center by Mean", "Center by Median"])

        self.__scalecb = QComboBox()
        self.__scalecb.addItems(["No scaling", "Scale by SD", "Scale by span"])

        form.addRow("Center:", self.__centercb)
        form.addRow("Scale:", self.__scalecb)
        self.layout().addLayout(form)
        self.__centercb.currentIndexChanged.connect(self.changed)
        self.__scalecb.currentIndexChanged.connect(self.changed)
        self.__centercb.activated.connect(self.edited)
        self.__scalecb.activated.connect(self.edited)

    def setParameters(self, params):
        center = params.get("center", Scale.CenterMean)
        scale = params.get("scale", Scale.ScaleBySD)
        self.__centercb.setCurrentIndex(center)
        self.__scalecb.setCurrentIndex(scale)

    def parameters(self):
        return {
            "center": self.__centercb.currentIndex(),
            "scale": self.__scalecb.currentIndex()
        }

    @staticmethod
    def createinstance(params):
        center = params.get("center", Scale.CenterMean)
        scale = params.get("scale", Scale.ScaleBySD)

        if center == Scale.NoCentering:
            center = None
        elif center == Scale.CenterMean:
            center = _Scaling.mean
        elif center == Scale.CenterMedian:
            center = _Scaling.median
        else:
            assert False

        if scale == Scale.NoScaling:
            scale = None
        elif scale == Scale.ScaleBySD:
            scale = _Scaling.std
        elif scale == Scale.ScaleBySpan:
            scale = _Scaling.span
        else:
            assert False

        return _Scaling(center=center, scale=scale)

    def __repr__(self):
        return "{}, {}".format(self.__centercb.currentText(),
                               self.__scalecb.currentText())