Skip to content

Commit

Permalink
Merge pull request #6890 from janezd/feature-statistics-legend
Browse files Browse the repository at this point in the history
Feature Statistics: Add a legend
  • Loading branch information
VesnaT authored Sep 26, 2024
2 parents 1d90230 + 82b2598 commit e045632
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
55 changes: 53 additions & 2 deletions Orange/widgets/data/owfeaturestatistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
import scipy.sparse as sp
from AnyQt.QtCore import Qt, QSize, QRectF, QModelIndex, pyqtSlot, \
QItemSelection, QItemSelectionRange, QItemSelectionModel
from AnyQt.QtGui import QPainter, QColor, QPalette
from AnyQt.QtGui import QPainter, QColor, QPalette, QFontMetrics
from AnyQt.QtWidgets import QStyledItemDelegate, QGraphicsScene, QTableView, \
QHeaderView, QStyle, QStyleOptionViewItem
QHeaderView, QStyle, QStyleOptionViewItem, \
QGraphicsView, QGraphicsItemGroup

import Orange.statistics.util as ut
from Orange.data import Table, StringVariable, DiscreteVariable, \
Expand All @@ -31,6 +32,8 @@
from Orange.widgets.utils.itemmodels import DomainModel, AbstractSortTableModel
from Orange.widgets.utils.signals import Input, Output
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.visualize.utils import CanvasRectangle, CanvasText
from Orange.widgets.visualize.utils.plotutils import wrap_legend_items


def _categorical_entropy(x):
Expand Down Expand Up @@ -773,6 +776,15 @@ def __init__(self):

box.layout().addWidget(self.table_view)

self.legend_items = []
self.legend = QGraphicsScene()
self.legend_view = u = QGraphicsView(self.legend)
u.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)
u.setFrameStyle(QGraphicsView.NoFrame)
u.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
u.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
box.layout().addWidget(u)

self.color_var_model = DomainModel(
valid_types=(ContinuousVariable, DiscreteVariable),
placeholder='None',
Expand All @@ -791,6 +803,10 @@ def __init__(self):
def sizeHint(): # pylint: disable=arguments-differ
return QSize(1050, 500)

def resizeEvent(self, event):
super().resizeEvent(event)
self.update_legend()

@Inputs.data
def set_data(self, data):
# Clear outputs and reset widget state
Expand Down Expand Up @@ -853,6 +869,41 @@ def on_header_click(self, *_):
def __color_var_changed(self, *_):
if self.model is not None:
self.model.set_target_var(self.color_var)
self.update_legend_items()
self.update_legend()

def update_legend_items(self):
self.legend.clear()
if self.color_var is None or not self.color_var.is_discrete:
self.legend_items = []
return
self.legend_items = []
size = QFontMetrics(self.font()).height()
for name, color in zip(self.color_var.values, self.color_var.palette.qcolors):
item = QGraphicsItemGroup()
item.addToGroup(
CanvasRectangle(None, -size / 2, -size / 2, size, size,
Qt.gray, color))
item.addToGroup(
CanvasText(None, name, size, 0, Qt.AlignVCenter))
self.legend_items.append(item)

def update_legend(self):
view = self.legend_view

if not self.legend_items:
self.legend.clear()
view.hide()
return

size = QFontMetrics(self.font()).height()
legend = wrap_legend_items(
self.legend_items,
self.width() - 30, size, size * 1.75)
self.legend.addItem(legend)
legend.setPos(15, 0)
view.setFixedHeight(int(legend.boundingRect().height()) + size)
view.show()

def on_select(self):
selection_indices = list(self.model.mapToSourceRows([
Expand Down
33 changes: 33 additions & 0 deletions Orange/widgets/data/tests/test_owfeaturestatistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,39 @@ def test_report(self):
self.assertIn("<table>", report_text)
self.assertEqual(6, report_text.count("<tr>")) # header + 5 rows

def test_color_legend(self):
w = self.widget
data = Table("heart_disease")
self.send_signal(self.widget.Inputs.data, data)

self.assertIs(w.color_var, data.domain.class_var)
self.assertEqual(len(w.legend_items), 2)
self.assertFalse(w.legend_view.isHidden())

w.cb_color_var.setCurrentIndex(4) # age (numeric, no legend)
w.cb_color_var.activated.emit(4)
self.assertEqual(len(w.legend_items), 0)
self.assertTrue(w.legend_view.isHidden())

w.cb_color_var.setCurrentIndex(6) # chest pain
w.cb_color_var.activated.emit(6)
self.assertEqual(len(w.legend_items), 4)
self.assertFalse(w.legend_view.isHidden())

w.cb_color_var.setCurrentIndex(0) # None
w.cb_color_var.activated.emit(0)
self.assertEqual(len(w.legend_items), 0)
self.assertTrue(w.legend_view.isHidden())

# Show
w.cb_color_var.setCurrentIndex(6) # chest pain
w.cb_color_var.activated.emit(6)

# to check that the legend is hidden when the data is removed
self.send_signal(self.widget.Inputs.data, None)
self.assertEqual(len(w.legend_items), 0)
self.assertTrue(w.legend_view.isHidden())


class TestSummary(WidgetTest):
def setUp(self):
Expand Down

0 comments on commit e045632

Please sign in to comment.