def __init__(self, parent=None, **kwargs): # type: (Optional[QWidget], Any) -> None super().__init__(parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] # type: List[_Tab] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed) self.setMouseTracking(True) self.__sloppyButton = None # type: Optional[QAbstractButton] self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout)
def __init__(self): super().__init__() self.corpus = None self.learning_thread = None # Commit button gui.auto_commit(self.buttonsArea, self, 'autocommit', 'Commit', box=False) button_group = QButtonGroup(self, exclusive=True) button_group.buttonClicked[int].connect(self.change_method) self.widgets = [] method_layout = QVBoxLayout() self.controlArea.layout().addLayout(method_layout) for i, (method, attr_name) in enumerate(self.methods): widget = method(self, title='Options') widget.setFixedWidth(self.control_area_width) widget.valueChanged.connect(self.commit) self.widgets.append(widget) setattr(self, attr_name, widget) rb = QRadioButton(text=widget.Model.name) button_group.addButton(rb, i) method_layout.addWidget(rb) method_layout.addWidget(widget) button_group.button(self.method_index).setChecked(True) self.toggle_widgets() method_layout.addStretch() # Topics description self.topic_desc = TopicViewer() self.topic_desc.topicSelected.connect(self.send_topic_by_id) self.mainArea.layout().addWidget(self.topic_desc) self.topic_desc.setFocus()
def __init__(self, parent=None, master=None, **kwargs): super().__init__(parent, **kwargs) self._group_var = self.DEFAULT_GROUP_VAR self._master = master self._master.input_data_changed.connect(self._set_model) self.setLayout(QVBoxLayout()) form = QFormLayout() cpm_b = QRadioButton("Counts per million", checked=True) med_b = QRadioButton("Median") self.group = QButtonGroup() self.group.buttonClicked.connect(self._on_button_clicked) for i, button in enumerate([cpm_b, med_b]): index = index_to_enum(Normalize.Method, i).value self.group.addButton(button, index - 1) form.addRow(button) self.group_by_check = QCheckBox("Cell Groups: ", enabled=self.DEFAULT_GROUP_BY) self.group_by_check.clicked.connect(self.edited) self.group_by_combo = QComboBox(enabled=self.DEFAULT_GROUP_BY) self.group_by_model = DomainModel(order=(DomainModel.METAS, DomainModel.CLASSES), valid_types=DiscreteVariable, alphabetical=True) self.group_by_combo.setModel(self.group_by_model) self.group_by_combo.currentIndexChanged.connect(self.changed) self.group_by_combo.activated.connect(self.edited) form.addRow(self.group_by_check, self.group_by_combo) self.layout().addLayout(form) self._set_model()
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.__method = self.DEFAULT_METHOD self.setLayout(QGridLayout()) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonClicked.connect(self.__method_rb_clicked) for method_id in range(len(self.Methods)): method = self.Methods[method_id] rb = QRadioButton(method.name) rb.setChecked(self.__method == method_id) rb.setToolTip(self.get_tooltip(method)) self.__group.addButton(rb, method_id) self.layout().addWidget(rb)
def setup_method_layout(self): self.group = QButtonGroup(self, exclusive=True) if self.initialize_methods: self.methods = [method() for method in self.methods] for i, method in enumerate(self.methods): rb = QRadioButton(self, text=self.textify(method.name)) rb.setChecked(i == self.method_index) rb.setToolTip(self.get_tooltip(method)) self.group.addButton(rb, i) self.method_layout.addWidget(rb, i, 0) self.group.buttonClicked.connect(self.update_value)
def __init__(self, tree, dataset, master, parent=None): QWidget.__init__(self, parent) Control.__init__(self, tree, dataset, master) self.setLayout(QVBoxLayout()) self.buttonGroup = QButtonGroup(self) self.values = [] for i, option in enumerate(tree.subelements_top("Option")): rb = QRadioButton(option.displayName, self) self.buttonGroup.addButton(rb) self.buttonGroup.setId(rb, i) self.layout().addWidget(rb) self.values.append(option.value) self.buttonGroup.button(0).setChecked(True)
class RadioBooleanFilter(QWidget, Control): """ Boolean filter (Only/Exclude) """ def __init__(self, tree, dataset, master, parent=None): QWidget.__init__(self, parent) Control.__init__(self, tree, dataset, master) self.setLayout(QVBoxLayout()) self.buttonGroup = QButtonGroup(self) self.values = [] for i, option in enumerate(tree.subelements_top("Option")): rb = QRadioButton(option.displayName, self) self.buttonGroup.addButton(rb) self.buttonGroup.setId(rb, i) self.layout().addWidget(rb) self.values.append(option.value) self.buttonGroup.button(0).setChecked(True) def value(self): return {"excluded": "%i" % self.buttonGroup.checkedId()} def get_filter(self): return self.tree.internalName, self.value() def query(self): return [("Filter", self.tree, self.value())] def setControlValue(self, name, value): for i, v in enumerate(self.values): if v == value: button = self.buttonGroup.button(i) button.setChecked(True) break
class RadioBooleanFilter(QWidget, Control): """ Boolean filter (Only/Exclude) """ def __init__(self, tree, dataset, master, parent=None): QWidget.__init__(self, parent) Control.__init__(self, tree, dataset, master) self.setLayout(QVBoxLayout()) self.buttonGroup = QButtonGroup(self) self.values = [] for i, option in enumerate(tree.subelements_top("Option")): rb = QRadioButton(option.displayName, self) self.buttonGroup.addButton(rb) self.buttonGroup.setId(rb, i) self.layout().addWidget(rb) self.values.append(option.value) self.buttonGroup.button(0).setChecked(True) def value(self): return {"excluded": "%i" % self.buttonGroup.checkedId()} def get_filter(self): return self.tree.internalName, self.value() def query(self): return [("Filter", self.tree, self.value())] def setControlValue(self, name, value): for i, v in enumerate(self.values): if v == value: button = self.buttonGroup.button(i) button.setChecked(True) break
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.fixedThresh = 50 self.percThresh = 5 self.useFixedThreshold = False self.filter0 = True self.setLayout(QVBoxLayout()) self.layout().addWidget(QLabel("Remove features with too many")) options = ["missing values", "zeros"] self.filter_buttons = QButtonGroup(exclusive=True) self.filter_buttons.buttonClicked.connect(self.filterByClicked) for idx, option, in enumerate(options): btn = QRadioButton(self, text=option, checked=idx == 0) self.filter_buttons.addButton(btn, id=idx) self.layout().addWidget(btn) self.layout().addSpacing(20) filter_settings = QGroupBox(title='Threshold:', flat=True) filter_settings.setLayout(QFormLayout()) self.settings_buttons = QButtonGroup(exclusive=True) self.settings_buttons.buttonClicked.connect(self.filterSettingsClicked) btn_perc = QRadioButton(self, text='Percentage', checked=not self.useFixedThreshold) self.settings_buttons.addButton(btn_perc, id=0) self.percSpin = QSpinBox(minimum=0, maximum=100, value=self.percThresh, enabled=not self.useFixedThreshold) self.percSpin.valueChanged[int].connect(self.setPercThresh) self.percSpin.editingFinished.connect(self.edited) btn_fix = QRadioButton(self, text='Fixed', checked=self.useFixedThreshold) self.settings_buttons.addButton(btn_fix, id=1) self.fixedSpin = QSpinBox(minimum=0, maximum=1000000, value=self.fixedThresh, enabled=self.useFixedThreshold) self.fixedSpin.valueChanged[int].connect(self.setFixedThresh) self.fixedSpin.editingFinished.connect(self.edited) filter_settings.layout().addRow(btn_fix, self.fixedSpin) filter_settings.layout().addRow(btn_perc, self.percSpin) self.layout().addWidget(filter_settings)
def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed ) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout)
def qbuttongroup_emit_clicked(bg: QButtonGroup, id_: int): button = bg.button(id_) bg.buttonClicked.emit(button) if QT_VERSION_INFO >= (5, 15): bg.idClicked.emit(id_) if QT_VERSION_INFO < (6, 0): bg.buttonClicked[int].emit(id_)
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.__scoreidx = 0 self.__strategy = UnivariateFeatureSelect.Fixed self.__k = 10 self.__p = 75.0 box = QGroupBox(title="Score", flat=True) box.setLayout(QVBoxLayout()) self.__cb = cb = QComboBox(self, ) self.__cb.currentIndexChanged.connect(self.setScoreIndex) self.__cb.activated.connect(self.edited) box.layout().addWidget(cb) self.layout().addWidget(box) box = QGroupBox(title="Strategy", flat=True) self.__group = group = QButtonGroup(self, exclusive=True) self.__spins = {} form = QFormLayout() fixedrb = QRadioButton("Fixed:", checked=True) group.addButton(fixedrb, UnivariateFeatureSelect.Fixed) kspin = QSpinBox( minimum=1, value=self.__k, enabled=self.__strategy == UnivariateFeatureSelect.Fixed) kspin.valueChanged[int].connect(self.setK) kspin.editingFinished.connect(self.edited) self.__spins[UnivariateFeatureSelect.Fixed] = kspin form.addRow(fixedrb, kspin) percrb = QRadioButton("Percentile:") group.addButton(percrb, UnivariateFeatureSelect.Percentile) pspin = QDoubleSpinBox( minimum=0.0, maximum=100.0, singleStep=0.5, value=self.__p, suffix="%", enabled=self.__strategy == UnivariateFeatureSelect.Percentile) pspin.valueChanged[float].connect(self.setP) pspin.editingFinished.connect(self.edited) self.__spins[UnivariateFeatureSelect.Percentile] = pspin # Percentile controls disabled for now. pspin.setEnabled(False) percrb.setEnabled(False) form.addRow(percrb, pspin) # form.addRow(QRadioButton("FDR"), QDoubleSpinBox()) # form.addRow(QRadioButton("FPR"), QDoubleSpinBox()) # form.addRow(QRadioButton("FWE"), QDoubleSpinBox()) self.__group.buttonClicked.connect(self.__on_buttonClicked) box.setLayout(form) self.layout().addWidget(box)
class SingleMethodModule(PreprocessorModule): Methods = NotImplemented DEFAULT_METHOD = NotImplemented def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.__method = self.DEFAULT_METHOD self.setLayout(QGridLayout()) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonClicked.connect(self.__method_rb_clicked) for method_id in range(len(self.Methods)): method = self.Methods[method_id] rb = QRadioButton(method.name) rb.setChecked(self.__method == method_id) rb.setToolTip(self.get_tooltip(method)) self.__group.addButton(rb, method_id) self.layout().addWidget(rb) @property def method(self) -> int: return self.__method def setParameters(self, params: Dict): self._set_method(params.get("method", self.DEFAULT_METHOD)) def _set_method(self, method: int): if self.__method != method: self.__method = method self.__group.button(method).setChecked(True) self.changed.emit() def __method_rb_clicked(self): self._set_method(self.__group.checkedId()) self.edited.emit() def parameters(self) -> Dict: return {"method": self.__method} def __repr__(self): return self.Methods[self.__method].name
def __init__(self): super().__init__() self.corpus = None self.learning_thread = None # Commit button gui.auto_commit(self.buttonsArea, self, 'autocommit', 'Commit', box=False) button_group = QButtonGroup(self, exclusive=True) button_group.buttonClicked[int].connect(self.change_method) self.widgets = [] method_layout = QVBoxLayout() self.controlArea.layout().addLayout(method_layout) for i, (method, attr_name) in enumerate(self.methods): widget = method(self, title='Options') widget.setFixedWidth(self.control_area_width) widget.valueChanged.connect(self.commit) self.widgets.append(widget) setattr(self, attr_name, widget) rb = QRadioButton(text=widget.Model.name) button_group.addButton(rb, i) method_layout.addWidget(rb) method_layout.addWidget(widget) button_group.button(self.method_index).setChecked(True) self.toggle_widgets() method_layout.addStretch() # Topics description self.topic_desc = TopicViewer() self.topic_desc.topicSelected.connect(self.send_topic_by_id) self.mainArea.layout().addWidget(self.topic_desc) self.topic_desc.setFocus()
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.__method = ImputeEditor.Average self.__group = group = QButtonGroup(self, exclusive=True) group.buttonClicked.connect(self.__on_buttonClicked) for methodid in [self.Average, self.Random, self.DropRows]: text = self.Names[methodid] rb = QRadioButton(text=text, checked=self.__method == methodid) group.addButton(rb, methodid) self.layout().addWidget(rb)
class SingleMethodModule(PreprocessorModule): method_index = settings.Setting(0) initialize_methods = True def setup_method_layout(self): self.group = QButtonGroup(self, exclusive=True) if self.initialize_methods: self.methods = [method() for method in self.methods] for i, method in enumerate(self.methods): rb = QRadioButton(self, text=self.textify(method.name)) rb.setChecked(i == self.method_index) rb.setToolTip(self.get_tooltip(method)) self.group.addButton(rb, i) self.method_layout.addWidget(rb, i, 0) self.group.buttonClicked.connect(self.update_value) def get_value(self): self.method_index = self.group.checkedId() return self.methods[self.method_index]
class SingleMethodModule(PreprocessorModule): method_index = settings.Setting(0) initialize_methods = True def setup_method_layout(self): self.group = QButtonGroup(self, exclusive=True) if self.initialize_methods: self.methods = [method() for method in self.methods] for i, method in enumerate(self.methods): rb = QRadioButton(self, text=self.textify(method.name)) rb.setChecked(i == self.method_index) rb.setToolTip(self.get_tooltip(method)) self.group.addButton(rb, i) self.method_layout.addWidget(rb, i, 0) self.group.buttonClicked.connect(self.update_value) def get_value(self): self.method_index = self.group.checkedId() return self.methods[self.method_index]
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self._n_genes = self.DEFAULT_N_GENS self._n_groups = self.DEFAULT_N_GROUPS form = QFormLayout() self.n_genes_spin = QSpinBox(minimum=1, maximum=10**6, value=self._n_genes) self.n_genes_spin.valueChanged[int].connect(self._set_n_genes) self.n_genes_spin.editingFinished.connect(self.edited) form.addRow("Number of genes:", self.n_genes_spin) self.layout().addLayout(form) disp_b = QRadioButton("Dispersion", checked=True) vari_b = QRadioButton("Variance") mean_b = QRadioButton("Mean") self.group = QButtonGroup() self.group.buttonClicked.connect(self._on_button_clicked) for i, button in enumerate([disp_b, vari_b, mean_b]): index = index_to_enum(SelectMostVariableGenes.Method, i).value self.group.addButton(button, index - 1) form.addRow(button) self.stats_check = QCheckBox("Compute statistics for", checked=self.DEFAULT_COMPUTE_STATS) self.stats_check.clicked.connect(self.edited) self.n_groups_spin = QSpinBox(minimum=1, value=self._n_groups) self.n_groups_spin.valueChanged[int].connect(self._set_n_groups) self.n_groups_spin.editingFinished.connect(self.edited) box = QHBoxLayout() box.addWidget(self.stats_check) box.addWidget(self.n_groups_spin) box.addWidget(QLabel("gene groups.")) box.addStretch() self.layout().addLayout(box)
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.__treatment = Continuize.Indicators self.__group = group = QButtonGroup(exclusive=True) group.buttonClicked.connect(self.__on_buttonClicked) for treatment, text in ContinuizeEditor.Continuizers.items(): rb = QRadioButton(text=text, checked=self.__treatment == treatment) group.addButton(rb, enum_to_index(ContinuizeEditor._Type, treatment)) self.layout().addWidget(rb) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
def __init__(self, tree, dataset, master, parent=None): QWidget.__init__(self, parent) Control.__init__(self, tree, dataset, master) self.setLayout(QVBoxLayout()) self.buttonGroup = QButtonGroup(self) self.values = [] for i, option in enumerate(tree.subelements_top("Option")): rb = QRadioButton(option.displayName, self) self.buttonGroup.addButton(rb) self.buttonGroup.setId(rb, i) self.layout().addWidget(rb) self.values.append(option.value) self.buttonGroup.button(0).setChecked(True)
def setup_method_layout(self): self.group = QButtonGroup(self, exclusive=True) if self.initialize_methods: self.methods = [method() for method in self.methods] for i, method in enumerate(self.methods): rb = QRadioButton(self, text=self.textify(method.name)) rb.setChecked(i == self.method_index) rb.setToolTip(self.get_tooltip(method)) self.group.addButton(rb, i) self.method_layout.addWidget(rb, i, 0) self.group.buttonClicked.connect(self.update_value)
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.__method = Scale.NormalizeBySD self.__group = group = QButtonGroup(self, exclusive=True) group.buttonClicked.connect(self.__on_buttonClicked) for methodid in [self.NormalizeBySD, self.CenterByMean, self.ScaleBySD, self.NormalizeSpan_NonZeroBased, self.NormalizeBySpan_ZeroBased]: text = self.Names[methodid] rb = QRadioButton(text=text, checked=self.__method == methodid) group.addButton(rb, methodid) self.layout().addWidget(rb)
def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.setLayout(QVBoxLayout()) self.__strategy = RandomFeatureSelectEditor.Fixed self.__k = 10 self.__p = 75.0 box = QGroupBox(title="Strategy", flat=True) self.__group = group = QButtonGroup(self, exclusive=True) self.__spins = {} form = QFormLayout() fixedrb = QRadioButton("Fixed", checked=True) group.addButton(fixedrb, RandomFeatureSelectEditor.Fixed) kspin = QSpinBox( minimum=1, value=self.__k, enabled=self.__strategy == RandomFeatureSelectEditor.Fixed, ) kspin.valueChanged[int].connect(self.setK) kspin.editingFinished.connect(self.edited) self.__spins[RandomFeatureSelectEditor.Fixed] = kspin form.addRow(fixedrb, kspin) percrb = QRadioButton("Percentage") group.addButton(percrb, RandomFeatureSelectEditor.Percentage) pspin = QDoubleSpinBox( minimum=0.0, maximum=100.0, singleStep=0.5, value=self.__p, suffix="%", enabled=self.__strategy == RandomFeatureSelectEditor.Percentage, ) pspin.valueChanged[float].connect(self.setP) pspin.editingFinished.connect(self.edited) self.__spins[RandomFeatureSelectEditor.Percentage] = pspin form.addRow(percrb, pspin) self.__group.buttonClicked.connect(self.__on_buttonClicked) box.setLayout(form) self.layout().addWidget(box)
def __init__(self, parent=None, **kwargs): BaseEditor.__init__(self, parent, **kwargs) self.__method = DiscretizeEditor.EqualFreq self.__nintervals = 4 layout = QVBoxLayout() self.setLayout(layout) self.__group = group = QButtonGroup(self, exclusive=True) for method in [ self.EntropyMDL, self.EqualFreq, self.EqualWidth, self.Drop ]: rb = QRadioButton(self, text=self.Names[method], checked=self.__method == method) layout.addWidget(rb) group.addButton(rb, method) group.buttonClicked.connect(self.__on_buttonClicked) self.__slbox = slbox = QGroupBox( title="间隔数(等宽/等频)Number of intervals (for equal width/frequency)", flat=True) slbox.setLayout(QHBoxLayout()) self.__slider = slider = QSlider(orientation=Qt.Horizontal, minimum=2, maximum=10, value=self.__nintervals, enabled=self.__method in [self.EqualFreq, self.EqualWidth], pageStep=1, tickPosition=QSlider.TicksBelow) slider.valueChanged.connect(self.__on_valueChanged) slbox.layout().addWidget(slider) self.__slabel = slabel = QLabel() slbox.layout().addWidget(slabel) container = QHBoxLayout() container.setContentsMargins(13, 0, 0, 0) container.addWidget(slbox) self.layout().insertLayout(3, container) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
def __init__(self): super().__init__() ConcurrentWidgetMixin.__init__(self) self.corpus = None self.learning_thread = None self.__pending_selection = self.selection self.perplexity = "n/a" self.coherence = "n/a" # Commit button gui.auto_commit(self.buttonsArea, self, 'autocommit', 'Commit', box=False) button_group = QButtonGroup(self, exclusive=True) button_group.buttonClicked[int].connect(self.change_method) self.widgets = [] method_layout = QVBoxLayout() self.controlArea.layout().addLayout(method_layout) for i, (method, attr_name) in enumerate(self.methods): widget = method(self, title='Options') widget.setFixedWidth(self.control_area_width) widget.valueChanged.connect(self.commit.deferred) self.widgets.append(widget) setattr(self, attr_name, widget) rb = QRadioButton(text=widget.Model.name) button_group.addButton(rb, i) method_layout.addWidget(rb) method_layout.addWidget(widget) button_group.button(self.method_index).setChecked(True) self.toggle_widgets() method_layout.addStretch() box = gui.vBox(self.controlArea, "Topic evaluation") gui.label(box, self, "Log perplexity: %(perplexity)s") gui.label(box, self, "Topic coherence: %(coherence)s") self.controlArea.layout().insertWidget(1, box) # Topics description self.topic_desc = TopicViewer() self.topic_desc.topicSelected.connect(self.send_topic_by_id) self.mainArea.layout().addWidget(self.topic_desc) self.topic_desc.setFocus()
class RemoveSparseEditor(BaseEditor): options = ["missing", "zeros"] def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.fixedThresh = 50 self.percThresh = 5 self.useFixedThreshold = False self.filter0 = True self.setLayout(QVBoxLayout()) self.layout().addWidget(QLabel("Remove features with too many")) options = ["missing values", "zeros"] self.filter_buttons = QButtonGroup(exclusive=True) self.filter_buttons.buttonClicked.connect(self.filterByClicked) for idx, option, in enumerate(options): btn = QRadioButton(self, text=option, checked=idx == 0) self.filter_buttons.addButton(btn, id=idx) self.layout().addWidget(btn) self.layout().addSpacing(20) filter_settings = QGroupBox(title='Threshold:', flat=True) filter_settings.setLayout(QFormLayout()) self.settings_buttons = QButtonGroup(exclusive=True) self.settings_buttons.buttonClicked.connect(self.filterSettingsClicked) btn_perc = QRadioButton(self, text='Percentage', checked=not self.useFixedThreshold) self.settings_buttons.addButton(btn_perc, id=0) self.percSpin = QSpinBox(minimum=0, maximum=100, value=self.percThresh, enabled=not self.useFixedThreshold) self.percSpin.valueChanged[int].connect(self.setPercThresh) self.percSpin.editingFinished.connect(self.edited) btn_fix = QRadioButton(self, text='Fixed', checked=self.useFixedThreshold) self.settings_buttons.addButton(btn_fix, id=1) self.fixedSpin = QSpinBox(minimum=0, maximum=1000000, value=self.fixedThresh, enabled=self.useFixedThreshold) self.fixedSpin.valueChanged[int].connect(self.setFixedThresh) self.fixedSpin.editingFinished.connect(self.edited) filter_settings.layout().addRow(btn_fix, self.fixedSpin) filter_settings.layout().addRow(btn_perc, self.percSpin) self.layout().addWidget(filter_settings) def filterSettingsClicked(self): self.setUseFixedThreshold(self.settings_buttons.checkedId()) self.percSpin.setEnabled(not self.useFixedThreshold) self.fixedSpin.setEnabled(self.useFixedThreshold) self.edited.emit() def filterByClicked(self): self.setFilter0(self.filter_buttons.checkedId()) def setFilter0(self, id_): if self.filter0 != id_: self.filter0 = id_ self.edited.emit() def setFixedThresh(self, thresh): if self.fixedThresh != thresh: self.fixedThresh = thresh self.fixedSpin.setValue(thresh) self.edited.emit() def setPercThresh(self, thresh): if self.percThresh != thresh: self.percThresh = thresh self.percSpin.setValue(thresh) self.edited.emit() def setUseFixedThreshold(self, val): if self.useFixedThreshold != val: self.useFixedThreshold = val self.edited.emit() def parameters(self): return { 'fixedThresh': self.fixedThresh, 'percThresh': self.percThresh, 'useFixedThreshold': self.useFixedThreshold, 'filter0': self.filter0 } def setParameters(self, params): self.setPercThresh(params.get('percThresh', 5)) self.setFixedThresh(params.get('fixedThresh', 50)) self.setUseFixedThreshold(params.get('useFixedThreshold', False)) self.setFilter0(params.get('filter0', True)) @staticmethod def createinstance(params): params = dict(params) filter0 = params.pop('filter0', True) useFixedThreshold = params.pop('useFixedThreshold', True) if useFixedThreshold: threshold = params.pop('fixedThresh', 50) else: threshold = params.pop('percThresh', 5) / 100 return RemoveSparse(threshold, filter0)
class TabBarWidget(QWidget): """ A tab bar widget using tool buttons as tabs. """ # TODO: A uniform size box layout. currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): QWidget.__init__(self, parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed ) self.setMouseTracking(True) self.__sloppyButton = None self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=None, toolTip=None): """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=None, toolTip=None): """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): """ Remove a tab at `index`. """ if index >= 0 and index < self.count(): self.layout().takeItem(index) tab = self.__tabs.pop(index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=icon) self.__updateTab(index) def setTabToolTip(self, index, toolTip): """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace(palette=palette) self.__updateTab(index) def setCurrentIndex(self, index): """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): """ Return the current index. """ return self.__currentIndex def button(self, index): """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): if self.__iconSize != size: self.__iconSize = size for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width()+10, 0) p4 = self.pos() + QPoint(self.width()+10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return QWidget.eventFilter(self, receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return QWidget.leaveEvent(self, event)
def _setup_control_area(self) -> None: box = gui.widgetBox(self.controlArea, "Word Scoring Methods") for value, (n, _, tt) in SCORING_METHODS.items(): b = gui.hBox(box, margin=0) gui.checkBox( b, self, value, label=n, callback=self.__setting_changed, tooltip=tt, ) if value in ADDITIONAL_OPTIONS: value, options = ADDITIONAL_OPTIONS[value] gui.comboBox( b, self, value, items=options, callback=self.__setting_changed, ) box = gui.widgetBox(self.controlArea, "Aggregation") gui.comboBox( box, self, "aggregation", items=[n for n in AGGREGATIONS], callback=self.__setting_changed, ) gui.rubber(self.controlArea) # select words box box = gui.vBox(self.buttonsArea, "Select Documents") grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) self._sel_method_buttons = QButtonGroup() for method, label in enumerate(SelectionMethods.ITEMS): button = QRadioButton(label) button.setChecked(method == self.sel_method) grid.addWidget(button, method, 0) self._sel_method_buttons.addButton(button, method) self._sel_method_buttons.buttonClicked[int].connect( self.__set_selection_method) spin = gui.spin( box, self, "n_selected", 1, 999, addToLayout=False, callback=lambda: self.__set_selection_method(SelectionMethods. N_BEST), ) grid.addWidget(spin, 3, 1) box.layout().addLayout(grid) # autocommit gui.auto_send(self.buttonsArea, self, "auto_commit")
def __init__(self): super().__init__() self.measure_scores = None self.update_scores = True self.usefulAttributes = [] self.learners = {} self.labels = [] self.out_domain_desc = None self.all_measures = SCORES self.selectedMeasures = dict([(m.name, True) for m in self.all_measures]) # Discrete (0) or continuous (1) class mode self.rankMode = 0 self.data = None self.discMeasures = [m for m in self.all_measures if issubclass(DiscreteVariable, m.score.class_type)] self.contMeasures = [m for m in self.all_measures if issubclass(ContinuousVariable, m.score.class_type)] self.score_checks = [] self.cls_scoring_box = gui.vBox(None, "Scoring for Classification") self.reg_scoring_box = gui.vBox(None, "Scoring for Regression") boxes = [self.cls_scoring_box] * 7 + [self.reg_scoring_box] * 2 for _score, var, box in zip(SCORES, self._score_vars, boxes): check = gui.checkBox( box, self, var, label=_score.name, callback=lambda val=_score: self.measuresSelectionChanged(val)) self.score_checks.append(check) self.score_stack = QStackedWidget(self) self.score_stack.addWidget(self.cls_scoring_box) self.score_stack.addWidget(self.reg_scoring_box) self.score_stack.addWidget(QWidget()) self.controlArea.layout().addWidget(self.score_stack) gui.rubber(self.controlArea) selMethBox = gui.vBox( self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=self.nSelectedChanged) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) # Discrete, continuous and no_class table views are stacked self.ranksViewStack = QStackedLayout() self.mainArea.layout().addLayout(self.ranksViewStack) self.discRanksView = QTableView() self.ranksViewStack.addWidget(self.discRanksView) self.discRanksView.setSelectionBehavior(QTableView.SelectRows) self.discRanksView.setSelectionMode(QTableView.MultiSelection) self.discRanksView.setSortingEnabled(True) self.discRanksLabels = ["#"] + [m.shortname for m in self.discMeasures] self.discRanksModel = QStandardItemModel(self) self.discRanksModel.setHorizontalHeaderLabels(self.discRanksLabels) self.discRanksProxyModel = MySortProxyModel(self) self.discRanksProxyModel.setSourceModel(self.discRanksModel) self.discRanksView.setModel(self.discRanksProxyModel) self.discRanksView.setColumnWidth(0, 20) self.discRanksView.selectionModel().selectionChanged.connect( self.commit ) self.discRanksView.pressed.connect(self.onSelectItem) self.discRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.discRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[0] is not None: self.discRanksView.horizontalHeader().restoreState( self.headerState[0]) self.contRanksView = QTableView() self.ranksViewStack.addWidget(self.contRanksView) self.contRanksView.setSelectionBehavior(QTableView.SelectRows) self.contRanksView.setSelectionMode(QTableView.MultiSelection) self.contRanksView.setSortingEnabled(True) self.contRanksLabels = ["#"] + [m.shortname for m in self.contMeasures] self.contRanksModel = QStandardItemModel(self) self.contRanksModel.setHorizontalHeaderLabels(self.contRanksLabels) self.contRanksProxyModel = MySortProxyModel(self) self.contRanksProxyModel.setSourceModel(self.contRanksModel) self.contRanksView.setModel(self.contRanksProxyModel) self.contRanksView.setColumnWidth(0, 20) self.contRanksView.selectionModel().selectionChanged.connect( self.commit ) self.contRanksView.pressed.connect(self.onSelectItem) self.contRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.contRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[1] is not None: self.contRanksView.horizontalHeader().restoreState( self.headerState[1]) self.noClassRanksView = QTableView() self.ranksViewStack.addWidget(self.noClassRanksView) self.noClassRanksView.setSelectionBehavior(QTableView.SelectRows) self.noClassRanksView.setSelectionMode(QTableView.MultiSelection) self.noClassRanksView.setSortingEnabled(True) self.noClassRanksLabels = ["#"] self.noClassRanksModel = QStandardItemModel(self) self.noClassRanksModel.setHorizontalHeaderLabels(self.noClassRanksLabels) self.noClassRanksProxyModel = MySortProxyModel(self) self.noClassRanksProxyModel.setSourceModel(self.noClassRanksModel) self.noClassRanksView.setModel(self.noClassRanksProxyModel) self.noClassRanksView.setColumnWidth(0, 20) self.noClassRanksView.selectionModel().selectionChanged.connect( self.commit ) self.noClassRanksView.pressed.connect(self.onSelectItem) self.noClassRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.noClassRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[2] is not None: self.noClassRanksView.horizontalHeader().restoreState( self.headerState[2]) # Switch the current view to Discrete self.switchRanksMode(0) self.resetInternals() self.updateDelegates() self.updateVisibleScoreColumns() self.resize(690, 500) self.measure_scores = table((len(self.measures), 0), None)
class OWRank(OWWidget): name = "Rank" description = "Rank and filter data features by their relevance." icon = "icons/Rank.svg" priority = 1102 buttons_area_orientation = Qt.Vertical inputs = [("Data", Table, "setData"), ("Scorer", score.Scorer, "set_learner", widget.Multiple)] outputs = [("Reduced Data", Table, widget.Default), ("Scores", Table)] SelectNone, SelectAll, SelectManual, SelectNBest = range(4) cls_default_selected = Setting({"Gain Ratio", "Gini Decrease"}) reg_default_selected = Setting({"Univariate Linear Regression", "RReliefF"}) selectMethod = Setting(SelectNBest) nSelected = Setting(5) auto_apply = Setting(True) # Header state for discrete/continuous/no_class scores headerState = Setting([None, None, None]) settings_version = 1 settingsHandler = DomainContextHandler() selected_rows = ContextSetting([]) gain = inf_gain = gini = anova = chi2 = ulr = relief = rrelief = fcbc = True _score_vars = ["gain", "inf_gain", "gini", "anova", "chi2", "relief", "fcbc", "ulr", "rrelief"] class Warning(OWWidget.Warning): no_target_var = Msg("Data does not have a target variable") class Error(OWWidget.Error): invalid_type = Msg("Cannot handle target variable type {}") inadequate_learner = Msg("{}") def __init__(self): super().__init__() self.measure_scores = None self.update_scores = True self.usefulAttributes = [] self.learners = {} self.labels = [] self.out_domain_desc = None self.all_measures = SCORES self.selectedMeasures = dict([(m.name, True) for m in self.all_measures]) # Discrete (0) or continuous (1) class mode self.rankMode = 0 self.data = None self.discMeasures = [m for m in self.all_measures if issubclass(DiscreteVariable, m.score.class_type)] self.contMeasures = [m for m in self.all_measures if issubclass(ContinuousVariable, m.score.class_type)] self.score_checks = [] self.cls_scoring_box = gui.vBox(None, "Scoring for Classification") self.reg_scoring_box = gui.vBox(None, "Scoring for Regression") boxes = [self.cls_scoring_box] * 7 + [self.reg_scoring_box] * 2 for _score, var, box in zip(SCORES, self._score_vars, boxes): check = gui.checkBox( box, self, var, label=_score.name, callback=lambda val=_score: self.measuresSelectionChanged(val)) self.score_checks.append(check) self.score_stack = QStackedWidget(self) self.score_stack.addWidget(self.cls_scoring_box) self.score_stack.addWidget(self.reg_scoring_box) self.score_stack.addWidget(QWidget()) self.controlArea.layout().addWidget(self.score_stack) gui.rubber(self.controlArea) selMethBox = gui.vBox( self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=self.nSelectedChanged) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) # Discrete, continuous and no_class table views are stacked self.ranksViewStack = QStackedLayout() self.mainArea.layout().addLayout(self.ranksViewStack) self.discRanksView = QTableView() self.ranksViewStack.addWidget(self.discRanksView) self.discRanksView.setSelectionBehavior(QTableView.SelectRows) self.discRanksView.setSelectionMode(QTableView.MultiSelection) self.discRanksView.setSortingEnabled(True) self.discRanksLabels = ["#"] + [m.shortname for m in self.discMeasures] self.discRanksModel = QStandardItemModel(self) self.discRanksModel.setHorizontalHeaderLabels(self.discRanksLabels) self.discRanksProxyModel = MySortProxyModel(self) self.discRanksProxyModel.setSourceModel(self.discRanksModel) self.discRanksView.setModel(self.discRanksProxyModel) self.discRanksView.setColumnWidth(0, 20) self.discRanksView.selectionModel().selectionChanged.connect( self.commit ) self.discRanksView.pressed.connect(self.onSelectItem) self.discRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.discRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[0] is not None: self.discRanksView.horizontalHeader().restoreState( self.headerState[0]) self.contRanksView = QTableView() self.ranksViewStack.addWidget(self.contRanksView) self.contRanksView.setSelectionBehavior(QTableView.SelectRows) self.contRanksView.setSelectionMode(QTableView.MultiSelection) self.contRanksView.setSortingEnabled(True) self.contRanksLabels = ["#"] + [m.shortname for m in self.contMeasures] self.contRanksModel = QStandardItemModel(self) self.contRanksModel.setHorizontalHeaderLabels(self.contRanksLabels) self.contRanksProxyModel = MySortProxyModel(self) self.contRanksProxyModel.setSourceModel(self.contRanksModel) self.contRanksView.setModel(self.contRanksProxyModel) self.contRanksView.setColumnWidth(0, 20) self.contRanksView.selectionModel().selectionChanged.connect( self.commit ) self.contRanksView.pressed.connect(self.onSelectItem) self.contRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.contRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[1] is not None: self.contRanksView.horizontalHeader().restoreState( self.headerState[1]) self.noClassRanksView = QTableView() self.ranksViewStack.addWidget(self.noClassRanksView) self.noClassRanksView.setSelectionBehavior(QTableView.SelectRows) self.noClassRanksView.setSelectionMode(QTableView.MultiSelection) self.noClassRanksView.setSortingEnabled(True) self.noClassRanksLabels = ["#"] self.noClassRanksModel = QStandardItemModel(self) self.noClassRanksModel.setHorizontalHeaderLabels(self.noClassRanksLabels) self.noClassRanksProxyModel = MySortProxyModel(self) self.noClassRanksProxyModel.setSourceModel(self.noClassRanksModel) self.noClassRanksView.setModel(self.noClassRanksProxyModel) self.noClassRanksView.setColumnWidth(0, 20) self.noClassRanksView.selectionModel().selectionChanged.connect( self.commit ) self.noClassRanksView.pressed.connect(self.onSelectItem) self.noClassRanksView.horizontalHeader().sectionClicked.connect( self.headerClick ) self.noClassRanksView.verticalHeader().sectionClicked.connect( self.onSelectItem ) if self.headerState[2] is not None: self.noClassRanksView.horizontalHeader().restoreState( self.headerState[2]) # Switch the current view to Discrete self.switchRanksMode(0) self.resetInternals() self.updateDelegates() self.updateVisibleScoreColumns() self.resize(690, 500) self.measure_scores = table((len(self.measures), 0), None) def switchRanksMode(self, index): """ Switch between discrete/continuous/no_class mode """ self.rankMode = index self.ranksViewStack.setCurrentIndex(index) if index == 0: self.ranksView = self.discRanksView self.ranksModel = self.discRanksModel self.ranksProxyModel = self.discRanksProxyModel self.measures = self.discMeasures self.selected_checks = self.cls_default_selected self.reg_scoring_box.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.cls_scoring_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) elif index == 1: self.ranksView = self.contRanksView self.ranksModel = self.contRanksModel self.ranksProxyModel = self.contRanksProxyModel self.measures = self.contMeasures self.selected_checks = self.reg_default_selected self.cls_scoring_box.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.reg_scoring_box.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) else: self.ranksView = self.noClassRanksView self.ranksModel = self.noClassRanksModel self.ranksProxyModel = self.noClassRanksProxyModel self.measures = [] self.selected_checks = set() self.reg_scoring_box.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.cls_scoring_box.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) shape = (len(self.measures) + len(self.learners), 0) self.measure_scores = table(shape, None) self.update_scores = False for check, score in zip(self.score_checks, SCORES): check.setChecked(score.name in self.selected_checks) self.update_scores = True self.score_stack.setCurrentIndex(index) self.updateVisibleScoreColumns() @check_sql_input def setData(self, data): self.closeContext() self.clear_messages() self.resetInternals() self.data = data self.switchRanksMode(0) if self.data is not None: domain = self.data.domain attrs = domain.attributes self.usefulAttributes = [attr for attr in attrs if attr.is_discrete or attr.is_continuous] if domain.has_continuous_class: self.switchRanksMode(1) elif not domain.class_var: self.Warning.no_target_var() self.switchRanksMode(2) elif not domain.has_discrete_class: self.Error.invalid_type(type(domain.class_var).__name__) if issparse(self.data.X): # keep only measures supporting sparse data self.measures = [m for m in self.measures if m.score.supports_sparse_data] self.ranksModel.setRowCount(len(attrs)) for i, a in enumerate(attrs): if a.is_discrete: v = len(a.values) else: v = "C" item = ScoreValueItem() item.setData(v, Qt.DisplayRole) self.ranksModel.setItem(i, 0, item) item = QStandardItem(a.name) item.setData(gui.attributeIconDict[a], Qt.DecorationRole) self.ranksModel.setVerticalHeaderItem(i, item) shape = (len(self.measures) + len(self.learners), len(attrs)) self.measure_scores = table(shape, None) self.updateScores() else: self.send("Scores", None) self.selected_rows = [] self.openContext(data) self.selectMethodChanged() self.commit() def get_selection(self): selection = self.ranksView.selectionModel().selection() return list(set(ind.row() for ind in selection.indexes())) def set_learner(self, learner, lid=None): if learner is None and lid is not None: del self.learners[lid] elif learner is not None: self.learners[lid] = score_meta( learner.name, learner.name, learner ) attrs_len = 0 if not self.data else len(self.data.domain.attributes) shape = (len(self.learners), attrs_len) self.measure_scores = self.measure_scores[:len(self.measures)] self.measure_scores += table(shape, None) self.contRanksModel.setHorizontalHeaderLabels(self.contRanksLabels) self.discRanksModel.setHorizontalHeaderLabels(self.discRanksLabels) self.noClassRanksModel.setHorizontalHeaderLabels( self.noClassRanksLabels) measures_mask = [False] * len(self.measures) measures_mask += [True for _ in self.learners] self.updateScores(measures_mask) self.commit() def updateScores(self, measuresMask=None): """ Update the current computed scores. If `measuresMask` is given it must be an list of bool values indicating what measures should be recomputed. """ if not self.data: return if self.data.has_missing(): self.information("Missing values have been imputed.") measures = self.measures + [v for k, v in self.learners.items()] if measuresMask is None: # Update all selected measures measuresMask = [self.selectedMeasures.get(m.name) for m in self.measures] measuresMask = measuresMask + [v.name for k, v in self.learners.items()] data = self.data learner_col = len(self.measures) if len(measuresMask) <= len(self.measures) or \ measuresMask[len(self.measures)]: self.labels = [] self.Error.inadequate_learner.clear() self.setStatusMessage("Running") with self.progressBar(): n_measure_update = len([x for x in measuresMask if x is not False]) count = 0 for index, (meas, mask) in enumerate(zip(measures, measuresMask)): if not mask: continue self.progressBarSet(90 * count / n_measure_update) count += 1 if index < len(self.measures): estimator = meas.score() try: self.measure_scores[index] = estimator(data) except ValueError: self.measure_scores[index] = [] for attr in data.domain.attributes: try: self.measure_scores[index].append( estimator(data, attr)) except ValueError: self.measure_scores[index].append(None) else: learner = meas.score if isinstance(learner, Learner) and \ not learner.check_learner_adequacy(self.data.domain): self.Error.inadequate_learner( learner.learner_adequacy_err_msg) scores = table((1, len(data.domain.attributes))) else: scores = meas.score.score_data(data) for i, row in enumerate(scores): self.labels.append(meas.shortname + str(i + 1)) if len(self.measure_scores) > learner_col: self.measure_scores[learner_col] = row else: self.measure_scores.append(row) learner_col += 1 self.progressBarSet(90) self.contRanksModel.setHorizontalHeaderLabels( self.contRanksLabels + self.labels ) self.discRanksModel.setHorizontalHeaderLabels( self.discRanksLabels + self.labels ) self.noClassRanksModel.setHorizontalHeaderLabels( self.noClassRanksLabels + self.labels ) self.updateRankModel(measuresMask) self.ranksProxyModel.invalidate() self.selectMethodChanged() self.send("Scores", self.create_scores_table(self.labels)) self.setStatusMessage("") def updateRankModel(self, measuresMask): """ Update the rankModel. """ values = [] diff = len(self.measure_scores) - len(measuresMask) if len(measuresMask): measuresMask += [measuresMask[-1]] * diff for i in range(self.ranksModel.columnCount() - 1, len(self.measure_scores), -1): self.ranksModel.removeColumn(i) for i, (scores, m) in enumerate(zip(self.measure_scores, measuresMask)): if not m and self.ranksModel.item(0, i + 1): values.append([]) continue values_one = [] for j, _score in enumerate(scores): values_one.append(_score) item = self.ranksModel.item(j, i + 1) if not item: item = ScoreValueItem() self.ranksModel.setItem(j, i + 1, item) item.setData(_score, Qt.DisplayRole) values.append(values_one) for i, (vals, m) in enumerate(zip(values, measuresMask)): if not m: continue valid_vals = [v for v in vals if v is not None] if valid_vals: vmin, vmax = min(valid_vals), max(valid_vals) for j, v in enumerate(vals): if v is not None: # Set the bar ratio role for i-th measure. ratio = float((v - vmin) / ((vmax - vmin) or 1)) item = self.ranksModel.item(j, i + 1) item.setData(ratio, gui.BarRatioRole) self.ranksView.setColumnWidth(0, 20) self.ranksView.resizeRowsToContents() def resetInternals(self): self.data = None self.usefulAttributes = [] self.ranksModel.setRowCount(0) def onSelectItem(self, index): """ Called when the user selects/unselects an item in the table view. """ self.selectMethod = OWRank.SelectManual # Manual self.selectButtons.button(self.selectMethod).setChecked(True) self.commit() def setSelectMethod(self, method): if self.selectMethod != method: self.selectMethod = method self.selectButtons.button(method).setChecked(True) self.selectMethodChanged() def selectMethodChanged(self): self.autoSelection() self.ranksView.setFocus() def nSelectedChanged(self): self.selectMethod = OWRank.SelectNBest self.selectButtons.button(self.selectMethod).setChecked(True) self.selectMethodChanged() def autoSelection(self): selModel = self.ranksView.selectionModel() rowCount = self.ranksModel.rowCount() columnCount = self.ranksModel.columnCount() model = self.ranksProxyModel if self.selectMethod == OWRank.SelectNone: selection = QItemSelection() elif self.selectMethod == OWRank.SelectAll: selection = QItemSelection( model.index(0, 0), model.index(rowCount - 1, columnCount - 1) ) elif self.selectMethod == OWRank.SelectNBest: nSelected = min(self.nSelected, rowCount) selection = QItemSelection( model.index(0, 0), model.index(nSelected - 1, columnCount - 1) ) else: selection = QItemSelection() if len(self.selected_rows): selection = QItemSelection() for row in self.selected_rows: selection.append(QItemSelectionRange( model.index(row, 0), model.index(row, columnCount - 1))) selModel.select(selection, QItemSelectionModel.ClearAndSelect) def headerClick(self, index): if index >= 1 and self.selectMethod == OWRank.SelectNBest: # Reselect the top ranked attributes self.autoSelection() # Store the header states disc = bytes(self.discRanksView.horizontalHeader().saveState()) cont = bytes(self.contRanksView.horizontalHeader().saveState()) no_class = bytes(self.noClassRanksView.horizontalHeader().saveState()) self.headerState = [disc, cont, no_class] def measuresSelectionChanged(self, measure): """Measure selection has changed. Update column visibility. """ checked = self.selectedMeasures[measure.name] self.selectedMeasures[measure.name] = not checked if not checked: self.selected_checks.add(measure.name) elif measure.name in self.selected_checks: self.selected_checks.remove(measure.name) measures_mask = [False] * len(self.measures) measures_mask += [False for _ in self.learners] # Update scores for shown column if they are not yet computed. if measure in self.measures and self.measure_scores: index = self.measures.index(measure) if all(s is None for s in self.measure_scores[index]): measures_mask[index] = True if self.update_scores: self.updateScores(measures_mask) self.updateVisibleScoreColumns() def updateVisibleScoreColumns(self): """ Update the visible columns of the scores view. """ for i, measure in enumerate(self.measures): shown = self.selectedMeasures.get(measure.name) self.ranksView.setColumnHidden(i + 1, not shown) self.ranksView.setColumnWidth(i + 1, 100) index = self.ranksView.horizontalHeader().sortIndicatorSection() if self.ranksView.isColumnHidden(index): self.headerState[self.rankMode] = None if self.headerState[self.rankMode] is None: def get_sort_by_col(measures, selected_measures): cols = [i + 1 for i, m in enumerate(measures) if m.name in selected_measures] return cols[0] if cols else len(measures) + 1 col = get_sort_by_col(self.measures, self.selected_checks) self.ranksView.sortByColumn(col, Qt.DescendingOrder) self.autoSelection() def updateDelegates(self): self.contRanksView.setItemDelegate(gui.ColoredBarItemDelegate(self)) self.discRanksView.setItemDelegate(gui.ColoredBarItemDelegate(self)) self.noClassRanksView.setItemDelegate(gui.ColoredBarItemDelegate(self)) def send_report(self): if not self.data: return self.report_domain("Input", self.data.domain) self.report_table("Ranks", self.ranksView, num_format="{:.3f}") if self.out_domain_desc is not None: self.report_items("Output", self.out_domain_desc) def commit(self): self.selected_rows = self.get_selection() if self.data and len(self.data.domain.attributes) == len( self.selected_rows): self.selectMethod = OWRank.SelectAll self.selectButtons.button(self.selectMethod).setChecked(True) selected = self.selectedAttrs() if not self.data or not selected: self.send("Reduced Data", None) self.out_domain_desc = None else: data = Table(Domain(selected, self.data.domain.class_var, self.data.domain.metas), self.data) self.send("Reduced Data", data) self.out_domain_desc = report.describe_domain(data.domain) def selectedAttrs(self): if self.data: inds = self.ranksView.selectionModel().selectedRows(0) source = self.ranksProxyModel.mapToSource inds = map(source, inds) inds = [ind.row() for ind in inds] return [self.data.domain.attributes[i] for i in inds] else: return [] def create_scores_table(self, labels): indices = [i for i, m in enumerate(self.measures) if self.selectedMeasures.get(m.name, False)] measures = [s.name for s in self.measures if self.selectedMeasures.get(s.name, False)] measures += [label for label in labels] if not measures: return None features = [ContinuousVariable(s) for s in measures] metas = [StringVariable("Feature name")] domain = Domain(features, metas=metas) scores = np.array([row for i, row in enumerate(self.measure_scores) if i in indices or i >= len(self.measures)]).T feature_names = np.array([a.name for a in self.data.domain.attributes]) # Reshape to 2d array as Table does not like 1d arrays feature_names = feature_names[:, None] new_table = Table(domain, scores, metas=feature_names) new_table.name = "Feature Scores" return new_table @classmethod def migrate_settings(cls, settings, version): if not version: # Before fc5caa1e1d716607f1f5c4e0b0be265c23280fa0 # headerState had length 2 headerState = settings.get("headerState", None) if headerState is not None and \ isinstance(headerState, tuple) and \ len(headerState) < 3: headerState = (list(headerState) + [None] * 3)[:3] settings["headerState"] = headerState
def __init__(self): super().__init__() self.data = None # type: Optional[Orange.data.Table] self._state = None # type: Optional[_FilterData] box = gui.widgetBox(self.controlArea, "Info") self._info = QLabel(box, wordWrap=True) self._info.setText("No data in input\n") box.layout().addWidget(self._info) box = gui.widgetBox(self.controlArea, "Filter Type", spacing=-1) rbg = QButtonGroup(box, exclusive=True) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) for id_ in [Cells, Genes, Data]: name, _, tip = FilterInfo[id_] b = QRadioButton(name, toolTip=tip, checked=id_ == self.selected_filter_type) rbg.addButton(b, id_) layout.addWidget(b, stretch=10, alignment=Qt.AlignCenter) box.layout().addLayout(layout) rbg.buttonClicked[int].connect(self.set_filter_type) self.filter_metric_cb = gui.comboBox( box, self, "selected_filter_metric", callback=self._update_metric, enabled=self.selected_filter_type != Data) for id_ in [DetectionCount, TotalCounts]: text, ttip = MeasureInfo[id_] self.filter_metric_cb.addItem(text) idx = self.filter_metric_cb.count() - 1 self.filter_metric_cb.setItemData(idx, ttip, Qt.ToolTipRole) self.filter_metric_cb.setCurrentIndex(self.selected_filter_metric) form = QFormLayout(labelAlignment=Qt.AlignLeft, formAlignment=Qt.AlignLeft, fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow) self._filter_box = box = gui.widgetBox( self.controlArea, "Filter", orientation=form) # type: QGroupBox self.threshold_stacks = ( QStackedWidget(enabled=self.limit_lower_enabled), QStackedWidget(enabled=self.limit_upper_enabled), ) finfo = np.finfo(np.float64) for filter_ in [Cells, Genes, Data]: if filter_ in {Cells, Genes}: minimum = 0.0 ndecimals = 1 metric = self.selected_filter_metric else: minimum = finfo.min ndecimals = 3 metric = -1 spinlower = QDoubleSpinBox( self, minimum=minimum, maximum=finfo.max, decimals=ndecimals, keyboardTracking=False, ) spinupper = QDoubleSpinBox( self, minimum=minimum, maximum=finfo.max, decimals=ndecimals, keyboardTracking=False, ) lower, upper = self.thresholds.get((filter_, metric), (0, 0)) spinlower.setValue(lower) spinupper.setValue(upper) self.threshold_stacks[0].addWidget(spinlower) self.threshold_stacks[1].addWidget(spinupper) spinlower.valueChanged.connect(self._limitchanged) spinupper.valueChanged.connect(self._limitchanged) self.threshold_stacks[0].setCurrentIndex(self.selected_filter_type) self.threshold_stacks[1].setCurrentIndex(self.selected_filter_type) self.limit_lower_enabled_cb = cb = QCheckBox( "Min", checked=self.limit_lower_enabled) cb.toggled.connect(self.set_lower_limit_enabled) cb.setAttribute(Qt.WA_LayoutUsesWidgetRect, True) form.addRow(cb, self.threshold_stacks[0]) self.limit_upper_enabled_cb = cb = QCheckBox( "Max", checked=self.limit_upper_enabled) cb.toggled.connect(self.set_upper_limit_enabled) cb.setAttribute(Qt.WA_LayoutUsesWidgetRect, True) form.addRow(cb, self.threshold_stacks[1]) box = gui.widgetBox(self.controlArea, "Plot Options") self._showpoints = gui.checkBox(box, self, "display_dotplot", "Show data points", callback=self._update_dotplot) self.log_scale_cb = QCheckBox("Log scale", checked=self.scale == Scale.Log1p.name) self.log_scale_cb.toggled[bool].connect( lambda state: self.set_filter_scale(Scale.Log1p if state else Scale.Linear)) box.layout().addWidget(self.log_scale_cb) self.controlArea.layout().addStretch(10) gui.auto_commit(self.controlArea, self, "auto_commit", "Commit") self._view = pg.GraphicsView() self._view.enableMouse(False) self._view.setAntialiasing(True) self._plot = plot = ViolinPlot() self._plot.setDataPointsVisible(self.display_dotplot) self._plot.setSelectionMode( (ViolinPlot.Low if self.limit_lower_enabled else 0) | (ViolinPlot.High if self.limit_upper_enabled else 0)) self._plot.setRange(QRectF(-1., 0., 2., 1.)) self._plot.selectionEdited.connect(self._limitchanged_plot) self._view.setCentralWidget(self._plot) bottom = self._plot.getAxis("bottom") # type: pg.AxisItem bottom.hide() plot.setMouseEnabled(False, False) plot.hideButtons() self.mainArea.layout().addWidget(self._view) # Coalescing commit timer self._committimer = QTimer(self, singleShot=True) self._committimer.timeout.connect(self.commit) self.addAction( QAction("Select All", self, shortcut=QKeySequence.SelectAll, triggered=self._select_all)) self._setup_axes()
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 __init__(self): super().__init__() self.scorers = OrderedDict() self.out_domain_desc = None self.data = None self.problem_type_mode = ProblemType.CLASSIFICATION if not self.selected_methods: self.selected_methods = {method.name for method in SCORES if method.is_default} # GUI self.ranksModel = model = TableModel(parent=self) # type: TableModel self.ranksView = view = TableView(self) # type: TableView self.mainArea.layout().addWidget(view) view.setModel(model) view.setColumnWidth(0, 30) view.selectionModel().selectionChanged.connect(self.on_select) def _set_select_manual(): self.setSelectionMethod(OWRank.SelectManual) view.pressed.connect(_set_select_manual) view.verticalHeader().sectionClicked.connect(_set_select_manual) view.horizontalHeader().sectionClicked.connect(self.headerClick) self.measuresStack = stacked = QStackedWidget(self) self.controlArea.layout().addWidget(stacked) for scoring_methods in (CLS_SCORES, REG_SCORES, []): box = gui.vBox(None, "Scoring Methods" if scoring_methods else None) stacked.addWidget(box) for method in scoring_methods: box.layout().addWidget(QCheckBox( method.name, self, objectName=method.shortname, # To be easily found in tests checked=method.name in self.selected_methods, stateChanged=partial(self.methodSelectionChanged, method_name=method.name))) gui.rubber(box) gui.rubber(self.controlArea) self.switchProblemType(ProblemType.CLASSIFICATION) selMethBox = gui.vBox(self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectionMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=lambda: self.setSelectionMethod(OWRank.SelectNBest)) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectionMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) self.resize(690, 500)
def __init__(self): super().__init__() self.scorers = OrderedDict() self.out_domain_desc = None self.data = None self.problem_type_mode = ProblemType.CLASSIFICATION if not self.selected_methods: self.selected_methods = {method.name for method in SCORES if method.is_default} # GUI self.ranksModel = model = TableModel(parent=self) # type: TableModel self.ranksView = view = TableView(self) # type: TableView self.mainArea.layout().addWidget(view) view.setModel(model) view.setColumnWidth(0, 30) view.selectionModel().selectionChanged.connect(self.on_select) def _set_select_manual(): self.setSelectionMethod(OWRank.SelectManual) view.pressed.connect(_set_select_manual) view.verticalHeader().sectionClicked.connect(_set_select_manual) view.horizontalHeader().sectionClicked.connect(self.headerClick) self.measuresStack = stacked = QStackedWidget(self) self.controlArea.layout().addWidget(stacked) for scoring_methods in (CLS_SCORES, REG_SCORES, []): box = gui.vBox(None, "Scoring Methods" if scoring_methods else None) stacked.addWidget(box) for method in scoring_methods: box.layout().addWidget(QCheckBox( method.name, self, objectName=method.shortname, # To be easily found in tests checked=method.name in self.selected_methods, stateChanged=partial(self.methodSelectionChanged, method_name=method.name))) gui.rubber(box) gui.rubber(self.controlArea) self.switchProblemType(ProblemType.CLASSIFICATION) selMethBox = gui.vBox(self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectionMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=lambda: self.setSelectionMethod(OWRank.SelectNBest)) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectionMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) self.resize(690, 500)
class OWRank(OWWidget): name = "Rank" description = "Rank and filter data features by their relevance." icon = "icons/Rank.svg" priority = 1102 keywords = [] buttons_area_orientation = Qt.Vertical class Inputs: data = Input("Data", Table) scorer = Input("Scorer", score.Scorer, multiple=True) class Outputs: reduced_data = Output("Reduced Data", Table, default=True) scores = Output("Scores", Table) features = Output("Features", AttributeList, dynamic=False) SelectNone, SelectAll, SelectManual, SelectNBest = range(4) nSelected = ContextSetting(5) auto_apply = Setting(True) sorting = Setting((0, Qt.DescendingOrder)) selected_methods = Setting(set()) settings_version = 2 settingsHandler = DomainContextHandler() selected_rows = ContextSetting([]) selectionMethod = ContextSetting(SelectNBest) class Information(OWWidget.Information): no_target_var = Msg("Data does not have a single target variable. " "You can still connect in unsupervised scorers " "such as PCA.") missings_imputed = Msg('Missing values will be imputed as needed.') class Error(OWWidget.Error): invalid_type = Msg("Cannot handle target variable type {}") inadequate_learner = Msg("Scorer {} inadequate: {}") no_attributes = Msg("Data does not have a single attribute.") def __init__(self): super().__init__() self.scorers = OrderedDict() self.out_domain_desc = None self.data = None self.problem_type_mode = ProblemType.CLASSIFICATION if not self.selected_methods: self.selected_methods = {method.name for method in SCORES if method.is_default} # GUI self.ranksModel = model = TableModel(parent=self) # type: TableModel self.ranksView = view = TableView(self) # type: TableView self.mainArea.layout().addWidget(view) view.setModel(model) view.setColumnWidth(0, 30) view.selectionModel().selectionChanged.connect(self.on_select) def _set_select_manual(): self.setSelectionMethod(OWRank.SelectManual) view.pressed.connect(_set_select_manual) view.verticalHeader().sectionClicked.connect(_set_select_manual) view.horizontalHeader().sectionClicked.connect(self.headerClick) self.measuresStack = stacked = QStackedWidget(self) self.controlArea.layout().addWidget(stacked) for scoring_methods in (CLS_SCORES, REG_SCORES, []): box = gui.vBox(None, "Scoring Methods" if scoring_methods else None) stacked.addWidget(box) for method in scoring_methods: box.layout().addWidget(QCheckBox( method.name, self, objectName=method.shortname, # To be easily found in tests checked=method.name in self.selected_methods, stateChanged=partial(self.methodSelectionChanged, method_name=method.name))) gui.rubber(box) gui.rubber(self.controlArea) self.switchProblemType(ProblemType.CLASSIFICATION) selMethBox = gui.vBox(self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectionMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=lambda: self.setSelectionMethod(OWRank.SelectNBest)) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectionMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) self.resize(690, 500) def switchProblemType(self, index): """ Switch between discrete/continuous/no_class mode """ self.measuresStack.setCurrentIndex(index) self.problem_type_mode = index @Inputs.data @check_sql_input def set_data(self, data): self.closeContext() self.selected_rows = [] self.ranksModel.clear() self.ranksModel.resetSorting(True) self.get_method_scores.cache_clear() self.get_scorer_scores.cache_clear() self.Error.clear() self.Information.clear() self.Information.missings_imputed( shown=data is not None and data.has_missing()) if data is not None and not len(data.domain.attributes): data = None self.Error.no_attributes() self.data = data self.switchProblemType(ProblemType.CLASSIFICATION) if self.data is not None: domain = self.data.domain if domain.has_discrete_class: problem_type = ProblemType.CLASSIFICATION elif domain.has_continuous_class: problem_type = ProblemType.REGRESSION elif not domain.class_var: self.Information.no_target_var() problem_type = ProblemType.UNSUPERVISED else: # This can happen? self.Error.invalid_type(type(domain.class_var).__name__) problem_type = None if problem_type is not None: self.switchProblemType(problem_type) self.ranksModel.setVerticalHeaderLabels(domain.attributes) self.ranksView.setVHeaderFixedWidthFromLabel( max((a.name for a in domain.attributes), key=len)) self.selectionMethod = OWRank.SelectNBest self.openContext(data) self.selectButtons.button(self.selectionMethod).setChecked(True) def handleNewSignals(self): self.setStatusMessage('Running') self.updateScores() self.setStatusMessage('') self.on_select() @Inputs.scorer def set_learner(self, scorer, id): if scorer is None: self.scorers.pop(id, None) else: # Avoid caching a (possibly stale) previous instance of the same # Scorer passed via the same signal if id in self.scorers: self.get_scorer_scores.cache_clear() self.scorers[id] = ScoreMeta(scorer.name, scorer.name, scorer, ProblemType.from_variable(scorer.class_type), False) @memoize_method() def get_method_scores(self, method): estimator = method.scorer() data = self.data try: scores = np.asarray(estimator(data)) except ValueError: log.warning("Scorer %s wasn't able to compute all scores at once", method.name) try: scores = np.array([estimator(data, attr) for attr in data.domain.attributes]) except ValueError: log.error( "Scorer %s wasn't able to compute scores at all", method.name) scores = np.full(len(data.domain.attributes), np.nan) return scores @memoize_method() def get_scorer_scores(self, scorer): try: scores = scorer.scorer.score_data(self.data).T except ValueError: log.error( "Scorer %s wasn't able to compute scores at all", scorer.name) scores = np.full((len(self.data.domain.attributes), 1), np.nan) labels = ((scorer.shortname,) if scores.shape[1] == 1 else tuple(scorer.shortname + '_' + str(i) for i in range(1, 1 + scores.shape[1]))) return scores, labels def updateScores(self): if self.data is None: self.ranksModel.clear() self.Outputs.scores.send(None) return methods = [method for method in SCORES if (method.name in self.selected_methods and method.problem_type == self.problem_type_mode and (not issparse(self.data.X) or method.scorer.supports_sparse_data))] scorers = [] self.Error.inadequate_learner.clear() for scorer in self.scorers.values(): if scorer.problem_type in (self.problem_type_mode, ProblemType.UNSUPERVISED): scorers.append(scorer) else: self.Error.inadequate_learner(scorer.name, scorer.learner_adequacy_err_msg) method_scores = tuple(self.get_method_scores(method) for method in methods) scorer_scores, scorer_labels = (), () if scorers: scorer_scores, scorer_labels = zip(*(self.get_scorer_scores(scorer) for scorer in scorers)) scorer_labels = tuple(chain.from_iterable(scorer_labels)) labels = tuple(method.shortname for method in methods) + scorer_labels model_array = np.column_stack( ([len(a.values) if a.is_discrete else np.nan for a in self.data.domain.attributes],) + (method_scores if method_scores else ()) + (scorer_scores if scorer_scores else ()) ) for column, values in enumerate(model_array.T): self.ranksModel.setExtremesFrom(column, values) self.ranksModel.wrap(model_array.tolist()) self.ranksModel.setHorizontalHeaderLabels(('#',) + labels) self.ranksView.setColumnWidth(0, 40) # Re-apply sort try: sort_column, sort_order = self.sorting if sort_column < len(labels): # adds 1 for '#' (discrete count) column self.ranksModel.sort(sort_column + 1, sort_order) self.ranksView.horizontalHeader().setSortIndicator(sort_column + 1, sort_order) except ValueError: pass self.autoSelection() self.Outputs.scores.send(self.create_scores_table(labels)) def on_select(self): # Save indices of attributes in the original, unsorted domain self.selected_rows = self.ranksModel.mapToSourceRows([ i.row() for i in self.ranksView.selectionModel().selectedRows(0)]) self.commit() def setSelectionMethod(self, method): self.selectionMethod = method self.selectButtons.button(method).setChecked(True) self.autoSelection() def autoSelection(self): selModel = self.ranksView.selectionModel() model = self.ranksModel rowCount = model.rowCount() columnCount = model.columnCount() if self.selectionMethod == OWRank.SelectNone: selection = QItemSelection() elif self.selectionMethod == OWRank.SelectAll: selection = QItemSelection( model.index(0, 0), model.index(rowCount - 1, columnCount - 1) ) elif self.selectionMethod == OWRank.SelectNBest: nSelected = min(self.nSelected, rowCount) selection = QItemSelection( model.index(0, 0), model.index(nSelected - 1, columnCount - 1) ) else: selection = QItemSelection() if len(self.selected_rows): for row in model.mapFromSourceRows(self.selected_rows): selection.append(QItemSelectionRange( model.index(row, 0), model.index(row, columnCount - 1))) selModel.select(selection, QItemSelectionModel.ClearAndSelect) def headerClick(self, index): if index >= 1 and self.selectionMethod == OWRank.SelectNBest: # Reselect the top ranked attributes self.autoSelection() # Store the header states sort_order = self.ranksModel.sortOrder() sort_column = self.ranksModel.sortColumn() - 1 # -1 for '#' (discrete count) column self.sorting = (sort_column, sort_order) def methodSelectionChanged(self, state, method_name): if state == Qt.Checked: self.selected_methods.add(method_name) elif method_name in self.selected_methods: self.selected_methods.remove(method_name) self.updateScores() def send_report(self): if not self.data: return self.report_domain("Input", self.data.domain) self.report_table("Ranks", self.ranksView, num_format="{:.3f}") if self.out_domain_desc is not None: self.report_items("Output", self.out_domain_desc) def commit(self): selected_attrs = [] if self.data is not None: selected_attrs = [self.data.domain.attributes[i] for i in self.selected_rows] if not selected_attrs: self.Outputs.reduced_data.send(None) self.Outputs.features.send(None) self.out_domain_desc = None else: reduced_domain = Domain( selected_attrs, self.data.domain.class_var, self.data.domain.metas) data = self.data.transform(reduced_domain) self.Outputs.reduced_data.send(data) self.Outputs.features.send(AttributeList(selected_attrs)) self.out_domain_desc = report.describe_domain(data.domain) def create_scores_table(self, labels): model_list = self.ranksModel.tolist() if not model_list or len(model_list[0]) == 1: # Empty or just n_values column return None domain = Domain([ContinuousVariable(label) for label in labels], metas=[StringVariable("Feature")]) # Prevent np.inf scores finfo = np.finfo(np.float64) scores = np.clip(np.array(model_list)[:, 1:], finfo.min, finfo.max) feature_names = np.array([a.name for a in self.data.domain.attributes]) # Reshape to 2d array as Table does not like 1d arrays feature_names = feature_names[:, None] new_table = Table(domain, scores, metas=feature_names) new_table.name = "Feature Scores" return new_table @classmethod def migrate_settings(cls, settings, version): # If older settings, restore sort header to default # Saved selected_rows will likely be incorrect if version is None or version < 2: column, order = 0, Qt.DescendingOrder headerState = settings.pop("headerState", None) # Lacking knowledge of last problemType, use discrete ranks view's ordering if isinstance(headerState, (tuple, list)): headerState = headerState[0] if isinstance(headerState, bytes): hview = QHeaderView(Qt.Horizontal) hview.restoreState(headerState) column, order = hview.sortIndicatorSection() - 1, hview.sortIndicatorOrder() settings["sorting"] = (column, order) @classmethod def migrate_context(cls, context, version): if version is None or version < 2: # Old selection was saved as sorted indices. New selection is original indices. # Since we can't devise the latter without first computing the ranks, # just reset the selection to avoid confusion. context.values['selected_rows'] = []
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()
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)
class TabBarWidget(QWidget): """ A vertical tab bar widget using tool buttons as for tabs. """ currentChanged = Signal(int) def __init__(self, parent=None, **kwargs): # type: (Optional[QWidget], Any) -> None super().__init__(parent, **kwargs) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) self.__tabs = [] # type: List[_Tab] self.__currentIndex = -1 self.__changeOnHover = False self.__iconSize = QSize(26, 26) self.__group = QButtonGroup(self, exclusive=True) self.__group.buttonPressed[QAbstractButton].connect( self.__onButtonPressed) self.setMouseTracking(True) self.__sloppyButton = None # type: Optional[QAbstractButton] self.__sloppyRegion = QRegion() self.__sloppyTimer = QTimer(self, singleShot=True) self.__sloppyTimer.timeout.connect(self.__onSloppyTimeout) def setChangeOnHover(self, changeOnHover): # type: (bool) -> None """ If set to ``True`` the tab widget will change the current index when the mouse hovers over a tab button. """ if self.__changeOnHover != changeOnHover: self.__changeOnHover = changeOnHover def changeOnHover(self): # type: () -> bool """ Does the current tab index follow the mouse cursor. """ return self.__changeOnHover def count(self): # type: () -> int """ Return the number of tabs in the widget. """ return len(self.__tabs) def addTab(self, text, icon=QIcon(), toolTip=""): # type: (str, QIcon, str) -> int """ Add a new tab and return it's index. """ return self.insertTab(self.count(), text, icon, toolTip) def insertTab(self, index, text, icon=QIcon(), toolTip=""): # type: (int, str, QIcon, str) -> int """ Insert a tab at `index` """ button = TabButton(self, objectName="tab-button") button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) button.setIconSize(self.__iconSize) button.setMouseTracking(True) self.__group.addButton(button) button.installEventFilter(self) tab = _Tab(text, icon, toolTip, button, None, None) self.layout().insertWidget(index, button) self.__tabs.insert(index, tab) self.__updateTab(index) if self.currentIndex() == -1: self.setCurrentIndex(0) return index def removeTab(self, index): # type: (int) -> None """ Remove a tab at `index`. """ if 0 <= index < self.count(): tab = self.__tabs.pop(index) layout_index = self.layout().indexOf(tab.button) if layout_index != -1: self.layout().takeAt(layout_index) self.__group.removeButton(tab.button) tab.button.removeEventFilter(self) if tab.button is self.__sloppyButton: self.__sloppyButton = None self.__sloppyRegion = QRegion() tab.button.deleteLater() tab.button.setParent(None) if self.currentIndex() == index: if self.count(): self.setCurrentIndex(max(index - 1, 0)) else: self.setCurrentIndex(-1) def setTabIcon(self, index, icon): # type: (int, QIcon) -> None """ Set the `icon` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(icon=QIcon(icon)) self.__updateTab(index) def setTabToolTip(self, index, toolTip): # type: (int, str) -> None """ Set `toolTip` for tab at `index`. """ self.__tabs[index] = self.__tabs[index]._replace(toolTip=toolTip) self.__updateTab(index) def setTabText(self, index, text): # type: (int, str) -> None """ Set tab `text` for tab at `index` """ self.__tabs[index] = self.__tabs[index]._replace(text=text) self.__updateTab(index) def setTabPalette(self, index, palette): # type: (int, QPalette) -> None """ Set the tab button palette. """ self.__tabs[index] = self.__tabs[index]._replace( palette=QPalette(palette)) self.__updateTab(index) def setCurrentIndex(self, index): # type: (int) -> None """ Set the current tab index. """ if self.__currentIndex != index: self.__currentIndex = index self.__sloppyRegion = QRegion() self.__sloppyButton = None if index != -1: self.__tabs[index].button.setChecked(True) self.currentChanged.emit(index) def currentIndex(self): # type: () -> int """ Return the current index. """ return self.__currentIndex def button(self, index): # type: (int) -> QAbstractButton """ Return the `TabButton` instance for index. """ return self.__tabs[index].button def setIconSize(self, size): # type: (QSize) -> None if self.__iconSize != size: self.__iconSize = QSize(size) for tab in self.__tabs: tab.button.setIconSize(self.__iconSize) def __updateTab(self, index): # type: (int) -> None """ Update the tab button. """ tab = self.__tabs[index] b = tab.button if tab.text: b.setText(tab.text) if tab.icon is not None and not tab.icon.isNull(): b.setIcon(tab.icon) if tab.palette: b.setPalette(tab.palette) def __onButtonPressed(self, button): # type: (QAbstractButton) -> None for i, tab in enumerate(self.__tabs): if tab.button is button: self.setCurrentIndex(i) break def __calcSloppyRegion(self, current): # type: (QPoint) -> QRegion """ Given a current mouse cursor position return a region of the widget where hover/move events should change the current tab only on a timeout. """ p1 = current + QPoint(0, 2) p2 = current + QPoint(0, -2) p3 = self.pos() + QPoint(self.width() + 10, 0) p4 = self.pos() + QPoint(self.width() + 10, self.height()) return QRegion(QPolygon([p1, p2, p3, p4])) def __setSloppyButton(self, button): # type: (QAbstractButton) -> None """ Set the current sloppy button (a tab button inside sloppy region) and reset the sloppy timeout. """ if not button.isChecked(): self.__sloppyButton = button delay = self.style().styleHint(QStyle.SH_Menu_SubMenuPopupDelay, None) # The delay timeout is the same as used by Qt in the QMenu. self.__sloppyTimer.start(delay) else: self.__sloppyTimer.stop() def __onSloppyTimeout(self): # type: () -> None if self.__sloppyButton is not None: button = self.__sloppyButton self.__sloppyButton = None if not button.isChecked(): index = [tab.button for tab in self.__tabs].index(button) self.setCurrentIndex(index) def eventFilter(self, receiver, event): if event.type() == QEvent.MouseMove and \ isinstance(receiver, TabButton): pos = receiver.mapTo(self, event.pos()) if self.__sloppyRegion.contains(pos): self.__setSloppyButton(receiver) else: if not receiver.isChecked(): index = [tab.button for tab in self.__tabs].index(receiver) self.setCurrentIndex(index) #also update sloppy region if mouse is moved on the same icon self.__sloppyRegion = self.__calcSloppyRegion(pos) return super().eventFilter(receiver, event) def leaveEvent(self, event): self.__sloppyButton = None self.__sloppyRegion = QRegion() return super().leaveEvent(event)
def _setup_gui(self): grid = QGridLayout() box = gui.widgetBox(self.controlArea, "Scoring Methods", grid) yake_cb = gui.comboBox(self.controlArea, self, "yake_lang_index", items=YAKE_LANGUAGES, callback=self.__on_yake_lang_changed) rake_cb = gui.comboBox(self.controlArea, self, "rake_lang_index", items=RAKE_LANGUAGES, callback=self.__on_rake_lang_changed) for i, (method_name, _) in enumerate(ScoringMethods.ITEMS): check_box = QCheckBox(method_name, self) check_box.setChecked(method_name in self.selected_scoring_methods) check_box.stateChanged.connect( lambda state, name=method_name: self. __on_scoring_method_state_changed(state, name)) box.layout().addWidget(check_box, i, 0) if method_name == ScoringMethods.YAKE: box.layout().addWidget(yake_cb, i, 1) if method_name == ScoringMethods.RAKE: box.layout().addWidget(rake_cb, i, 1) box = gui.vBox(self.controlArea, "Aggregation") gui.comboBox(box, self, "agg_method", items=AggregationMethods.ITEMS, callback=self.update_scores) box = gui.vBox(self.controlArea, "Select Words") grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) box.layout().addLayout(grid) self.__sel_method_buttons = QButtonGroup() for method, label in enumerate(SelectionMethods.ITEMS): button = QRadioButton(label) button.setChecked(method == self.sel_method) grid.addWidget(button, method, 0) self.__sel_method_buttons.addButton(button, method) self.__sel_method_buttons.buttonClicked[int].connect( self._set_selection_method) spin = gui.spin(box, self, "n_selected", 1, 999, addToLayout=False, callback=lambda: self._set_selection_method( SelectionMethods.N_BEST)) grid.addWidget(spin, 3, 1) gui.rubber(self.controlArea) gui.auto_send(self.buttonsArea, self, "auto_apply") self.__filter_line_edit = QLineEdit( textChanged=self.__on_filter_changed, placeholderText="Filter...") self.mainArea.layout().addWidget(self.__filter_line_edit) def select_manual(): self._set_selection_method(SelectionMethods.MANUAL) self.view = KeywordsTableView() self.view.pressedAny.connect(select_manual) self.view.horizontalHeader().setSortIndicator(*self.DEFAULT_SORTING) self.view.horizontalHeader().sectionClicked.connect( self.__on_horizontal_header_clicked) self.mainArea.layout().addWidget(self.view) proxy = SortFilterProxyModel() proxy.setFilterKeyColumn(0) proxy.setFilterCaseSensitivity(False) self.view.setModel(proxy) self.view.model().setSourceModel(self.model) self.view.selectionModel().selectionChanged.connect( self.__on_selection_changed)
class OWKeywords(OWWidget, ConcurrentWidgetMixin): name = "Extract Keywords" description = "Infers characteristic words from the input corpus." icon = "icons/Keywords.svg" priority = 1100 keywords = ["characteristic", "term"] DEFAULT_SORTING = (1, Qt.DescendingOrder) settingsHandler = DomainContextHandler() selected_scoring_methods: Set[str] = Setting({ScoringMethods.TF_IDF}) yake_lang_index: int = Setting(YAKE_LANGUAGES.index("English")) rake_lang_index: int = Setting(RAKE_LANGUAGES.index("English")) agg_method: int = Setting(AggregationMethods.MEAN) sel_method: int = ContextSetting(SelectionMethods.N_BEST) n_selected: int = ContextSetting(3) sort_column_order: Tuple[int, int] = Setting(DEFAULT_SORTING) selected_words = ContextSetting([], schema_only=True) auto_apply: bool = Setting(True) class Inputs: corpus = Input("Corpus", Corpus) words = Input("Words", Table) class Outputs: words = Output("Words", Corpus) class Warning(OWWidget.Warning): no_words_column = Msg("Input is missing 'Words' column.") def __init__(self): OWWidget.__init__(self) ConcurrentWidgetMixin.__init__(self) self.corpus: Optional[Corpus] = None self.words: Optional[List] = None self.__cached_keywords = {} self.model = KeywordsTableModel(parent=self) self._setup_gui() def _setup_gui(self): grid = QGridLayout() box = gui.widgetBox(self.controlArea, "Scoring Methods", grid) yake_cb = gui.comboBox(self.controlArea, self, "yake_lang_index", items=YAKE_LANGUAGES, callback=self.__on_yake_lang_changed) rake_cb = gui.comboBox(self.controlArea, self, "rake_lang_index", items=RAKE_LANGUAGES, callback=self.__on_rake_lang_changed) for i, (method_name, _) in enumerate(ScoringMethods.ITEMS): check_box = QCheckBox(method_name, self) check_box.setChecked(method_name in self.selected_scoring_methods) check_box.stateChanged.connect( lambda state, name=method_name: self. __on_scoring_method_state_changed(state, name)) box.layout().addWidget(check_box, i, 0) if method_name == ScoringMethods.YAKE: box.layout().addWidget(yake_cb, i, 1) if method_name == ScoringMethods.RAKE: box.layout().addWidget(rake_cb, i, 1) box = gui.vBox(self.controlArea, "Aggregation") gui.comboBox(box, self, "agg_method", items=AggregationMethods.ITEMS, callback=self.update_scores) box = gui.vBox(self.controlArea, "Select Words") grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) box.layout().addLayout(grid) self.__sel_method_buttons = QButtonGroup() for method, label in enumerate(SelectionMethods.ITEMS): button = QRadioButton(label) button.setChecked(method == self.sel_method) grid.addWidget(button, method, 0) self.__sel_method_buttons.addButton(button, method) self.__sel_method_buttons.buttonClicked[int].connect( self._set_selection_method) spin = gui.spin(box, self, "n_selected", 1, 999, addToLayout=False, callback=lambda: self._set_selection_method( SelectionMethods.N_BEST)) grid.addWidget(spin, 3, 1) gui.rubber(self.controlArea) gui.auto_send(self.buttonsArea, self, "auto_apply") self.__filter_line_edit = QLineEdit( textChanged=self.__on_filter_changed, placeholderText="Filter...") self.mainArea.layout().addWidget(self.__filter_line_edit) def select_manual(): self._set_selection_method(SelectionMethods.MANUAL) self.view = KeywordsTableView() self.view.pressedAny.connect(select_manual) self.view.horizontalHeader().setSortIndicator(*self.DEFAULT_SORTING) self.view.horizontalHeader().sectionClicked.connect( self.__on_horizontal_header_clicked) self.mainArea.layout().addWidget(self.view) proxy = SortFilterProxyModel() proxy.setFilterKeyColumn(0) proxy.setFilterCaseSensitivity(False) self.view.setModel(proxy) self.view.model().setSourceModel(self.model) self.view.selectionModel().selectionChanged.connect( self.__on_selection_changed) def __on_scoring_method_state_changed(self, state: int, method_name: str): if state == Qt.Checked: self.selected_scoring_methods.add(method_name) elif method_name in self.selected_scoring_methods: self.selected_scoring_methods.remove(method_name) self.update_scores() def __on_yake_lang_changed(self): if ScoringMethods.YAKE in self.selected_scoring_methods: if ScoringMethods.YAKE in self.__cached_keywords: del self.__cached_keywords[ScoringMethods.YAKE] self.update_scores() def __on_rake_lang_changed(self): if ScoringMethods.RAKE in self.selected_scoring_methods: if ScoringMethods.RAKE in self.__cached_keywords: del self.__cached_keywords[ScoringMethods.RAKE] self.update_scores() def __on_filter_changed(self): model = self.view.model() model.setFilterFixedString(self.__filter_line_edit.text().strip()) self._select_rows() def __on_horizontal_header_clicked(self, index: int): header = self.view.horizontalHeader() self.sort_column_order = (index, header.sortIndicatorOrder()) self._select_rows() # explicitly call commit, because __on_selection_changed will not be # invoked, since selection is actually the same, only order is not if self.sel_method == SelectionMethods.MANUAL and self.selected_words \ or self.sel_method == SelectionMethods.ALL: self.commit() def __on_selection_changed(self): selected_rows = self.view.selectionModel().selectedRows(0) model = self.view.model() self.selected_words = [ model.data(model.index(i.row(), 0)) for i in selected_rows ] self.commit() @Inputs.corpus def set_corpus(self, corpus: Optional[Corpus]): self.closeContext() self._clear() self.corpus = corpus self.openContext(self.corpus) self.__sel_method_buttons.button(self.sel_method).setChecked(True) def _clear(self): self.clear_messages() self.cancel() self.selected_words = [] self.model.clear() self.__cached_keywords = {} @Inputs.words def set_words(self, words: Optional[Table]): self.words = None self.Warning.no_words_column.clear() if words: if WORDS_COLUMN_NAME in words.domain and words.domain[ WORDS_COLUMN_NAME].attributes.get("type") == "words": self.words = list(words.get_column_view(WORDS_COLUMN_NAME)[0]) else: self.Warning.no_words_column() def handleNewSignals(self): self.update_scores() def update_scores(self): kwargs = { ScoringMethods.YAKE: { "language": YAKE_LANGUAGES[self.yake_lang_index], "max_len": self.corpus.ngram_range[1] if self.corpus else 1 }, ScoringMethods.RAKE: { "language": RAKE_LANGUAGES[self.rake_lang_index], "max_len": self.corpus.ngram_range[1] if self.corpus else 1 }, } self.start(run, self.corpus, self.words, self.__cached_keywords, self.selected_scoring_methods, kwargs, self.agg_method) def _set_selection_method(self, method: int): self.sel_method = method self.__sel_method_buttons.button(method).setChecked(True) self._select_rows() def _select_rows(self): model = self.view.model() n_rows, n_columns = model.rowCount(), model.columnCount() if self.sel_method == SelectionMethods.NONE: selection = QItemSelection() elif self.sel_method == SelectionMethods.ALL: selection = QItemSelection(model.index(0, 0), model.index(n_rows - 1, n_columns - 1)) elif self.sel_method == SelectionMethods.MANUAL: selection = QItemSelection() for i in range(n_rows): word = model.data(model.index(i, 0)) if word in self.selected_words: _selection = QItemSelection(model.index(i, 0), model.index(i, n_columns - 1)) selection.merge(_selection, QItemSelectionModel.Select) elif self.sel_method == SelectionMethods.N_BEST: n_sel = min(self.n_selected, n_rows) selection = QItemSelection(model.index(0, 0), model.index(n_sel - 1, n_columns - 1)) else: raise NotImplementedError self.view.selectionModel().select(selection, QItemSelectionModel.ClearAndSelect) def on_exception(self, ex: Exception): raise ex def on_partial_result(self, _: Any): pass # pylint: disable=arguments-differ def on_done(self, results: Results): self.__cached_keywords = results.all_keywords self.model.wrap(results.scores) self.model.setHorizontalHeaderLabels(["Word"] + results.labels) self._apply_sorting() if self.model.rowCount() > 0: self._select_rows() else: self.__on_selection_changed() def _apply_sorting(self): if self.model.columnCount() <= self.sort_column_order[0]: self.sort_column_order = self.DEFAULT_SORTING header = self.view.horizontalHeader() current_sorting = (header.sortIndicatorSection(), header.sortIndicatorOrder()) if current_sorting != self.sort_column_order: header.setSortIndicator(*self.sort_column_order) # needed to sort nans; 1. column has strings # if self.sort_column_order[0] > 0: # self.model.sort(*self.sort_column_order) def onDeleteWidget(self): self.shutdown() super().onDeleteWidget() def commit(self): words = None if self.selected_words: words_var = StringVariable(WORDS_COLUMN_NAME) words_var.attributes = {"type": "words"} model = self.model attrs = [ ContinuousVariable(model.headerData(i + 1, Qt.Horizontal)) for i in range(len(self.selected_scoring_methods)) ] domain = Domain(attrs, metas=[words_var]) sort_column, reverse = self.sort_column_order data = sorted(model, key=lambda a: a[sort_column], reverse=reverse) data = [s[1:] + s[:1] for s in data if s[0] in self.selected_words] words = Table.from_list(domain, data) words.name = "Words" self.Outputs.words.send(words) def send_report(self): if not self.corpus: return self.report_data("Corpus", self.corpus) if self.words is not None: self.report_paragraph("Words", ", ".join(self.words)) self.report_table("Keywords", self.view, num_format="{:.3f}")
class OWRank(OWWidget): name = "Rank" description = "Rank and filter data features by their relevance." icon = "icons/Rank.svg" priority = 1102 keywords = [] buttons_area_orientation = Qt.Vertical class Inputs: data = Input("Data", Table) scorer = Input("Scorer", score.Scorer, multiple=True) class Outputs: reduced_data = Output("Reduced Data", Table, default=True) scores = Output("Scores", Table) features = Output("Features", AttributeList, dynamic=False) SelectNone, SelectAll, SelectManual, SelectNBest = range(4) nSelected = ContextSetting(5) auto_apply = Setting(True) sorting = Setting((0, Qt.DescendingOrder)) selected_methods = Setting(set()) settings_version = 2 settingsHandler = DomainContextHandler() selected_rows = ContextSetting([]) selectionMethod = ContextSetting(SelectNBest) class Information(OWWidget.Information): no_target_var = Msg("Data does not have a single target variable. " "You can still connect in unsupervised scorers " "such as PCA.") missings_imputed = Msg('Missing values will be imputed as needed.') class Error(OWWidget.Error): invalid_type = Msg("Cannot handle target variable type {}") inadequate_learner = Msg("Scorer {} inadequate: {}") no_attributes = Msg("Data does not have a single attribute.") def __init__(self): super().__init__() self.scorers = OrderedDict() self.out_domain_desc = None self.data = None self.problem_type_mode = ProblemType.CLASSIFICATION if not self.selected_methods: self.selected_methods = {method.name for method in SCORES if method.is_default} # GUI self.ranksModel = model = TableModel(parent=self) # type: TableModel self.ranksView = view = TableView(self) # type: TableView self.mainArea.layout().addWidget(view) view.setModel(model) view.setColumnWidth(0, 30) view.selectionModel().selectionChanged.connect(self.on_select) def _set_select_manual(): self.setSelectionMethod(OWRank.SelectManual) view.pressed.connect(_set_select_manual) view.verticalHeader().sectionClicked.connect(_set_select_manual) view.horizontalHeader().sectionClicked.connect(self.headerClick) self.measuresStack = stacked = QStackedWidget(self) self.controlArea.layout().addWidget(stacked) for scoring_methods in (CLS_SCORES, REG_SCORES, []): box = gui.vBox(None, "Scoring Methods" if scoring_methods else None) stacked.addWidget(box) for method in scoring_methods: box.layout().addWidget(QCheckBox( method.name, self, objectName=method.shortname, # To be easily found in tests checked=method.name in self.selected_methods, stateChanged=partial(self.methodSelectionChanged, method_name=method.name))) gui.rubber(box) gui.rubber(self.controlArea) self.switchProblemType(ProblemType.CLASSIFICATION) selMethBox = gui.vBox(self.controlArea, "Select Attributes", addSpace=True) grid = QGridLayout() grid.setContentsMargins(6, 0, 6, 0) self.selectButtons = QButtonGroup() self.selectButtons.buttonClicked[int].connect(self.setSelectionMethod) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.selectButtons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr("None"), OWRank.SelectNone) b2 = button(self.tr("All"), OWRank.SelectAll) b3 = button(self.tr("Manual"), OWRank.SelectManual) b4 = button(self.tr("Best ranked:"), OWRank.SelectNBest) s = gui.spin(selMethBox, self, "nSelected", 1, 100, callback=lambda: self.setSelectionMethod(OWRank.SelectNBest)) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(b4, 3, 0) grid.addWidget(s, 3, 1) self.selectButtons.button(self.selectionMethod).setChecked(True) selMethBox.layout().addLayout(grid) gui.auto_commit(selMethBox, self, "auto_apply", "Send", box=False) self.resize(690, 500) def switchProblemType(self, index): """ Switch between discrete/continuous/no_class mode """ self.measuresStack.setCurrentIndex(index) self.problem_type_mode = index @Inputs.data @check_sql_input def set_data(self, data): self.closeContext() self.selected_rows = [] self.ranksModel.clear() self.ranksModel.resetSorting(True) self.get_method_scores.cache_clear() self.get_scorer_scores.cache_clear() self.Error.clear() self.Information.clear() self.Information.missings_imputed( shown=data is not None and data.has_missing()) if data is not None and not len(data.domain.attributes): data = None self.Error.no_attributes() self.data = data self.switchProblemType(ProblemType.CLASSIFICATION) if self.data is not None: domain = self.data.domain if domain.has_discrete_class: problem_type = ProblemType.CLASSIFICATION elif domain.has_continuous_class: problem_type = ProblemType.REGRESSION elif not domain.class_var: self.Information.no_target_var() problem_type = ProblemType.UNSUPERVISED else: # This can happen? self.Error.invalid_type(type(domain.class_var).__name__) problem_type = None if problem_type is not None: self.switchProblemType(problem_type) self.ranksModel.setVerticalHeaderLabels(domain.attributes) self.ranksView.setVHeaderFixedWidthFromLabel( max((a.name for a in domain.attributes), key=len)) self.selectionMethod = OWRank.SelectNBest self.openContext(data) self.selectButtons.button(self.selectionMethod).setChecked(True) def handleNewSignals(self): self.setStatusMessage('Running') self.updateScores() self.setStatusMessage('') self.on_select() @Inputs.scorer def set_learner(self, scorer, id): if scorer is None: self.scorers.pop(id, None) else: # Avoid caching a (possibly stale) previous instance of the same # Scorer passed via the same signal if id in self.scorers: self.get_scorer_scores.cache_clear() self.scorers[id] = ScoreMeta(scorer.name, scorer.name, scorer, ProblemType.from_variable(scorer.class_type), False) @memoize_method() def get_method_scores(self, method): estimator = method.scorer() data = self.data try: scores = np.asarray(estimator(data)) except ValueError: log.warning("Scorer %s wasn't able to compute all scores at once", method.name) try: scores = np.array([estimator(data, attr) for attr in data.domain.attributes]) except ValueError: log.error( "Scorer %s wasn't able to compute scores at all", method.name) scores = np.full(len(data.domain.attributes), np.nan) return scores @memoize_method() def get_scorer_scores(self, scorer): try: scores = scorer.scorer.score_data(self.data).T except ValueError: log.error( "Scorer %s wasn't able to compute scores at all", scorer.name) scores = np.full((len(self.data.domain.attributes), 1), np.nan) labels = ((scorer.shortname,) if scores.shape[1] == 1 else tuple(scorer.shortname + '_' + str(i) for i in range(1, 1 + scores.shape[1]))) return scores, labels def updateScores(self): if self.data is None: self.ranksModel.clear() self.Outputs.scores.send(None) return methods = [method for method in SCORES if (method.name in self.selected_methods and method.problem_type == self.problem_type_mode and (not issparse(self.data.X) or method.scorer.supports_sparse_data))] scorers = [] self.Error.inadequate_learner.clear() for scorer in self.scorers.values(): if scorer.problem_type in (self.problem_type_mode, ProblemType.UNSUPERVISED): scorers.append(scorer) else: self.Error.inadequate_learner(scorer.name, scorer.learner_adequacy_err_msg) method_scores = tuple(self.get_method_scores(method) for method in methods) scorer_scores, scorer_labels = (), () if scorers: scorer_scores, scorer_labels = zip(*(self.get_scorer_scores(scorer) for scorer in scorers)) scorer_labels = tuple(chain.from_iterable(scorer_labels)) labels = tuple(method.shortname for method in methods) + scorer_labels model_array = np.column_stack( ([len(a.values) if a.is_discrete else np.nan for a in self.data.domain.attributes],) + (method_scores if method_scores else ()) + (scorer_scores if scorer_scores else ()) ) for column, values in enumerate(model_array.T): self.ranksModel.setExtremesFrom(column, values) self.ranksModel.wrap(model_array.tolist()) self.ranksModel.setHorizontalHeaderLabels(('#',) + labels) self.ranksView.setColumnWidth(0, 40) # Re-apply sort try: sort_column, sort_order = self.sorting if sort_column < len(labels): # adds 1 for '#' (discrete count) column self.ranksModel.sort(sort_column + 1, sort_order) self.ranksView.horizontalHeader().setSortIndicator(sort_column + 1, sort_order) except ValueError: pass self.autoSelection() self.Outputs.scores.send(self.create_scores_table(labels)) def on_select(self): # Save indices of attributes in the original, unsorted domain self.selected_rows = self.ranksModel.mapToSourceRows([ i.row() for i in self.ranksView.selectionModel().selectedRows(0)]) self.commit() def setSelectionMethod(self, method): self.selectionMethod = method self.selectButtons.button(method).setChecked(True) self.autoSelection() def autoSelection(self): selModel = self.ranksView.selectionModel() model = self.ranksModel rowCount = model.rowCount() columnCount = model.columnCount() if self.selectionMethod == OWRank.SelectNone: selection = QItemSelection() elif self.selectionMethod == OWRank.SelectAll: selection = QItemSelection( model.index(0, 0), model.index(rowCount - 1, columnCount - 1) ) elif self.selectionMethod == OWRank.SelectNBest: nSelected = min(self.nSelected, rowCount) selection = QItemSelection( model.index(0, 0), model.index(nSelected - 1, columnCount - 1) ) else: selection = QItemSelection() if len(self.selected_rows): for row in model.mapFromSourceRows(self.selected_rows): selection.append(QItemSelectionRange( model.index(row, 0), model.index(row, columnCount - 1))) selModel.select(selection, QItemSelectionModel.ClearAndSelect) def headerClick(self, index): if index >= 1 and self.selectionMethod == OWRank.SelectNBest: # Reselect the top ranked attributes self.autoSelection() # Store the header states sort_order = self.ranksModel.sortOrder() sort_column = self.ranksModel.sortColumn() - 1 # -1 for '#' (discrete count) column self.sorting = (sort_column, sort_order) def methodSelectionChanged(self, state, method_name): if state == Qt.Checked: self.selected_methods.add(method_name) elif method_name in self.selected_methods: self.selected_methods.remove(method_name) self.updateScores() def send_report(self): if not self.data: return self.report_domain("Input", self.data.domain) self.report_table("Ranks", self.ranksView, num_format="{:.3f}") if self.out_domain_desc is not None: self.report_items("Output", self.out_domain_desc) def commit(self): selected_attrs = [] if self.data is not None: selected_attrs = [self.data.domain.attributes[i] for i in self.selected_rows] if not selected_attrs: self.Outputs.reduced_data.send(None) self.Outputs.features.send(None) self.out_domain_desc = None else: reduced_domain = Domain( selected_attrs, self.data.domain.class_var, self.data.domain.metas) data = self.data.transform(reduced_domain) self.Outputs.reduced_data.send(data) self.Outputs.features.send(AttributeList(selected_attrs)) self.out_domain_desc = report.describe_domain(data.domain) def create_scores_table(self, labels): model_list = self.ranksModel.tolist() if not model_list or len(model_list[0]) == 1: # Empty or just n_values column return None domain = Domain([ContinuousVariable(label) for label in labels], metas=[StringVariable("Feature")]) # Prevent np.inf scores finfo = np.finfo(np.float64) scores = np.clip(np.array(model_list)[:, 1:], finfo.min, finfo.max) feature_names = np.array([a.name for a in self.data.domain.attributes]) # Reshape to 2d array as Table does not like 1d arrays feature_names = feature_names[:, None] new_table = Table(domain, scores, metas=feature_names) new_table.name = "Feature Scores" return new_table @classmethod def migrate_settings(cls, settings, version): # If older settings, restore sort header to default # Saved selected_rows will likely be incorrect if version is None or version < 2: column, order = 0, Qt.DescendingOrder headerState = settings.pop("headerState", None) # Lacking knowledge of last problemType, use discrete ranks view's ordering if isinstance(headerState, (tuple, list)): headerState = headerState[0] if isinstance(headerState, bytes): hview = QHeaderView(Qt.Horizontal) hview.restoreState(headerState) column, order = hview.sortIndicatorSection() - 1, hview.sortIndicatorOrder() settings["sorting"] = (column, order) @classmethod def migrate_context(cls, context, version): if version is None or version < 2: # Old selection was saved as sorted indices. New selection is original indices. # Since we can't devise the latter without first computing the ranks, # just reset the selection to avoid confusion. context.values['selected_rows'] = []