def test_emits_column_changes_on_row_insert(self): inserted = [] removed = [] model = PyTableModel() model.columnsInserted.connect(inserted.append) model.columnsRemoved.connect(removed.append) inserted = QSignalSpy(model.columnsInserted) removed = QSignalSpy(model.columnsRemoved) model.append([2]) self.assertEqual(list(inserted)[-1][1:], [0, 0]) model.append([2, 3]) self.assertEqual(list(inserted)[-1][1:], [1, 1]) del model[:] self.assertEqual(list(removed)[0][1:], [0, 1]) model.extend([[0, 1], [0, 2]]) self.assertEqual(list(inserted)[-1][1:], [0, 1]) model.clear() self.assertEqual(list(removed)[0][1:], [0, 1]) model[:] = [[1], [2]] self.assertEqual(list(inserted)[-1][1:], [0, 0])
def __init__(self): super().__init__() self.corpus = None # corpus taken from distances self.linkage = None # hierarchical clustering linkage as returned by Orange self.distances = None # DistMatrix on input self.clustering_mask = None # 1D array of clusters for self.corpus self.threshold = 0 # hierarchical clustering distance threshold self.threshold_spin = None # Info self.n_documents = '' self.n_unique = '' self.n_duplicates = '' info_box = gui.widgetBox(self.controlArea, box='Info') gui.label(info_box, self, 'Documents: %(n_documents)s') gui.label(info_box, self, ' ◦ unique: %(n_unique)s') gui.label(info_box, self, ' ◦ duplicates: %(n_duplicates)s') # Threshold Histogram & Cluster View self.histogram = Histogram(self) self.table_view = gui.TableView(selectionMode=QListView.SingleSelection) self.table_model = PyTableModel() self.table_model.setHorizontalHeaderLabels(['Cluster', 'Size']) self.table_view.setModel(self.table_model) self.table_view.selectionModel().selectionChanged.connect(self.send_duplicates) # Add to main area height = 300 main_area = gui.hBox(self.mainArea) self.histogram.setMinimumWidth(500) self.histogram.setMinimumHeight(height) self.table_view.setFixedWidth(140) main_area.layout().addWidget(self.histogram) main_area.layout().addWidget(self.table_view) # Controls gui.comboBox(self.controlArea, self, 'linkage_method', items=self.LINKAGE, box='Linkage', callback=self.recalculate_linkage, orientation=Qt.Horizontal) self.threshold_spin = gui.doubleSpin(self.controlArea, self, 'threshold', 0, float('inf'), 0.01, decimals=2, label='Distance threshold', box='Distances', callback=self.threshold_changed, keyboardTracking=False, controlWidth=60) self.histogram.region.sigRegionChangeFinished.connect(self.threshold_from_histogram_region) self.threshold_spin.setEnabled(False) gui.rubber(self.controlArea) # Output gui.comboBox(self.controlArea, self, "cluster_role", box='Output', label='Append Cluster IDs to:', callback=self.send_corpus, items=self.CLUSTER_ROLES)
def __init__(self): OWWidget.__init__(self) ConcurrentWidgetMixin.__init__(self) self.data = None self.selected_attributes = None box = gui.vBox(self.controlArea, "Granger Test") gui.hSlider( box, self, "confidence", minValue=90, maxValue=99, label="Confidence:", labelFormat=" %d%%", callback=self._setting_changed, ) gui.spin( box, self, "max_lag", 1, 50, label="Max lag:", callback=self._setting_changed, ) self.test_button = gui.button(box, self, "&Test", self._toggle_run) gui.rubber(self.controlArea) self.model = model = PyTableModel(parent=self) model.setHorizontalHeaderLabels(COLUMNS) self.causality_view = view = gui.TableView(self) view.setModel(model) bold = view.BoldFontDelegate(self) view.setItemDelegateForColumn(2, bold) view.setItemDelegateForColumn(4, bold) view.horizontalHeader().setStretchLastSection(False) view.horizontalHeader().sectionClicked.connect(self.header_click) view.selectionModel().selectionChanged.connect(self.on_select) view.sortByColumn(1, Qt.AscendingOrder) self.mainArea.layout().addWidget(view) self._set_modified(False) self.auto_commit_widget = gui.auto_commit( widget=self.controlArea, master=self, value="autocommit", label="Apply", commit=self.commit, )
def _create_layout(self): self._new_webview() box = gui.widgetBox(self.controlArea, 'Info') self.topic_info = gui.label(box, self, '%(n_topic_words)d words in a topic') gui.label(box, self, '%(documents_info_str)s') box = gui.widgetBox(self.controlArea, 'Cloud preferences') gui.checkBox(box, self, 'words_color', 'Color words', callback=self.on_cloud_pref_change) TILT_VALUES = ('no', 'slight', 'more', 'full') gui.valueSlider(box, self, 'words_tilt', label='Words tilt:', values=list(range(len(TILT_VALUES))), callback=self.on_cloud_pref_change, labelFormat=lambda x: TILT_VALUES[x]) gui.button(box, None, 'Regenerate word cloud', callback=self.on_cloud_pref_change) box = gui.widgetBox(self.controlArea, 'Words && weights') class TableView(gui.TableView): def __init__(self, parent): super().__init__(parent) self._parent = parent def selectionChanged(self, selected, deselected): super().selectionChanged(selected, deselected) parent = self._parent for index in deselected.indexes(): data = parent.tablemodel[index.row()][1] self._parent.selected_words.remove(data) for index in selected.indexes(): data = parent.tablemodel[index.row()][1] self._parent.selected_words.add(data) parent.cloud_reselect() view = self.tableview = TableView(self) model = self.tablemodel = PyTableModel() model.setHorizontalHeaderLabels(['Weight', 'Word']) view.setModel(model) box.layout().addWidget(view)
class TestAbstractSortTableModel(TestCase): def setUp(self): assert issubclass(PyTableModel, AbstractSortTableModel) self.model = PyTableModel([[1, 4], [2, 3]]) def test_sorting(self): self.model.sort(1, Qt.AscendingOrder) self.assertSequenceEqual( self.model.mapToSourceRows(...).tolist(), [1, 0]) self.model.sort(1, Qt.DescendingOrder) self.assertSequenceEqual( self.model.mapToSourceRows(...).tolist(), [0, 1])
def __init__(self): self.data = None self._models = OrderedDict() box = gui.vBox(self.controlArea, 'Evaluation Parameters') gui.spin(box, self, 'n_folds', 1, 100, label='Number of folds:', callback=self.on_changed) gui.spin(box, self, 'forecast_steps', 1, 100, label='Forecast steps:', callback=self.on_changed) gui.auto_commit(box, self, 'autocommit', '&Apply') gui.rubber(self.controlArea) self.model = model = PyTableModel(parent=self) view = gui.TableView(self) view.setModel(model) view.horizontalHeader().setStretchLastSection(False) view.verticalHeader().setVisible(True) self.mainArea.layout().addWidget(view)
def __init__(self): self.data = None box = gui.vBox(self.controlArea, 'Granger Test') gui.hSlider(box, self, 'confidence', minValue=90, maxValue=99, label='Confidence:', labelFormat=" %d%%", callback=self.on_changed) gui.spin(box, self, 'max_lag', 1, 50, label='Max lag:', callback=self.on_changed) gui.auto_commit(box, self, 'autocommit', '&Test') gui.rubber(self.controlArea) self.model = model = PyTableModel(parent=self) model.setHorizontalHeaderLabels(['Min. lag', 'Series 1', '', 'Series 2']) view = gui.TableView(self) view.setModel(model) bold = view.BoldFontDelegate(self) view.setItemDelegateForColumn(1, bold) view.setItemDelegateForColumn(3, bold) view.horizontalHeader().setStretchLastSection(False) self.mainArea.layout().addWidget(view)
def _setup_gui(self): # Control area box = gui.hBox(self.controlArea, "Filtering") gui.doubleSpin(box, self, "threshold", 0, 1, 0.01, None, label="Threshold: ", orientation=Qt.Horizontal, callback=self.__on_threshold_changed) box = gui.hBox(self.controlArea, "Display") gui.radioButtons(box, self, "display_index", DisplayDocument.ITEMS, callback=self.__on_display_changed) gui.rubber(self.controlArea) # Main area model = PyTableModel(parent=self) self._list_view = SemanticListView() self._list_view.setModel(model) self._list_view.selectionModel().selectionChanged.connect( self.__on_selection_changed) self._list_view.horizontalHeader().sectionClicked.connect( self.__on_horizontal_header_clicked) splitter = QSplitter() splitter.addWidget(self._list_view) self._web_view = gui.WebviewWidget(splitter, debug=False) splitter.setSizes([200, 300]) self.mainArea.layout().addWidget(splitter)
def test_sorting(self): assert issubclass(PyTableModel, AbstractSortTableModel) model = PyTableModel([[1, 4], [2, 2], [3, 3]]) model.sort(1, Qt.AscendingOrder) # mapToSourceRows self.assertSequenceEqual(model.mapToSourceRows(...).tolist(), [1, 2, 0]) self.assertEqual(model.mapToSourceRows(1).tolist(), 2) self.assertSequenceEqual(model.mapToSourceRows([1, 2]).tolist(), [2, 0]) self.assertSequenceEqual(model.mapToSourceRows([]), []) self.assertSequenceEqual(model.mapToSourceRows(np.array([], dtype=int)).tolist(), []) self.assertRaises(IndexError, model.mapToSourceRows, np.r_[0.]) # mapFromSourceRows self.assertSequenceEqual(model.mapFromSourceRows(...).tolist(), [2, 0, 1]) self.assertEqual(model.mapFromSourceRows(1).tolist(), 0) self.assertSequenceEqual(model.mapFromSourceRows([1, 2]).tolist(), [0, 1]) self.assertSequenceEqual(model.mapFromSourceRows([]), []) self.assertSequenceEqual(model.mapFromSourceRows(np.array([], dtype=int)).tolist(), []) self.assertRaises(IndexError, model.mapFromSourceRows, np.r_[0.]) model.sort(1, Qt.DescendingOrder) self.assertSequenceEqual(model.mapToSourceRows(...).tolist(), [0, 2, 1]) self.assertSequenceEqual(model.mapFromSourceRows(...).tolist(), [0, 2, 1])
class TestPyTableModel(TestCase): def setUp(self): self.model = PyTableModel([[1, 4], [2, 3]]) def test_init(self): self.model = PyTableModel() self.assertEqual(self.model.rowCount(), 0) def test_rowCount(self): self.assertEqual(self.model.rowCount(), 2) self.assertEqual(len(self.model), 2) def test_columnCount(self): self.assertEqual(self.model.columnCount(), 2) def test_data(self): self.assertEqual(str(self.model.data(self.model.index(0, 0))), '1') def test_sort(self): self.model.sort(1) self.assertEqual(self.model[0][0], 2) def test_setHeaderLabels(self): self.model.setHorizontalHeaderLabels(['Col 1', 'Col 2']) self.assertEqual(self.model.headerData(1, Qt.Horizontal), 'Col 2') self.assertEqual(self.model.headerData(1, Qt.Vertical), '1') def test_removeRows(self): self.model.removeRows(0, 1) self.assertEqual(len(self.model), 1) self.assertEqual(self.model[0][1], 3) def test_removeColumns(self): self.model.removeColumns(0, 1) self.assertEqual(self.model.columnCount(), 1) self.assertEqual(self.model[1][0], 3) def test_insertRows(self): self.model.insertRows(0, 1) self.assertEqual(self.model[1][0], 1) def test_insertColumns(self): self.model.insertColumns(0, 1) self.assertEqual(self.model[0], ['', 1, 4]) def test_wrap(self): self.model.wrap([[0]]) self.assertEqual(self.model.rowCount(), 1) self.assertEqual(self.model.columnCount(), 1) def test_clear(self): self.model.clear() self.assertEqual(self.model.rowCount(), 0) def test_append(self): self.model.append([5, 6]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_extend(self): self.model.extend([[5, 6]]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_insert(self): self.model.insert(0, [5, 6]) self.assertEqual(self.model[0][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_remove(self): self.model.remove([2, 3]) self.assertEqual(self.model.rowCount(), 1)
def test_sorting(self): assert issubclass(PyTableModel, AbstractSortTableModel) model = PyTableModel([[1, 4], [2, 2], [3, 3]]) model.sort(1, Qt.AscendingOrder) # mapToSourceRows self.assertSequenceEqual( model.mapToSourceRows(...).tolist(), [1, 2, 0]) self.assertEqual(model.mapToSourceRows(1).tolist(), 2) self.assertSequenceEqual( model.mapToSourceRows([1, 2]).tolist(), [2, 0]) self.assertSequenceEqual(model.mapToSourceRows([]), []) self.assertSequenceEqual( model.mapToSourceRows(np.array([], dtype=int)).tolist(), []) self.assertRaises(IndexError, model.mapToSourceRows, np.r_[0.]) # mapFromSourceRows self.assertSequenceEqual( model.mapFromSourceRows(...).tolist(), [2, 0, 1]) self.assertEqual(model.mapFromSourceRows(1).tolist(), 0) self.assertSequenceEqual( model.mapFromSourceRows([1, 2]).tolist(), [0, 1]) self.assertSequenceEqual(model.mapFromSourceRows([]), []) self.assertSequenceEqual( model.mapFromSourceRows(np.array([], dtype=int)).tolist(), []) self.assertRaises(IndexError, model.mapFromSourceRows, np.r_[0.]) model.sort(1, Qt.DescendingOrder) self.assertSequenceEqual( model.mapToSourceRows(...).tolist(), [0, 2, 1]) self.assertSequenceEqual( model.mapFromSourceRows(...).tolist(), [0, 2, 1])
class OWDuplicates(widget.OWWidget): name = 'Duplicate Detection' description = 'Detect & remove duplicates from a corpus.' icon = 'icons/Duplicates.svg' priority = 45 inputs = [(IO.distances, DistMatrix, 'set_distances')] outputs = [ (IO.corpus_without_duplicates, Corpus), (IO.duplicates, Corpus), (IO.corpus, Corpus), ] resizing_enabled = True class Error(OWWidget.Error): dist_matrix_invalid_shape = Msg('Duplicate detection only supports ' 'distances calculated between rows.') too_little_documents = Msg('More than one document is required.') LINKAGE = ['Single', 'Average', 'Complete', 'Weighted', 'Ward'] linkage_method = settings.Setting(1) # Cluster variable domain role AttributeRole, ClassRole, MetaRole = 0, 1, 2 CLUSTER_ROLES = ["Attributes", "Class", "Metas"] cluster_role = settings.Setting(2) def __init__(self): super().__init__() self.corpus = None # corpus taken from distances self.linkage = None # hierarchical clustering linkage as returned by Orange self.distances = None # DistMatrix on input self.clustering_mask = None # 1D array of clusters for self.corpus self.threshold = 0 # hierarchical clustering distance threshold self.threshold_spin = None # Info self.n_documents = '' self.n_unique = '' self.n_duplicates = '' info_box = gui.widgetBox(self.controlArea, box='Info') gui.label(info_box, self, 'Documents: %(n_documents)s') gui.label(info_box, self, ' ◦ unique: %(n_unique)s') gui.label(info_box, self, ' ◦ duplicates: %(n_duplicates)s') # Threshold Histogram & Cluster View self.histogram = Histogram(self) self.table_view = gui.TableView(selectionMode=QListView.SingleSelection) self.table_model = PyTableModel() self.table_model.setHorizontalHeaderLabels(['Cluster', 'Size']) self.table_view.setModel(self.table_model) self.table_view.selectionModel().selectionChanged.connect(self.send_duplicates) # Add to main area height = 300 main_area = gui.hBox(self.mainArea) self.histogram.setMinimumWidth(500) self.histogram.setMinimumHeight(height) self.table_view.setFixedWidth(140) main_area.layout().addWidget(self.histogram) main_area.layout().addWidget(self.table_view) # Controls gui.comboBox(self.controlArea, self, 'linkage_method', items=self.LINKAGE, box='Linkage', callback=self.recalculate_linkage, orientation=Qt.Horizontal) self.threshold_spin = gui.doubleSpin(self.controlArea, self, 'threshold', 0, float('inf'), 0.01, decimals=2, label='Distance threshold', box='Distances', callback=self.threshold_changed, keyboardTracking=False, controlWidth=60) self.histogram.region.sigRegionChangeFinished.connect(self.threshold_from_histogram_region) self.threshold_spin.setEnabled(False) gui.rubber(self.controlArea) # Output gui.comboBox(self.controlArea, self, "cluster_role", box='Output', label='Append Cluster IDs to:', callback=self.send_corpus, items=self.CLUSTER_ROLES) def reset(self): self.corpus = None self.linkage = None self.distances = None self.clustering_mask = None self.n_documents = '' self.n_unique = '' self.n_duplicates = '' self.threshold = 0 self.threshold_spin.setEnabled(False) self.table_model.clear() self.histogram.setValues([]) def set_distances(self, distances): self.Error.clear() self.distances = distances if distances is None: self.reset() return self.corpus = self.distances.row_items self.n_documents = len(self.corpus) if self.n_documents < 2: self.Error.too_little_documents() self.reset() return if distances.shape != (self.n_documents, self.n_documents): self.Error.dist_matrix_invalid_shape() self.reset() return self.threshold_spin.setEnabled(True) self.recalculate_linkage() def threshold_from_histogram_region(self): _, self.threshold = self.histogram.getRegion() self.threshold_changed() def threshold_changed(self): self.threshold = np.clip(self.threshold, *self.histogram.boundary()) self.histogram.setRegion(0, self.threshold) self.detect_duplicates() def recalculate_linkage(self): if self.distances is not None: self.linkage = dist_matrix_linkage(self.distances, self.LINKAGE[self.linkage_method].lower()) # Magnitude of the spinbox's step is data-dependent vals = sorted(self.linkage[:, 2]) low, up = vals[0], vals[-1] step = (up - low) / 20 self.threshold_spin.setSingleStep(step) self.threshold = np.clip(self.threshold, low, up) self.histogram.setValues([]) # without this range breaks when changing linkages self.histogram.setValues(vals) self.histogram.setRegion(0, self.threshold) self.detect_duplicates() def detect_duplicates(self): if self.distances is not None: self.cluster_linkage() self.send_corpus() self.send_corpus_without_duplicates() self.fill_cluster_view() def cluster_linkage(self): # cluster documents n = int(self.n_documents) clusters = {j: [j] for j in range(n)} for i, (c1, c2, dist, size) in enumerate(self.linkage): if dist > self.threshold: break clusters[n + i] = clusters[c1] + clusters[c2] del clusters[c1] del clusters[c2] self.n_unique = len(clusters) self.n_duplicates = n - self.n_unique # create mask self.clustering_mask = np.empty(n, dtype=int) for i, c in enumerate(clusters.values()): self.clustering_mask[c] = i def fill_cluster_view(self): self.table_model.clear() c = Counter(self.clustering_mask) for id_, count in c.items(): self.table_model.append([Cluster(id_), count]) self.table_view.sortByColumn(1, Qt.DescendingOrder) self.table_view.selectRow(0) def send_corpus(self): if self.clustering_mask is not None: cluster_var = DiscreteVariable( 'Duplicates Cluster', values=[str(Cluster(v)) for v in set(self.clustering_mask.flatten())] ) corpus, domain = self.corpus, self.corpus.domain attrs = domain.attributes class_ = domain.class_vars metas = domain.metas if self.cluster_role == self.AttributeRole: attrs = attrs + (cluster_var,) elif self.cluster_role == self.ClassRole: class_ = class_ + (cluster_var,) elif self.cluster_role == self.MetaRole: metas = metas + (cluster_var,) domain = Domain(attrs, class_, metas) corpus = corpus.from_table(domain, corpus) corpus.get_column_view(cluster_var)[0][:] = self.clustering_mask self.send(IO.corpus, corpus) else: self.send(IO.corpus, None) def send_corpus_without_duplicates(self): if self.clustering_mask is not None: # TODO make this more general, currently we just take the first document mask = [np.where(self.clustering_mask == i)[0][0] for i in set(self.clustering_mask)] c = self.corpus[mask] c.name = '{} (Without Duplicates)'.format(self.corpus.name) self.send(IO.corpus_without_duplicates, c) else: self.send(IO.corpus_without_duplicates, None) def send_duplicates(self): index = self.table_view.selectionModel().currentIndex().row() cluster = self.table_model[index][0] mask = np.flatnonzero(self.clustering_mask == cluster.id) c = self.corpus[mask] c.name = '{} {}'.format(IO.duplicates, cluster) self.send(IO.duplicates, c) def send_report(self): self.report_items([ ('Linkage', self.LINKAGE[self.linkage_method]), ('Distance threshold', '{:.2f}'.format(self.threshold)), ('Documents', self.n_documents), ('Unique', self.n_unique), ('Duplicates', self.n_duplicates), ])
def __init__(self): self.data = None box = gui.vBox(self.controlArea, 'Moving Transform') def _disable_fixed_wlen(): fixed_wlen.setDisabled(not self.non_overlapping) self.view.repaint() self.on_changed() gui.checkBox(box, self, 'non_overlapping', label=self._NON_OVERLAPPING_WINDOWS, callback=_disable_fixed_wlen, tooltip='If this is checked, instead of rolling windows ' 'through the series, they are applied side-to-side, ' 'so the resulting output series will be some ' 'length-of-fixed-window-times shorter.') fixed_wlen = gui.spin(box, self, 'fixed_wlen', 2, 1000, label='Fixed window width:', callback=self.on_changed) fixed_wlen.setDisabled(not self.non_overlapping) # TODO: allow the user to choose left-aligned, right-aligned, or center-aligned window class TableView(gui.TableView): def __init__(self, parent): super().__init__(parent, editTriggers=(self.SelectedClicked | self.CurrentChanged | self.DoubleClicked | self.EditKeyPressed), ) self.horizontalHeader().setStretchLastSection(False) agg_functions = ListModel(AGG_FUNCTIONS + [Cumulative_sum, Cumulative_product], parent=self) self.setItemDelegateForColumn(0, self.VariableDelegate(parent)) self.setItemDelegateForColumn(1, self.SpinDelegate(parent)) self.setItemDelegateForColumn(2, self.ComboDelegate(self, agg_functions)) class _ItemDelegate(QStyledItemDelegate): def updateEditorGeometry(self, widget, option, _index): widget.setGeometry(option.rect) class ComboDelegate(_ItemDelegate): def __init__(self, parent=None, combo_model=None): super().__init__(parent) self._parent = parent if combo_model is not None: self._combo_model = combo_model def createEditor(self, parent, _QStyleOptionViewItem, index): combo = QComboBox(parent) combo.setModel(self._combo_model) return combo def setEditorData(self, combo, index): var = index.model().data(index, Qt.EditRole) combo.setCurrentIndex(self._combo_model.indexOf(var)) def setModelData(self, combo, model, index): var = self._combo_model[combo.currentIndex()] model.setData(index, var, Qt.EditRole) class VariableDelegate(ComboDelegate): @property def _combo_model(self): return self._parent.var_model class SpinDelegate(_ItemDelegate): def paint(self, painter, option, index): # Don't paint window length if non-overlapping windows set if not self.parent().non_overlapping: super().paint(painter, option, index) def createEditor(self, parent, _QStyleOptionViewItem, _index): # Don't edit window length if non-overlapping windows set if self.parent().non_overlapping: return None spin = QSpinBox(parent, minimum=1, maximum=1000) return spin def setEditorData(self, spin, index): spin.setValue(index.model().data(index, Qt.EditRole)) def setModelData(self, spin, model, index): spin.interpretText() model.setData(index, spin.value(), Qt.EditRole) self.var_model = VariableListModel(parent=self) self.table_model = model = PyTableModel(self.transformations, parent=self, editable=True) model.setHorizontalHeaderLabels(['Series', 'Window width', 'Aggregation function']) model.dataChanged.connect(self.on_changed) self.view = view = TableView(self) view.setModel(model) box.layout().addWidget(view) hbox = gui.hBox(box) from os.path import dirname, join self.add_button = button = gui.button( hbox, self, 'Add &Transform', callback=self.on_add_transform) button.setIcon(QIcon(join(dirname(__file__), 'icons', 'LineChart-plus.png'))) self.del_button = button = gui.button( hbox, self, '&Delete Selected', callback=self.on_del_transform) QIcon.setThemeName('gnome') # Works for me button.setIcon(QIcon.fromTheme('edit-delete')) gui.auto_commit(box, self, 'autocommit', '&Apply')
def test_init(self): self.model = PyTableModel() self.assertEqual(self.model.rowCount(), 0)
def __init__(self): OWWidget.__init__(self) ConcurrentWidgetMixin.__init__(self) self.data: Optional[Table] = None self.attr_name_to_variable: Optional[Table] = None self.covariates_from_worker_result = None self.time_var: Optional[str] = None self.event_var: Optional[str] = None gui.rubber(self.controlArea) sel_method_box = gui.vBox(self.buttonsArea, 'Select Attributes') grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) grid.setSpacing(6) self.select_buttons = QButtonGroup() self.select_buttons.buttonClicked[int].connect( self.set_selection_method) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.select_buttons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr('None'), OWRankSurvivalFeatures.select_none) b2 = button(self.tr('Manual'), OWRankSurvivalFeatures.manual_selection) b3 = button(self.tr('Best ranked:'), OWRankSurvivalFeatures.select_n_best) s = gui.spin( sel_method_box, self, 'n_selected', 1, 999, callback=lambda: self.set_selection_method(OWRankSurvivalFeatures. select_n_best), addToLayout=False, ) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(s, 2, 1) sel_method_box.layout().addLayout(grid) self.commit_button = gui.auto_commit(self.buttonsArea, self, 'auto_commit', '&Commit', box=False) # Main area self.model = PyTableModel(parent=self) self.table_view = TableView(parent=self) self.table_view.setModel(self.model) self.model.setHorizontalHeaderLabels([ 'Log-Likelihood', 'Log-Likelihood Ratio', f'{"p".center(13)}', 'FDR' ]) self.table_view.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.table_view.selectionModel().selectionChanged.connect( self.on_select) def _set_select_manual(): self.set_selection_method(OWRankSurvivalFeatures.manual_selection) self.table_view.manualSelection.connect(_set_select_manual) self.table_view.verticalHeader().sectionClicked.connect( _set_select_manual) self.mainArea.layout().addWidget(self.table_view)
def test_report_table(self): rep = OWReport.get_instance() model = PyTableModel([['x', 1, 2], ['y', 2, 2]]) model.setHorizontalHeaderLabels(['a', 'b', 'c']) model.setData(model.index(0, 0), Qt.AlignHCenter | Qt.AlignTop, Qt.TextAlignmentRole) model.setData(model.index(1, 0), QFont('', -1, QFont.Bold), Qt.FontRole) model.setData(model.index(1, 2), QBrush(Qt.red), Qt.BackgroundRole) view = gui.TableView() view.show() view.setModel(model) rep.report_table('Name', view) self.maxDiff = None self.assertEqual( rep.report_html, '<h2>Name</h2><table>\n' '<tr>' '<th style="color:black;border:0;background:transparent;' 'text-align:left;vertical-align:middle;">a</th>' '<th style="color:black;border:0;background:transparent;' 'text-align:left;vertical-align:middle;">b</th>' '<th style="color:black;border:0;background:transparent;' 'text-align:left;vertical-align:middle;">c</th>' '</tr>' '<tr>' '<td style="color:black;border:0;background:transparent;' 'text-align:center;vertical-align:top;">x</td>' '<td style="color:black;border:0;background:transparent;' 'text-align:right;vertical-align:middle;">1</td>' '<td style="color:black;border:0;background:transparent;' 'text-align:right;vertical-align:middle;">2</td>' '</tr>' '<tr>' '<td style="color:black;border:0;background:transparent;' 'font-weight: bold;text-align:left;vertical-align:middle;">y</td>' '<td style="color:black;border:0;background:transparent;' 'text-align:right;vertical-align:middle;">2</td>' '<td style="color:black;border:0;background:#ff0000;' 'text-align:right;vertical-align:middle;">2</td>' '</tr></table>')
def test_editable(self): editable_model = PyTableModel([[0]], editable=True) self.assertFalse(int(self.model.flags(self.model.index(0, 0)) & Qt.ItemIsEditable)) self.assertTrue(int(editable_model.flags(editable_model.index(0, 0)) & Qt.ItemIsEditable))
class OWRankSurvivalFeatures(OWWidget, ConcurrentWidgetMixin): name = 'Rank Survival Features' # TODO: Add widget metadata description = '' icon = 'icons/owranksurvivalfeatures.svg' priority = 30 keywords = [] buttons_area_orientation = Qt.Vertical select_none, manual_selection, select_n_best = range(3) settingsHandler = DomainContextHandler() selected_attrs = ContextSetting([], schema_only=True) selection_method = Setting(select_n_best, schema_only=True) n_selected = Setting(20, schema_only=True) auto_commit: bool = Setting(False, schema_only=True) class Inputs: data = Input('Data', Table) class Outputs: reduced_data = Output('Reduced Data', Table, default=True) def __init__(self): OWWidget.__init__(self) ConcurrentWidgetMixin.__init__(self) self.data: Optional[Table] = None self.attr_name_to_variable: Optional[Table] = None self.covariates_from_worker_result = None self.time_var: Optional[str] = None self.event_var: Optional[str] = None gui.rubber(self.controlArea) sel_method_box = gui.vBox(self.buttonsArea, 'Select Attributes') grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) grid.setSpacing(6) self.select_buttons = QButtonGroup() self.select_buttons.buttonClicked[int].connect( self.set_selection_method) def button(text, buttonid, toolTip=None): b = QRadioButton(text) self.select_buttons.addButton(b, buttonid) if toolTip is not None: b.setToolTip(toolTip) return b b1 = button(self.tr('None'), OWRankSurvivalFeatures.select_none) b2 = button(self.tr('Manual'), OWRankSurvivalFeatures.manual_selection) b3 = button(self.tr('Best ranked:'), OWRankSurvivalFeatures.select_n_best) s = gui.spin( sel_method_box, self, 'n_selected', 1, 999, callback=lambda: self.set_selection_method(OWRankSurvivalFeatures. select_n_best), addToLayout=False, ) grid.addWidget(b1, 0, 0) grid.addWidget(b2, 1, 0) grid.addWidget(b3, 2, 0) grid.addWidget(s, 2, 1) sel_method_box.layout().addLayout(grid) self.commit_button = gui.auto_commit(self.buttonsArea, self, 'auto_commit', '&Commit', box=False) # Main area self.model = PyTableModel(parent=self) self.table_view = TableView(parent=self) self.table_view.setModel(self.model) self.model.setHorizontalHeaderLabels([ 'Log-Likelihood', 'Log-Likelihood Ratio', f'{"p".center(13)}', 'FDR' ]) self.table_view.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.table_view.selectionModel().selectionChanged.connect( self.on_select) def _set_select_manual(): self.set_selection_method(OWRankSurvivalFeatures.manual_selection) self.table_view.manualSelection.connect(_set_select_manual) self.table_view.verticalHeader().sectionClicked.connect( _set_select_manual) self.mainArea.layout().addWidget(self.table_view) @property def covariates(self) -> Optional[List[str]]: if not self.data: return return [attr.name for attr in self.data.domain.attributes] @Inputs.data @check_survival_data def set_data(self, data: Table): self.closeContext() self.selected_attrs = [] self.covariates_from_worker_result = [] self.model.clear() self.model.resetSorting() if not data: return self.data = data self.attr_name_to_variable = { attr.name: attr for attr in self.data.domain.attributes } self.openContext(data) time_var, event_var = get_survival_endpoints(self.data.domain) self.time_var, self.event_var = time_var.name, event_var.name self.start(worker, self.data, self.covariates, self.time_var, self.event_var) def commit(self): if not self.selected_attrs: self.Outputs.reduced_data.send(None) else: reduced_domain = Domain(self.selected_attrs, self.data.domain.class_vars, self.data.domain.metas) data = self.data.transform(reduced_domain) self.Outputs.reduced_data.send(data) def on_done(self, worker_result): covariate_names, results = worker_result # wrap everything except covariate names self.model.wrap(results.tolist()) # this is temp solution because covariate orders gets mixed when using multiprocessing self.covariates_from_worker_result = covariate_names.tolist() # match covariate names to domain variables and set vertical header self.model.setVerticalHeaderLabels( [self.attr_name_to_variable[name] for name in covariate_names]) self.table_view.resizeColumnsToContents() self.auto_select() def on_exception(self, ex): raise ex def on_partial_result(self, result: Any) -> None: pass def set_selection_method(self, method): self.selection_method = method self.select_buttons.button(method).setChecked(True) self.auto_select() def auto_select(self): selection_model = self.table_view.selectionModel() row_count = self.model.rowCount() column_count = self.model.columnCount() if self.selection_method == OWRankSurvivalFeatures.select_none: selection = QItemSelection() elif self.selection_method == OWRankSurvivalFeatures.select_n_best: n_selected = min(self.n_selected, row_count) selection = QItemSelection( self.model.index(0, 0), self.model.index(n_selected - 1, column_count - 1)) else: selection = QItemSelection() if self.selected_attrs is not None: attr_indices = [ self.covariates_from_worker_result.index(var.name) for var in self.selected_attrs ] for row in self.model.mapFromSourceRows(attr_indices): selection.append( QItemSelectionRange( self.model.index(row, 0), self.model.index(row, column_count - 1))) selection_model.select(selection, QItemSelectionModel.ClearAndSelect) def on_select(self): selected_rows = self.table_view.selectionModel().selectedRows(0) row_indices = [i.row() for i in selected_rows] attr_indices = self.model.mapToSourceRows(row_indices) self.selected_attrs = [ self.model._headers[Qt.Vertical][row] for row in attr_indices ] self.commit()
def __init__(self): super().__init__() self.corpus = None # corpus taken from distances self.linkage = None # hierarchical clustering linkage as returned by Orange self.distances = None # DistMatrix on input self.clustering_mask = None # 1D array of clusters for self.corpus self.threshold_spin = None # Info self.n_documents = '' self.n_unique = '' self.n_duplicates = '' info_box = gui.widgetBox(self.controlArea, box='Info') gui.label(info_box, self, 'Documents: %(n_documents)s') gui.label(info_box, self, ' ◦ unique: %(n_unique)s') gui.label(info_box, self, ' ◦ duplicates: %(n_duplicates)s') # Threshold Histogram & Cluster View self.histogram = Histogram(self) self.table_view = gui.TableView( selectionMode=QListView.SingleSelection) self.table_model = PyTableModel() self.table_model.setHorizontalHeaderLabels(['Cluster', 'Size']) self.table_view.setModel(self.table_model) self.table_view.selectionModel().selectionChanged.connect( self.send_duplicates) # Add to main area height = 300 main_area = gui.hBox(self.mainArea) self.histogram.setMinimumWidth(500) self.histogram.setMinimumHeight(height) self.table_view.setFixedWidth(140) main_area.layout().addWidget(self.histogram) main_area.layout().addWidget(self.table_view) # Controls gui.comboBox(self.controlArea, self, 'linkage_method', items=self.LINKAGE, box='Linkage', callback=self.recalculate_linkage, orientation=Qt.Horizontal) self.threshold_spin = gui.doubleSpin(self.controlArea, self, 'threshold', 0, float('inf'), 0.01, decimals=2, label='Distance threshold', box='Distances', callback=self.threshold_changed, keyboardTracking=False, controlWidth=60) self.histogram.region.sigRegionChangeFinished.connect( self.threshold_from_histogram_region) self.threshold_spin.setEnabled(False) gui.rubber(self.controlArea) # Output gui.comboBox(self.controlArea, self, "cluster_role", box='Output', label='Append Cluster IDs to:', callback=self.send_corpus, items=self.CLUSTER_ROLES)
class TestPyTableModel(unittest.TestCase): def setUp(self): self.model = PyTableModel([[1, 4], [2, 3]]) def test_init(self): self.model = PyTableModel() self.assertEqual(self.model.rowCount(), 0) def test_rowCount(self): self.assertEqual(self.model.rowCount(), 2) self.assertEqual(len(self.model), 2) def test_columnCount(self): self.assertEqual(self.model.columnCount(), 2) def test_data(self): mi = self.model.index(0, 0) self.assertEqual(self.model.data(mi), '1') self.assertEqual(self.model.data(mi, Qt.EditRole), 1) def test_editable(self): editable_model = PyTableModel([[0]], editable=True) self.assertFalse(int(self.model.flags(self.model.index(0, 0)) & Qt.ItemIsEditable)) self.assertTrue(int(editable_model.flags(editable_model.index(0, 0)) & Qt.ItemIsEditable)) def test_sort(self): self.model.sort(1) self.assertEqual(self.model.index(0, 0).data(Qt.EditRole), 2) def test_setHeaderLabels(self): self.model.setHorizontalHeaderLabels(['Col 1', 'Col 2']) self.assertEqual(self.model.headerData(1, Qt.Horizontal), 'Col 2') self.assertEqual(self.model.headerData(1, Qt.Vertical), 2) def test_removeRows(self): self.model.removeRows(0, 1) self.assertEqual(len(self.model), 1) self.assertEqual(self.model[0][1], 3) def test_removeColumns(self): self.model.removeColumns(0, 1) self.assertEqual(self.model.columnCount(), 1) self.assertEqual(self.model[1][0], 3) def test_insertRows(self): self.model.insertRows(0, 1) self.assertEqual(self.model[1][0], 1) def test_insertColumns(self): self.model.insertColumns(0, 1) self.assertEqual(self.model[0], ['', 1, 4]) def test_wrap(self): self.model.wrap([[0]]) self.assertEqual(self.model.rowCount(), 1) self.assertEqual(self.model.columnCount(), 1) def test_clear(self): self.model.clear() self.assertEqual(self.model.rowCount(), 0) def test_append(self): self.model.append([5, 6]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_extend(self): self.model.extend([[5, 6]]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_insert(self): self.model.insert(0, [5, 6]) self.assertEqual(self.model[0][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_remove(self): self.model.remove([2, 3]) self.assertEqual(self.model.rowCount(), 1) def test_other_roles(self): self.model.append([2, 3]) self.model.setData(self.model.index(2, 0), Qt.AlignCenter, Qt.TextAlignmentRole) del self.model[1] self.assertTrue(Qt.AlignCenter & self.model.data(self.model.index(1, 0), Qt.TextAlignmentRole))
def setUp(self): self.model = PyTableModel([[1, 4], [2, 3]])
class TestPyTableModel(unittest.TestCase): def setUp(self): self.model = PyTableModel([[1, 4], [2, 3]]) def test_init(self): self.model = PyTableModel() self.assertEqual(self.model.rowCount(), 0) def test_rowCount(self): self.assertEqual(self.model.rowCount(), 2) self.assertEqual(len(self.model), 2) def test_columnCount(self): self.assertEqual(self.model.columnCount(), 2) def test_data(self): mi = self.model.index(0, 0) self.assertEqual(self.model.data(mi), '1') self.assertEqual(self.model.data(mi, Qt.EditRole), 1) def test_editable(self): editable_model = PyTableModel([[0]], editable=True) self.assertFalse( int(self.model.flags(self.model.index(0, 0)) & Qt.ItemIsEditable)) self.assertTrue( int( editable_model.flags(editable_model.index(0, 0)) & Qt.ItemIsEditable)) def test_sort(self): self.model.sort(1) self.assertEqual(self.model.index(0, 0).data(Qt.EditRole), 2) def test_setHeaderLabels(self): self.model.setHorizontalHeaderLabels(['Col 1', 'Col 2']) self.assertEqual(self.model.headerData(1, Qt.Horizontal), 'Col 2') self.assertEqual(self.model.headerData(1, Qt.Vertical), 2) def test_removeRows(self): self.model.removeRows(0, 1) self.assertEqual(len(self.model), 1) self.assertEqual(self.model[0][1], 3) def test_removeColumns(self): self.model.removeColumns(0, 1) self.assertEqual(self.model.columnCount(), 1) self.assertEqual(self.model[1][0], 3) def test_insertRows(self): self.model.insertRows(0, 1) self.assertEqual(self.model[1][0], 1) def test_insertColumns(self): self.model.insertColumns(0, 1) self.assertEqual(self.model[0], ['', 1, 4]) def test_wrap(self): self.model.wrap([[0]]) self.assertEqual(self.model.rowCount(), 1) self.assertEqual(self.model.columnCount(), 1) def test_clear(self): self.model.clear() self.assertEqual(self.model.rowCount(), 0) def test_append(self): self.model.append([5, 6]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_extend(self): self.model.extend([[5, 6]]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_insert(self): self.model.insert(0, [5, 6]) self.assertEqual(self.model[0][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_remove(self): self.model.remove([2, 3]) self.assertEqual(self.model.rowCount(), 1) def test_other_roles(self): self.model.append([2, 3]) self.model.setData(self.model.index(2, 0), Qt.AlignCenter, Qt.TextAlignmentRole) del self.model[1] self.assertTrue( Qt.AlignCenter & self.model.data(self.model.index(1, 0), Qt.TextAlignmentRole)) def test_set_iten_slice(self): self.model[:1] = [[10, 11], [12, 13], [14, 15]] self.assertEqual(list(self.model), [[10, 11], [12, 13], [14, 15], [2, 3]]) self.model[1:3] = [] self.assertEqual(list(self.model), [[10, 11], [2, 3]]) self.model[:] = [[20, 21]] self.assertEqual(list(self.model), [[20, 21]]) self.model[1:] = [[10, 11], [2, 3]] self.assertEqual(list(self.model), [[20, 21], [10, 11], [2, 3]]) def test_emits_column_changes_on_row_insert(self): inserted = [] removed = [] model = PyTableModel() model.columnsInserted.connect(inserted.append) model.columnsRemoved.connect(removed.append) inserted = QSignalSpy(model.columnsInserted) removed = QSignalSpy(model.columnsRemoved) model.append([2]) self.assertEqual(list(inserted)[-1][1:], [0, 0]) model.append([2, 3]) self.assertEqual(list(inserted)[-1][1:], [1, 1]) del model[:] self.assertEqual(list(removed)[0][1:], [0, 1]) model.extend([[0, 1], [0, 2]]) self.assertEqual(list(inserted)[-1][1:], [0, 1]) model.clear() self.assertEqual(list(removed)[0][1:], [0, 1]) model[:] = [[1], [2]] self.assertEqual(list(inserted)[-1][1:], [0, 0])
def __init__(self): self.data = None self.indices = [] gui.comboBox( self.controlArea, self, 'agg_interval', label='Aggregate by:', items=tuple(self.AGG_TIME.keys()), sendSelectedValue=True, orientation=Qt.Horizontal, callback=self.on_changed, ) self.model = model = PyTableModel(parent=self, editable=[False, True]) model.setHorizontalHeaderLabels(['Attribute', 'Aggregation function']) class TableView(gui.TableView): def __init__(self, parent): super().__init__( parent, editTriggers=(self.SelectedClicked | self.CurrentChanged | self.DoubleClicked | self.EditKeyPressed), ) self.horizontalHeader().setStretchLastSection(False) self.setItemDelegateForColumn(1, self.ComboDelegate(self)) class _ItemDelegate(QStyledItemDelegate): def updateEditorGeometry(self, widget, option, _index): widget.setGeometry(option.rect) class ComboDelegate(_ItemDelegate): def __init__(self, parent): super().__init__(parent) self._parent = parent self._combo_continuous_model = ListModel(AGG_FUNCTIONS, parent=self) self._combo_discrete_model = ListModel([Mode], parent=self) self._combo_string_model = ListModel([Concatenate], parent=self) def createEditor(self, parent, _QStyleOptionViewItem, index): combo = QComboBox(parent) attr = index.model()[index.row()][0] combo.setModel( self._combo_continuous_model if attr. is_continuous else self._combo_discrete_model if attr. is_discrete else self._combo_string_model) return combo def setEditorData(self, combo, index): var = index.model().data(index, Qt.EditRole) combo.setCurrentIndex(combo.model().indexOf(var)) def setModelData(self, combo, model, index): func = combo.model()[combo.currentIndex()] model.setData(index, func, Qt.EditRole) view = TableView(self) view.setModel(model) model.dataChanged.connect(self.on_changed) self.controlArea.layout().addWidget(view) gui.auto_commit(self.controlArea, self, 'autocommit', '&Apply')
def __init__(self): super().__init__() self.data = None self.domainmodels = [] self.unmatched = [] top = self.controlArea def _radioChanged(): self.mainArea.setVisible(self.is_decoding == 0 and len(self.unmatched)) self.commit() modes = gui.radioButtons(top, self, 'is_decoding', callback=_radioChanged) gui.appendRadioButton( modes, '&Encode region names into geographical coordinates:', insertInto=top) box = gui.indentedBox(top) model = DomainModel(parent=self, valid_types=(StringVariable, DiscreteVariable)) self.domainmodels.append(model) combo = gui.comboBox(box, self, 'str_attr', label='Region identifier:', orientation=Qt.Horizontal, callback=self.region_attr_changed, sendSelectedValue=True, model=model) gui.comboBox(box, self, 'str_type', label='Identifier type:', orientation=Qt.Horizontal, items=tuple(self.ID_TYPE.keys()), callback=lambda: self.commit(), sendSelectedValue=True) # Select first mode if any of its combos are changed for combo in box.findChildren(QComboBox): combo.activated.connect(lambda: setattr(self, 'is_decoding', 0)) gui.appendRadioButton(modes, '&Decode latitude and longitude into regions:', insertInto=top) box = gui.indentedBox(top) model = DomainModel(parent=self, valid_types=ContinuousVariable) self.domainmodels.append(model) combo = gui.comboBox(box, self, 'lat_attr', label='Latitude:', orientation=Qt.Horizontal, callback=lambda: self.commit(), sendSelectedValue=True, model=model) combo = gui.comboBox(box, self, 'lon_attr', label='Longitude:', orientation=Qt.Horizontal, callback=lambda: self.commit(), sendSelectedValue=True, model=model) gui.comboBox( box, self, 'admin', label='Administrative level:', orientation=Qt.Horizontal, callback=lambda: self.commit(), items= ('Country', '1st-level subdivision (state, region, province, municipality, ...)', '2nd-level subdivisions (1st-level & US counties)'), ) # Select second mode if any of its combos are changed for combo in box.findChildren(QComboBox): combo.activated.connect(lambda: setattr(self, 'is_decoding', 1)) gui.checkBox( top, self, 'append_features', label='E&xtend coded data with additional region properties', callback=lambda: self.commit(), toolTip='Extend coded data with region properties, such as' 'ISO codes, continent, subregion, region type, ' 'economy type, FIPS/HASC codes, region capital etc. as available.') gui.auto_commit(self.controlArea, self, 'autocommit', '&Apply') gui.rubber(self.controlArea) model = self.replacementsModel = PyTableModel(self.replacements, parent=self, editable=[False, True]) view = gui.TableView(self, sortingEnabled=False, selectionMode=gui.TableView.NoSelection, editTriggers=gui.TableView.AllEditTriggers) view.horizontalHeader().setResizeMode(QHeaderView.Stretch) view.verticalHeader().setSectionResizeMode(0) view.setModel(model) owwidget = self class TableItemDelegate(QItemDelegate): def createEditor(self, parent, options, index): nonlocal owwidget edit = QLineEdit(parent) wordlist = [''] + ToLatLon.valid_values( owwidget.ID_TYPE[owwidget.str_type]) edit.setCompleter( QCompleter(wordlist, edit, caseSensitivity=Qt.CaseInsensitive, filterMode=Qt.MatchContains)) def save_and_commit(): if edit.text() and edit.text() in wordlist: model = index.model() pindex = QPersistentModelIndex(index) if pindex.isValid(): new_index = pindex.sibling(pindex.row(), pindex.column()) save = model.setData(new_index, edit.text(), Qt.EditRole) if save: owwidget.commit() return edit.clear() edit.editingFinished.connect(save_and_commit) return edit view.setItemDelegate(TableItemDelegate()) model.setHorizontalHeaderLabels( ['Unmatched Identifier', 'Custom Replacement']) box = gui.vBox(self.mainArea) self.info_str = ' /' gui.label(box, self, 'Unmatched identifiers: %(info_str)s') box.layout().addWidget(view) self.mainArea.setVisible(self.is_decoding == 0)
def __init__(self): super().__init__() self.data = None self.domainmodels = [] top = self.controlArea def _radioChanged(): self.mainArea.setVisible(self.is_decoding == 0) self.commit() modes = gui.radioButtons(top, self, 'is_decoding', callback=_radioChanged) gui.appendRadioButton( modes, '&Encode region names into geographical coordinates:', insertInto=top) box = gui.indentedBox(top) model = DomainModel(parent=self, valid_types=(StringVariable, DiscreteVariable)) self.domainmodels.append(model) def _region_attr_changed(): if self.data is None: return # Auto-detect the type of region in the attribute and set its combo values = self._get_data_values() func = ToLatLon.detect_input(values) str_type = next((k for k, v in self.ID_TYPE.items() if v == func), None) if str_type is not None and str_type != self.str_type: self.str_type = str_type self.commit() combo = gui.comboBox(box, self, 'str_attr', label='Region identifier:', orientation=Qt.Horizontal, callback=_region_attr_changed, sendSelectedValue=True) combo.setModel(model) gui.comboBox(box, self, 'str_type', label='Identifier type:', orientation=Qt.Horizontal, items=tuple(self.ID_TYPE.keys()), callback=lambda: self.commit(), sendSelectedValue=True) # Select first mode if any of its combos are changed for combo in box.findChildren(QComboBox): combo.currentIndexChanged.connect( lambda: setattr(self, 'is_decoding', 0)) gui.appendRadioButton(modes, '&Decode latitude and longitude into regions:', insertInto=top) box = gui.indentedBox(top) model = DomainModel(parent=self, valid_types=ContinuousVariable) self.domainmodels.append(model) combo = gui.comboBox(box, self, 'lat_attr', label='Latitude:', orientation=Qt.Horizontal, callback=lambda: self.commit(), sendSelectedValue=True) combo.setModel(model) combo = gui.comboBox(box, self, 'lon_attr', label='Longitude:', orientation=Qt.Horizontal, callback=lambda: self.commit(), sendSelectedValue=True) combo.setModel(model) gui.comboBox( box, self, 'admin', label='Administrative level:', orientation=Qt.Horizontal, callback=lambda: self.commit(), items= ('Country', '1st-level subdivision (state, region, province, municipality, ...)', '2nd-level subdivisions (1st-level & US counties)'), ) # Select second mode if any of its combos are changed for combo in box.findChildren(QComboBox): combo.currentIndexChanged.connect( lambda: setattr(self, 'is_decoding', 1)) gui.checkBox( top, self, 'append_features', label='E&xtend coded data with additional region properties', callback=lambda: self.commit(), toolTip='Extend coded data with region properties, such as' 'ISO codes, continent, subregion, region type, ' 'economy type, FIPS/HASC codes, region capital etc. as available.') gui.auto_commit(self.controlArea, self, 'autocommit', '&Apply') model = self.replacementsModel = PyTableModel(self.replacements, parent=self, editable=[False, True]) view = gui.TableView(self, sortingEnabled=False, selectionMode=gui.TableView.NoSelection, editTriggers=gui.TableView.AllEditTriggers) view.horizontalHeader().setSectionResizeMode(0) view.verticalHeader().setSectionResizeMode(0) view.setModel(model) owwidget = self class EditorFactory(QItemEditorFactory): def createEditor(self, p_int, parent): nonlocal owwidget edit = QLineEdit(parent) wordlist = [''] + ToLatLon.valid_values( owwidget.ID_TYPE[owwidget.str_type]) edit.setCompleter( QCompleter(wordlist, edit, caseSensitivity=Qt.CaseInsensitive, filterMode=Qt.MatchContains)) return edit self.factory = EditorFactory() view.itemDelegate().setItemEditorFactory(self.factory) model.setHorizontalHeaderLabels( ['Unmatched Identifier', 'Custom Replacement']) box = gui.vBox(self.mainArea) self.info_str = ' /' gui.label(box, self, 'Unmatched identifiers: %(info_str)s') box.layout().addWidget(view) self.mainArea.setVisible(self.is_decoding == 0)
def _create_layout(self): self.mainArea.layout().addWidget(self.graphview) info = gui.widgetBox(self.controlArea, 'Info') gui.label(info, self, '%(n_object_types)d object types') gui.label(info, self, '%(n_relations)d relations') # Table view of relation details info = gui.widgetBox(self.controlArea, 'Relations') class TableView(gui.TableView): def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self._parent = parent self.bold_font = self.BoldFontDelegate( self) # member because PyQt sometimes unrefs too early self.setItemDelegateForColumn(2, self.bold_font) self.setItemDelegateForColumn(4, self.bold_font) self.horizontalHeader().setVisible(False) def selectionChanged(self, selected, deselected): super().selectionChanged(selected, deselected) if not selected: assert len(deselected) > 0 relation = None else: assert len(selected) == 1 data = self._parent.tablemodel[selected[0].top()][0] relation = Relation(data) self._parent.send(Output.RELATION, relation) model = self.tablemodel = PyTableModel(parent=self) table = self.table = TableView(self, selectionMode=TableView.SingleSelection) table.setModel(model) info.layout().addWidget(self.table) gui.lineEdit(self.controlArea, self, 'pref_algo_name', 'Fuser name:', orientation='horizontal', callback=self.checkcommit, enterPlaceholder=True) gui.radioButtons(self.controlArea, self, 'pref_algorithm', [i[0] for i in DECOMPOSITION_ALGO], box='Decomposition algorithm', callback=self.checkcommit) gui.radioButtons(self.controlArea, self, 'pref_initialization', INITIALIZATION_ALGO, box='Initialization algorithm', callback=self.checkcommit) slider = gui.hSlider(self.controlArea, self, 'pref_n_iterations', 'Maximum number of iterations', minValue=10, maxValue=500, createLabel=True, callback=self.checkcommit) slider.setTracking(False) self.slider_rank = gui.hSlider(self.controlArea, self, 'pref_rank', 'Factorization rank', minValue=1, maxValue=100, createLabel=True, labelFormat=" %d%%", callback=self.checkcommit) self.slider_rank.setTracking(False) gui.auto_commit(self.controlArea, self, "autorun", "Run", checkbox_label="Run after any change ")
def _create_layout(self): self._new_webview() box = gui.widgetBox(self.controlArea, 'Info') self.topic_info = gui.label(box, self, '%(n_topic_words)d words in a topic') gui.label(box, self, '%(documents_info_str)s') box = gui.widgetBox(self.controlArea, 'Cloud preferences') gui.checkBox(box, self, 'words_color', 'Color words', callback=self.on_cloud_pref_change) TILT_VALUES = ('no', '30°', '45°', '60°') gui.valueSlider(box, self, 'words_tilt', label='Words tilt:', values=list(range(len(TILT_VALUES))), callback=self.on_cloud_pref_change, labelFormat=lambda x: TILT_VALUES[x]) gui.button(box, None, 'Regenerate word cloud', callback=self.on_cloud_pref_change) box = gui.widgetBox(self.controlArea, 'Words && weights') class TableView(gui.TableView): def __init__(self, parent): super().__init__(parent) self._parent = parent self.__nope = False def setModel(self, model): """Otherwise QTableView.setModel() calls QAbstractItemView.setSelectionModel() which resets selection, calling selectionChanged() and overwriting any selected_words setting that may have been saved.""" self.__nope = True super().setModel(model) self.__nope = False def selectionChanged(self, selected, deselected): nonlocal model, proxymodel super().selectionChanged(selected, deselected) if not self.__nope: words = { model[proxymodel.mapToSource(index).row()][1] for index in self.selectionModel().selectedIndexes() } self._parent.update_selection(words, self) def update_selection(self, words): nonlocal model, proxymodel selection = QItemSelection() for i, (_, word) in enumerate(model): if word in words: index = proxymodel.mapFromSource(model.index(i, 1)) selection.select(index, index) self.__nope = True self.clearSelection() self.selectionModel().select( selection, QItemSelectionModel.Select | QItemSelectionModel.Rows) self.__nope = False view = self.tableview = TableView(self) model = self.tablemodel = PyTableModel(parent=self) proxymodel = QSortFilterProxyModel( self, dynamicSortFilter=True, sortCaseSensitivity=Qt.CaseInsensitive, sortRole=Qt.EditRole) proxymodel.setSourceModel(model) model.setHorizontalHeaderLabels(['Weight', 'Word']) view.setModel(proxymodel) box.layout().addWidget(view)
def setUp(self): assert issubclass(PyTableModel, AbstractSortTableModel) self.model = PyTableModel([[1, 4], [2, 3]])
class TestPyTableModel(TestCase): def setUp(self): self.model = PyTableModel([[1, 4], [2, 3]]) def test_init(self): self.model = PyTableModel() self.assertEqual(self.model.rowCount(), 0) def test_rowCount(self): self.assertEqual(self.model.rowCount(), 2) self.assertEqual(len(self.model), 2) def test_columnCount(self): self.assertEqual(self.model.columnCount(), 2) def test_data(self): mi = self.model.index(0, 0) self.assertEqual(self.model.data(mi), '1') self.assertEqual(self.model.data(mi, Qt.EditRole), 1) def test_editable(self): editable_model = PyTableModel([[0]], editable=True) self.assertFalse( int(self.model.flags(self.model.index(0, 0)) & Qt.ItemIsEditable)) self.assertTrue( int( editable_model.flags(editable_model.index(0, 0)) & Qt.ItemIsEditable)) def test_sort(self): self.model.sort(1) self.assertEqual(self.model[0][0], 2) def test_setHeaderLabels(self): self.model.setHorizontalHeaderLabels(['Col 1', 'Col 2']) self.assertEqual(self.model.headerData(1, Qt.Horizontal), 'Col 2') self.assertEqual(self.model.headerData(1, Qt.Vertical), '1') def test_removeRows(self): self.model.removeRows(0, 1) self.assertEqual(len(self.model), 1) self.assertEqual(self.model[0][1], 3) def test_removeColumns(self): self.model.removeColumns(0, 1) self.assertEqual(self.model.columnCount(), 1) self.assertEqual(self.model[1][0], 3) def test_insertRows(self): self.model.insertRows(0, 1) self.assertEqual(self.model[1][0], 1) def test_insertColumns(self): self.model.insertColumns(0, 1) self.assertEqual(self.model[0], ['', 1, 4]) def test_wrap(self): self.model.wrap([[0]]) self.assertEqual(self.model.rowCount(), 1) self.assertEqual(self.model.columnCount(), 1) def test_clear(self): self.model.clear() self.assertEqual(self.model.rowCount(), 0) def test_append(self): self.model.append([5, 6]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_extend(self): self.model.extend([[5, 6]]) self.assertEqual(self.model[2][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_insert(self): self.model.insert(0, [5, 6]) self.assertEqual(self.model[0][1], 6) self.assertEqual(self.model.rowCount(), 3) def test_remove(self): self.model.remove([2, 3]) self.assertEqual(self.model.rowCount(), 1) def test_other_roles(self): self.model.append([2, 3]) self.model.setData(self.model.index(2, 0), Qt.AlignCenter, Qt.TextAlignmentRole) del self.model[1] self.assertTrue( Qt.AlignCenter & self.model.data(self.model.index(1, 0), Qt.TextAlignmentRole))
def test_report_table(self): rep = OWReport.get_instance() model = PyTableModel([['x', 1, 2], ['y', 2, 2]]) model.setHorizontalHeaderLabels(['a', 'b', 'c']) model.setData(model.index(0, 0), Qt.AlignHCenter | Qt.AlignTop, Qt.TextAlignmentRole) model.setData(model.index(1, 0), QFont('', -1, QFont.Bold), Qt.FontRole) model.setData(model.index(1, 2), QBrush(Qt.red), Qt.BackgroundRole) view = gui.TableView() view.show() view.setModel(model) rep.report_table('Name', view) self.maxDiff = None self.assertEqual( rep.report_html, '<h2>Name</h2><table>\n' '<tr>' '<th style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:left;vertical-align:middle;">a</th>' '<th style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:left;vertical-align:middle;">b</th>' '<th style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:left;vertical-align:middle;">c</th>' '</tr>' '<tr>' '<td style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:center;vertical-align:top;">x</td>' '<td style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:right;vertical-align:middle;">1</td>' '<td style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:right;vertical-align:middle;">2</td>' '</tr>' '<tr>' '<td style="color:black;border:0;background:transparent;' 'font-weight:bold;text-align:left;vertical-align:middle;">y</td>' '<td style="color:black;border:0;background:transparent;' 'font-weight:normal;text-align:right;vertical-align:middle;">2</td>' '<td style="color:black;border:0;background:#ff0000;' 'font-weight:normal;text-align:right;vertical-align:middle;">2</td>' '</tr></table>')
def _create_layout(self): self.mainArea.layout().addWidget(self.graphview) info = gui.widgetBox(self.controlArea, 'Info') gui.label(info, self, '%(n_object_types)d object types') gui.label(info, self, '%(n_relations)d relations') class TableView(gui.TableView): def __init__(self, parent, bold_columns): super().__init__(parent, selectionMode=self.SingleSelection) self.horizontalHeader().setVisible(False) self.bold_font = self.BoldFontDelegate(self) # member because PyQt sometimes unrefs too early for col in bold_columns: self.setItemDelegateForColumn(col, self.bold_font) box = gui.widgetBox(self.controlArea, 'Recipe factors') table = self.table_factors = TableView(self, (2,)) model = self.model_factors = PyTableModel(parent=self) table.setModel(model) box.layout().addWidget(table) box = gui.widgetBox(self.controlArea, 'Backbone factors') table = self.table_backbones = TableView(self, (2, 4)) model = self.model_backbones = PyTableModel(parent=self) table.setModel(model) box.layout().addWidget(table) box = gui.widgetBox(self.controlArea, 'Completed relations') table = self.table_completions = TableView(self, (2, 4)) model = self.model_completions = PyTableModel(parent=self) table.setModel(model) box.layout().addWidget(table) self.controlArea.layout().addStretch(1) def _on_selected_factor(item): self.table_completions.clearSelection() self.table_backbones.clearSelection() def _on_selected_backbone(item): self.table_completions.clearSelection() self.table_factors.clearSelection() def _on_selected_completion(item): self.table_factors.clearSelection() self.table_backbones.clearSelection() def on_selection_changed(table, model, itemChanged_handler): def _f(selected, deselected): gui.TableView.selectionChanged(table, selected, deselected) if not selected: return self.commit(None) item = model[selected[0].top()][0] itemChanged_handler(item) self.commit(item) return _f self.table_factors.selectionChanged = on_selection_changed( self.table_factors, self.model_factors, _on_selected_factor) self.table_backbones.selectionChanged = on_selection_changed( self.table_backbones, self.model_backbones, _on_selected_backbone) self.table_completions.selectionChanged = on_selection_changed( self.table_completions, self.model_completions, _on_selected_completion)