File: models.py

package info (click to toggle)
orange3 3.40.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 15,908 kB
  • sloc: python: 162,745; ansic: 622; makefile: 322; sh: 93; cpp: 77
file content (156 lines) | stat: -rw-r--r-- 5,636 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from math import isnan

from AnyQt.QtCore import Qt, QIdentityProxyModel, QModelIndex

from orangewidget.gui import OrangeUserRole

import Orange
from Orange.widgets import gui
from Orange.widgets.utils.itemmodels import TableModel

_BarRole = gui.TableBarItem.BarRole


class RichTableModel(TableModel):
    """A TableModel with some extra bells and whistles/

    (adds support for gui.BarRole, include variable labels and icons
    in the header)
    """
    #: Rich header data flags.
    Name, Labels, Icon = 1, 2, 4

    #: Qt.ItemData role to retrieve variable's header attributes.
    LabelsItemsRole = next(OrangeUserRole)

    def __init__(self, sourcedata, parent=None):
        super().__init__(sourcedata, parent)

        self._header_flags = RichTableModel.Name
        self._continuous = [var.is_continuous for var in self.vars]
        labels = []
        for var in self.vars:
            if isinstance(var, Orange.data.Variable):
                labels.extend(var.attributes.keys())
        self._labels = list(sorted(
            {label for label in labels if not label.startswith("_")}))

    def data(self, index, role=Qt.DisplayRole):
        # pylint: disable=arguments-differ
        if role == _BarRole and self._continuous[index.column()]:
            val = super().data(index, TableModel.ValueRole)
            if val is None or isnan(val):
                return None

            dist = super().data(index, TableModel.VariableStatsRole)
            if dist is not None and dist.max > dist.min:
                return (val - dist.min) / (dist.max - dist.min)
            else:
                return None
        elif role == Qt.TextAlignmentRole and self._continuous[index.column()]:
            return Qt.AlignRight | Qt.AlignVCenter
        else:
            return super().data(index, role)

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            var = super().headerData(
                section, orientation, TableModel.VariableRole)
            if var is None:
                return super().headerData(
                    section, orientation, Qt.DisplayRole)

            lines = []
            if self._header_flags & RichTableModel.Name:
                lines.append(var.name)
            if self._header_flags & RichTableModel.Labels:
                lines.extend(str(var.attributes.get(label, ""))
                             for label in self._labels)
            return "\n".join(lines)
        elif orientation == Qt.Horizontal and \
                role == RichTableModel.LabelsItemsRole:
            var = super().headerData(
                section, orientation, TableModel.VariableRole)
            if var is None:
                return []
            return [(label, var.attributes.get(label))
                    for label in self._labels]
        elif orientation == Qt.Horizontal and role == Qt.DecorationRole and \
                self._header_flags & RichTableModel.Icon:
            var = super().headerData(
                section, orientation, TableModel.VariableRole)
            if var is not None:
                return gui.attributeIconDict[var]
            else:
                return None
        else:
            return super().headerData(section, orientation, role)

    def setRichHeaderFlags(self, flags):
        if flags != self._header_flags:
            self._header_flags = flags
            self.headerDataChanged.emit(
                Qt.Horizontal, 0, self.columnCount() - 1)

    def richHeaderFlags(self):
        return self._header_flags


# This is used for sub-setting large (SQL) models. Largely untested probably
# broken.
class TableSliceProxy(QIdentityProxyModel):
    def __init__(self, parent=None, rowSlice=slice(0, None, 1), **kwargs):
        super().__init__(parent, **kwargs)
        self.__rowslice = slice(0, None, 1)
        self.setRowSlice(rowSlice)

    def setRowSlice(self, rowslice):
        if rowslice.step is not None and rowslice.step != 1:
            raise ValueError("invalid stride")

        if self.__rowslice != rowslice:
            self.beginResetModel()
            self.__rowslice = rowslice
            self.endResetModel()

    def index(
            self, row: int, column: int, _parent: QModelIndex = QModelIndex()
    ) -> QModelIndex:
        return self.createIndex(row, column)

    def parent(self, _child: QModelIndex) -> QModelIndex:
        return QModelIndex()

    def sibling(self, row: int, column: int, _idx: QModelIndex) -> QModelIndex:
        return self.index(row, column)

    def mapToSource(self, proxyindex):
        model = self.sourceModel()
        if model is None or not proxyindex.isValid():
            return QModelIndex()

        row, col = proxyindex.row(), proxyindex.column()
        row = row + self.__rowslice.start
        if 0 <= row < model.rowCount():
            return model.createIndex(row, col)
        else:
            return QModelIndex()

    def mapFromSource(self, sourceindex):
        model = self.sourceModel()
        if model is None or not sourceindex.isValid():
            return QModelIndex()
        row, col = sourceindex.row(), sourceindex.column()
        row = row - self.__rowslice.start
        if 0 <= row < self.rowCount():
            return self.createIndex(row, col)
        else:
            return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0
        count = super().rowCount()
        start, stop, step = self.__rowslice.indices(count)
        assert step == 1
        return stop - start