Skip to content

header_view

AYHeaderStyleOption dataclass

Source code in client/ayon_core/ui/components/header_view.py
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
@dataclass
class AYHeaderStyleOption:
    color: QColor
    bg_color: QColor
    border_color: QColor
    border_width: int
    padding: tuple[int, int]
    sort_indicator_icon: str | None
    sort_indicator_color: QColor
    sort_indicator_icon_size: int

    @classmethod
    def from_style(cls, style_dict: dict[str, Any]) -> AYHeaderStyleOption:
        """Create a AYHeaderStyleOption instance from a style dictionary.

        Args:
            style_dict: Dictionary containing style properties.

        """
        padding = style_dict.get("padding", [4, 8])
        v_pad = h_pad = int(padding[0])
        if len(padding) > 1:
            h_pad = int(padding[1])
        return cls(
            color=QColor(style_dict.get("color", "#c1c7ce")),
            bg_color=QColor(style_dict.get("background-color", "#272d35")),
            border_color=QColor(style_dict.get("border-color", "#41474d")),
            border_width=style_dict.get("border-width", 1),
            padding=(v_pad, h_pad),
            sort_indicator_icon=style_dict.get("sort-indicator-icon"),
            sort_indicator_color=QColor(
                style_dict.get("sort-indicator-color", "#ffffff")
            ),
            sort_indicator_icon_size=style_dict.get(
                "sort-indicator-size", 16
            ),
        )

from_style(style_dict) classmethod

Create a AYHeaderStyleOption instance from a style dictionary.

Parameters:

Name Type Description Default
style_dict dict[str, Any]

Dictionary containing style properties.

required
Source code in client/ayon_core/ui/components/header_view.py
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
@classmethod
def from_style(cls, style_dict: dict[str, Any]) -> AYHeaderStyleOption:
    """Create a AYHeaderStyleOption instance from a style dictionary.

    Args:
        style_dict: Dictionary containing style properties.

    """
    padding = style_dict.get("padding", [4, 8])
    v_pad = h_pad = int(padding[0])
    if len(padding) > 1:
        h_pad = int(padding[1])
    return cls(
        color=QColor(style_dict.get("color", "#c1c7ce")),
        bg_color=QColor(style_dict.get("background-color", "#272d35")),
        border_color=QColor(style_dict.get("border-color", "#41474d")),
        border_width=style_dict.get("border-width", 1),
        padding=(v_pad, h_pad),
        sort_indicator_icon=style_dict.get("sort-indicator-icon"),
        sort_indicator_color=QColor(
            style_dict.get("sort-indicator-color", "#ffffff")
        ),
        sort_indicator_icon_size=style_dict.get(
            "sort-indicator-size", 16
        ),
    )

AYHeaderView

Bases: StyleMixin, QHeaderView

Source code in client/ayon_core/ui/components/header_view.py
 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
class AYHeaderView(StyleMixin, QHeaderView):
    def __init__(
        self,
        orientation: Qt.Orientation,
        parent: QWidget | None = None,
        style_model: StyleData | None = None,
        variant: str = "default",
    ) -> None:
        super().__init__(orientation, parent)

        self.setStretchLastSection(True)

        self._style_model = style_model
        self._variant_str = variant
        # Keys of columns the user has flagged as *pinned* (kept at the
        # left in the visual order).  Phase 1 only roundtrips this set;
        # visual freeze rendering is added in Phase 3.
        self._pinned_keys: set[str] = set()

        # Set default height
        header_style = self._style_model.get_style(
            "AYHeaderView", self._variant_str
        )
        height = header_style.get("height", 36)
        self.setFixedHeight(height)

    def pinned_keys(self) -> set[str]:
        """Return a copy of the set of pinned column keys.

        Returns:
            A new ``set[str]`` so callers can mutate the result freely.
        """
        return set(self._pinned_keys)

    def set_pinned_keys(self, keys: set[str]) -> None:
        """Replace the set of pinned column keys.

        Args:
            keys: New set of pinned keys.  A defensive copy is taken.
        """
        self._pinned_keys = set(keys)

    def paintSection(
        self,
        painter: QPainter,
        rect: QRect,
        logical_index: int,
    ) -> None:
        """Paint a single header section directly with QPainter.

        Falls back to the base implementation when no style model is
        available.

        Args:
            painter: The painter to draw with.
            rect: The bounding rectangle for this section.
            logical_index: The logical index of the section.
        """
        if self._style_model is None:
            super().paintSection(painter, rect, logical_index)
            return

        header_style = self._style_model.get_style(
            "AYHeaderView", self._variant_str
        )
        option = AYHeaderStyleOption.from_style(header_style)

        painter.save()
        painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
        painter.setClipRect(rect)

        # Draw cell background and border
        painter.setBrush(QBrush(option.bg_color))
        painter.setPen(
            QPen(option.border_color, option.border_width)
        )
        painter.drawRect(rect)

        # Label text
        font = self.font()
        font.setWeight(QFont.Weight.DemiBold)
        painter.setPen(option.color)
        painter.setFont(font)

        model = self.model()
        if model is not None:
            label = model.headerData(
                logical_index,
                self.orientation(),
                Qt.ItemDataRole.DisplayRole,
            )
            if label is not None:
                content_rect = self._get_content_rect(rect, option)
                painter.drawText(
                    content_rect,
                    Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft,
                    str(label),
                )

        # Sort indicator
        if (
            self.isSortIndicatorShown()
            and self.sortIndicatorSection() == logical_index
        ):
            self._paint_sort_indicator(
                painter,
                rect,
                logical_index,
                option
            )

        painter.restore()

    def _get_content_rect(
        self, rect: QRect, option: AYHeaderStyleOption
    ) -> QRect:
        v_pad, h_pad = option.padding
        return rect.adjusted(h_pad, v_pad, -h_pad, -v_pad)

    def _paint_sort_indicator(
        self,
        painter: QPainter,
        rect: QRect,
        logical_index: int,
        option: AYHeaderStyleOption,
    ) -> None:
        content_rect = self._get_content_rect(rect, option)
        is_sortable = True
        model = self.model()
        if (
            isinstance(model, PaginatedTableModel)
            and 0 <= logical_index < len(model.columns)
        ):
            is_sortable = model.columns[logical_index].sortable

        if not is_sortable:
            painter.restore()
            return

        _v_pad, h_pad = option.padding
        order = self.sortIndicatorOrder()
        icon_name = option.sort_indicator_icon
        if not icon_name:
            arrow = "▲" if order == Qt.SortOrder.AscendingOrder else "▼"
            painter.drawText(
                content_rect,
                Qt.AlignmentFlag.AlignVCenter
                | Qt.AlignmentFlag.AlignRight,
                arrow,
            )
            return

        icon = get_icon(
            icon_name,
            color=option.sort_indicator_color,
        )
        size = option.sort_indicator_icon_size
        margin = int((rect.height() - size) * 0.5)
        pixmap = icon.pixmap(size, size)
        target = rect.adjusted(
            max(rect.width() - (size + h_pad), 0),
            margin,
            -h_pad,
            -margin,
        )

        if order == Qt.SortOrder.AscendingOrder:
            painter.save()
            center = target.center()
            painter.translate(center)
            painter.rotate(180)
            painter.translate(-center)
            painter.drawPixmap(target, pixmap)
            painter.restore()
        else:
            painter.drawPixmap(target, pixmap)

paintSection(painter, rect, logical_index)

Paint a single header section directly with QPainter.

Falls back to the base implementation when no style model is available.

Parameters:

Name Type Description Default
painter QPainter

The painter to draw with.

required
rect QRect

The bounding rectangle for this section.

required
logical_index int

The logical index of the section.

required
Source code in client/ayon_core/ui/components/header_view.py
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def paintSection(
    self,
    painter: QPainter,
    rect: QRect,
    logical_index: int,
) -> None:
    """Paint a single header section directly with QPainter.

    Falls back to the base implementation when no style model is
    available.

    Args:
        painter: The painter to draw with.
        rect: The bounding rectangle for this section.
        logical_index: The logical index of the section.
    """
    if self._style_model is None:
        super().paintSection(painter, rect, logical_index)
        return

    header_style = self._style_model.get_style(
        "AYHeaderView", self._variant_str
    )
    option = AYHeaderStyleOption.from_style(header_style)

    painter.save()
    painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
    painter.setClipRect(rect)

    # Draw cell background and border
    painter.setBrush(QBrush(option.bg_color))
    painter.setPen(
        QPen(option.border_color, option.border_width)
    )
    painter.drawRect(rect)

    # Label text
    font = self.font()
    font.setWeight(QFont.Weight.DemiBold)
    painter.setPen(option.color)
    painter.setFont(font)

    model = self.model()
    if model is not None:
        label = model.headerData(
            logical_index,
            self.orientation(),
            Qt.ItemDataRole.DisplayRole,
        )
        if label is not None:
            content_rect = self._get_content_rect(rect, option)
            painter.drawText(
                content_rect,
                Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft,
                str(label),
            )

    # Sort indicator
    if (
        self.isSortIndicatorShown()
        and self.sortIndicatorSection() == logical_index
    ):
        self._paint_sort_indicator(
            painter,
            rect,
            logical_index,
            option
        )

    painter.restore()

pinned_keys()

Return a copy of the set of pinned column keys.

Returns:

Type Description
set[str]

A new set[str] so callers can mutate the result freely.

Source code in client/ayon_core/ui/components/header_view.py
93
94
95
96
97
98
99
def pinned_keys(self) -> set[str]:
    """Return a copy of the set of pinned column keys.

    Returns:
        A new ``set[str]`` so callers can mutate the result freely.
    """
    return set(self._pinned_keys)

set_pinned_keys(keys)

Replace the set of pinned column keys.

Parameters:

Name Type Description Default
keys set[str]

New set of pinned keys. A defensive copy is taken.

required
Source code in client/ayon_core/ui/components/header_view.py
101
102
103
104
105
106
107
def set_pinned_keys(self, keys: set[str]) -> None:
    """Replace the set of pinned column keys.

    Args:
        keys: New set of pinned keys.  A defensive copy is taken.
    """
    self._pinned_keys = set(keys)