|
17 | 17 | from AnyQt.QtCore import Qt, QSize, QRectF, QObject |
18 | 18 |
|
19 | 19 | from orangewidget.utils.combobox import ComboBox, ComboBoxSearch |
20 | | -from Orange.data import Domain, Table, Variable, DiscreteVariable |
| 20 | +from Orange.data import Domain, Table, Variable, DiscreteVariable, \ |
| 21 | + ContinuousVariable |
21 | 22 | from Orange.data.sql.table import SqlTable |
22 | 23 | import Orange.distance |
23 | 24 |
|
@@ -412,13 +413,36 @@ def _(idx, cb=cb): |
412 | 413 | form.addRow("Text", self.annotation_text_cb) |
413 | 414 | form.addRow("Color", self.row_side_color_cb) |
414 | 415 | box.layout().addWidget(annotbox) |
415 | | - posbox = gui.vBox(box, "Column Labels Position", addSpace=False) |
416 | | - posbox.setFlat(True) |
| 416 | + annotbox = QGroupBox("Column annotations", flat=True) |
| 417 | + form = QFormLayout( |
| 418 | + annotbox, |
| 419 | + formAlignment=Qt.AlignLeft, |
| 420 | + labelAlignment=Qt.AlignLeft, |
| 421 | + fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow |
| 422 | + ) |
| 423 | + self.col_side_color_model = DomainModel( |
| 424 | + placeholder="(None)", |
| 425 | + valid_types=(DiscreteVariable, ContinuousVariable), |
| 426 | + parent=self |
| 427 | + ) |
| 428 | + self.col_side_color_cb = cb = ComboBoxSearch( |
| 429 | + sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLength, |
| 430 | + minimumContentsLength=12 |
| 431 | + ) |
| 432 | + self.col_side_color_cb.setModel(self.col_side_color_model) |
| 433 | + self.column_annotation_color_var = None |
| 434 | + self.col_side_color_cb.activated.connect(self.__set_column_annotation_color_key_index) |
| 435 | + # posbox = gui.vBox(box, "Column Labels Position", addSpace=False) |
| 436 | + # posbox.setFlat(True) |
417 | 437 | cb = gui.comboBox( |
418 | | - posbox, self, "column_label_pos", |
| 438 | + None, self, "column_label_pos", |
419 | 439 | callback=self.update_column_annotations) |
420 | 440 | cb.setModel(create_list_model(ColumnLabelsPosData, parent=self)) |
421 | 441 | cb.setCurrentIndex(self.column_label_pos) |
| 442 | + form.addRow("Color", self.col_side_color_cb) |
| 443 | + form.addRow("Label position", cb) |
| 444 | + box.layout().addWidget(annotbox) |
| 445 | + |
422 | 446 | gui.checkBox(self.controlArea, self, "keep_aspect", |
423 | 447 | "Keep aspect ratio", box="Resize", |
424 | 448 | callback=self.__aspect_mode_changed) |
@@ -596,7 +620,7 @@ def set_dataset(self, data=None): |
596 | 620 | self.row_split_model.set_domain(data.domain) |
597 | 621 | self.col_annot_data = data.transpose(data[:0].transform(Domain(data.domain.attributes))) |
598 | 622 | self.col_split_model.set_domain(self.col_annot_data.domain) |
599 | | - |
| 623 | + self.col_side_color_model.set_domain(self.col_annot_data.domain) |
600 | 624 | if data.domain.has_discrete_class: |
601 | 625 | self.split_by_var = data.domain.class_var |
602 | 626 | else: |
@@ -633,7 +657,7 @@ def set_split_variable(self, var): |
633 | 657 | self.update_heatmaps() |
634 | 658 |
|
635 | 659 | def __on_split_cols_activated(self): |
636 | | - self.set_column_split_key(self.col_split_cb.currentData(Qt.UserRole)) |
| 660 | + self.set_column_split_key(self.col_split_cb.currentData(Qt.EditRole)) |
637 | 661 |
|
638 | 662 | def set_column_split_key(self, key): |
639 | 663 | if key != self.split_columns_key: |
@@ -812,7 +836,9 @@ def construct_heatmaps(self, data, group_var=None, column_split_key=None) -> 'Pa |
812 | 836 |
|
813 | 837 | self.__update_clustering_enable_state(effective_data) |
814 | 838 |
|
815 | | - parts = self._make_parts(effective_data, group_var, column_split_key) |
| 839 | + parts = self._make_parts( |
| 840 | + effective_data, group_var, |
| 841 | + column_split_key.name if column_split_key is not None else None) |
816 | 842 | # Restore/update the row/columns items descriptions from cache if |
817 | 843 | # available |
818 | 844 | rows_cache_key = (group_var, |
@@ -882,9 +908,15 @@ def setup_scene(self, parts, data): |
882 | 908 | col_names=columns, |
883 | 909 | ) |
884 | 910 | widget.setHeatmaps(parts) |
| 911 | + |
885 | 912 | side = self.row_side_colors() |
886 | 913 | if side is not None: |
887 | 914 | widget.setRowSideColorAnnotations(side[0], side[1], name=side[2].name) |
| 915 | + |
| 916 | + side = self.column_side_colors() |
| 917 | + if side is not None: |
| 918 | + widget.setColumnSideColorAnnotations(side[0], side[1], name=side[2].name) |
| 919 | + |
888 | 920 | widget.setColumnLabelsPosition(self._column_label_pos) |
889 | 921 | widget.setAspectRatioMode( |
890 | 922 | Qt.KeepAspectRatio if self.keep_aspect else Qt.IgnoreAspectRatio |
@@ -1111,6 +1143,28 @@ def _colorize(self, var: Variable, data: np.ndarray) -> Tuple[np.ndarray, ColorM |
1111 | 1143 | else: |
1112 | 1144 | raise TypeError |
1113 | 1145 |
|
| 1146 | + def __set_column_annotation_color_key_index(self, index): |
| 1147 | + self.column_annotation_color_var = self.col_side_color_cb.itemData(index, Qt.EditRole) |
| 1148 | + colors = self.column_side_colors() |
| 1149 | + if colors is not None: |
| 1150 | + self.scene.widget.setColumnSideColorAnnotations( |
| 1151 | + colors[0], colors[1], colors[2].name, |
| 1152 | + ) |
| 1153 | + else: |
| 1154 | + self.scene.widget.setColumnSideColorAnnotations(None) |
| 1155 | + |
| 1156 | + def column_side_colors(self): |
| 1157 | + var = self.column_annotation_color_var |
| 1158 | + if var is None: |
| 1159 | + return None |
| 1160 | + table = self.col_annot_data |
| 1161 | + var = table.domain[var] |
| 1162 | + column_data = column_data_from_table(table, var) |
| 1163 | + data, colormap = self._colorize(var, column_data) |
| 1164 | + if var.is_continuous: |
| 1165 | + colormap.span = (np.nanmin(column_data), np.nanmax(column_data)) |
| 1166 | + return data, colormap, var |
| 1167 | + |
1114 | 1168 | def update_column_annotations(self): |
1115 | 1169 | widget = self.scene.widget |
1116 | 1170 | if self.data is not None and widget is not None: |
|
0 commit comments