Skip to content

Commit

Permalink
Merge pull request #231 from janezd/labelled-separator
Browse files Browse the repository at this point in the history
[ENH] Add SeparatorItem and LabelledSeparator
  • Loading branch information
markotoplak authored Feb 21, 2023
2 parents 5c7b451 + 8bf82bb commit cf56c6b
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 6 deletions.
47 changes: 43 additions & 4 deletions orangewidget/utils/itemmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
)
from AnyQt.QtCore import pyqtSignal as Signal
from AnyQt.QtWidgets import (
QWidget, QBoxLayout, QToolButton, QAbstractButton, QAction
QWidget, QBoxLayout, QToolButton, QAbstractButton, QAction,
QStyledItemDelegate
)
from AnyQt.QtGui import QPalette, QPen

import numpy

Expand Down Expand Up @@ -496,11 +498,48 @@ def remove(self, val):
del self[self._table.index(val)]


class SeparatorItem:
pass


class LabelledSeparator(SeparatorItem):
def __init__(self, label=None):
self.label = label


class SeparatedListDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
# type: (QPainter, QStyleOptionViewItem, QModelIndex) -> None
super().paint(painter, option, index)
data = index.data(Qt.EditRole)
if not isinstance(data, LabelledSeparator):
return

painter.save()
palette = option.palette # type: QPalette
rect = option.rect # type: QRect
if data.label:
y = int(rect.bottom() - 0.1 * rect.height())
brush = palette.brush(QPalette.Active, QPalette.WindowText)
font = painter.font()
font.setPointSizeF(font.pointSizeF() * 0.9)
font.setBold(True)
painter.setFont(font)
painter.setPen(QPen(brush, 1.0))
painter.drawText(rect, Qt.AlignCenter, data.label)
else:
y = rect.center().y()
brush = palette.brush(QPalette.Disabled, QPalette.WindowText)
painter.setPen(QPen(brush, 1.0))
painter.drawLine(rect.left(), y, rect.left() + rect.width(), y)
painter.restore()


class PyListModel(QAbstractListModel):
""" A model for displaying python list like objects in Qt item view classes
"""
MIME_TYPE = "application/x-Orange-PyListModelData"
Separator = object()
Separator = SeparatorItem()
removed = Signal()

def __init__(self, iterable=None, parent=None,
Expand Down Expand Up @@ -558,7 +597,7 @@ def data(self, index, role=Qt.DisplayRole):
and self._is_index_valid(index):
return self[row]
elif self._is_index_valid(row):
if self[row] is self.Separator \
if isinstance(self[row], SeparatorItem) \
and role == Qt.AccessibleDescriptionRole:
return 'separator'
return self._other_data[row].get(role, None)
Expand Down Expand Up @@ -608,7 +647,7 @@ def flags(self, index):
if self._is_index_valid(index):
row = index.row()
default = Qt.NoItemFlags \
if self[row] is self.Separator else self._flags
if isinstance(self[row], SeparatorItem) else self._flags
return self._other_data[row].get("flags", default)
else:
return self._flags | Qt.ItemIsDropEnabled
Expand Down
36 changes: 34 additions & 2 deletions orangewidget/utils/tests/test_itemmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
# pylint: disable=missing-docstring

import unittest
from unittest.mock import patch, Mock

import numpy as np

from AnyQt.QtCore import Qt, QModelIndex
from AnyQt.QtCore import Qt, QModelIndex, QRect
from AnyQt.QtTest import QSignalSpy
from AnyQt.QtGui import QPalette, QFont

from orangewidget.utils.itemmodels import \
AbstractSortTableModel, PyTableModel, PyListModel, \
_argsort, _as_contiguous_range
_argsort, _as_contiguous_range, SeparatedListDelegate, LabelledSeparator


class TestArgsort(unittest.TestCase):
Expand Down Expand Up @@ -647,5 +649,35 @@ def test_separator(self):
i % 2 != 0, f"in row {i}")


class TestSeparatedListDelegate(unittest.TestCase):
@patch("AnyQt.QtWidgets.QStyledItemDelegate.paint")
def test_paint(self, _):
delegate = SeparatedListDelegate()
painter = Mock()
font = QFont()
font.setPointSizeF(10)
painter.font = lambda: font
option = Mock()
option.palette = QPalette()
option.rect = QRect(10, 20, 50, 5)
index = Mock()

index.data = Mock(return_value="foo")
delegate.paint(painter, option, index)
painter.drawText.assert_not_called()
painter.drawLine.assert_not_called()

index.data = Mock(return_value=LabelledSeparator())
delegate.paint(painter, option, index)
painter.drawLine.assert_called_with(10, 22, 60, 22)
painter.drawLine.reset_mock()
painter.drawText.assert_not_called()

index.data = Mock(return_value=LabelledSeparator("bar"))
delegate.paint(painter, option, index)
painter.drawLine.assert_called()
painter.drawText.assert_called_with(option.rect, Qt.AlignCenter, "bar")


if __name__ == "__main__":
unittest.main()

0 comments on commit cf56c6b

Please sign in to comment.