QStyledItemDelegate in QTableView ist falsch ausgerichtet

Post a reply

Smilies
:) :( :oops: :chelo: :roll: :wink: :muza: :sorry: :angel: :read: *x) :clever:
View more smilies

BBCode is ON
[img] is ON
[flash] is OFF
[url] is ON
Smilies are ON

Topic review
   

Expand view Topic review: QStyledItemDelegate in QTableView ist falsch ausgerichtet

by Anonymous » 06 Jan 2025, 06:02

Ich möchte eine Liste von Dateien mit Sternebewertung in einer QTableView anzeigen. Dazu verwende ich den folgenden Delegaten:

Code: Select all

class StarRatingDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)

def paint(self, painter, option, index):
file: File = index.data(Qt.UserRole)
star_rating_widget = StarRatingWidget(10, self.parent())
star_rating_widget.set_rating(file.rating)
star_rating_widget.render(painter, option.rect.topLeft())

Code: Select all

StarRatingWidget
Ist ein einfaches QWidget, das 5 QLables in einem QHBoxLayout enthält.
Das funktioniert bisher alles, aber alle StarRatingWidgets werden nach links oben verschoben:
Image

In der ersten Spalte wird die Bewertung als Zahl angezeigt. Sie können sehen, dass alle Sterne leicht nach links und etwas mehr als eine Zeilenhöhe nach oben verschoben sind.
Tests haben ergeben, dass option.rect die Koordinaten mit zurückgibt (0, 0) ist die obere linke Ecke der ersten Zelle, aber star_rating_widget.render behandelt die Koordinaten so, dass (0, 0) die obere linke Ecke des Fensters ist. Die Widgets werden also um den Abstand zwischen Tabelle und Fensterrand und zusätzlich um die Höhe des Tabellenkopfes verschoben.
Bevor jemand fragt, hier der vollständige Code. Zum Ausführen ist pyside6 erforderlich.

Code: Select all

#!/usr/bon/env python

from PySide6.QtCore import Qt, Signal, QAbstractItemModel, QModelIndex, QEvent
from PySide6.QtGui import QMouseEvent
from PySide6.QtWidgets import QApplication, QLabel, QTableView, QMainWindow, QSizePolicy, QHBoxLayout, QWidget, QStyledItemDelegate

class MainWindow(QMainWindow):
def __init__(self):
super().__init__()

self.setGeometry(100, 100, 250, 600)

self.central_widget = QWidget()
self.main_layout = QHBoxLayout()
self.central_widget.setLayout(self.main_layout)
self.setCentralWidget(self.central_widget)

self.list = QTableView()
self.list.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)
self.list.setSelectionMode(QTableView.SelectionMode.SingleSelection)
self.list.horizontalHeader().setStretchLastSection = True
self.list.verticalHeader().hide()
self.list.show_grid = False
self.list.setItemDelegateForColumn(1, StarRatingDelegate(self.list))
self.list.setModel(ListModel())
self.main_layout.addWidget(self.list)

class ListModel(QAbstractItemModel):
def __init__(self):
super().__init__()
self.horizontal_header_labels = ['Number', 'Stars']

def rowCount(self, parent=QModelIndex()):
return 50

def columnCount(self, parent=QModelIndex()):
return len(self.horizontal_header_labels)

def data(self, index, role):
if not index.isValid():
return None
if role == Qt.DisplayRole:
rating = (index.row() - 2) % 7
return None if rating >= 5 else rating + 1
return None

def headerData(self, section, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.horizontal_header_labels[section]
return None

def index(self, row, column, parent=QModelIndex()):
if self.hasIndex(row, column, parent):
return self.createIndex(row, column)
return QModelIndex()

def parent(self, index):
return QModelIndex()

class StarRatingWidget(QWidget):
rating_changed = Signal(int)

def __init__(self, font_size, parent=None):
super().__init__(parent)
self.rating = 0
self.hovered_star: int|None = None
self.stars: List[QLabel] = []
self.font_size: int = font_size
self.init_ui()

def star_mouse_event(self, i: int):
def event(event: QMouseEvent):
if event.type() == QEvent.Enter:
self.hovered_star = i
self.update()
elif event.type() == QEvent.Leave:
self.hovered_star = None
self.update()
return event

def init_ui(self):
layout = QHBoxLayout()
for i in range(5):
star = QLabel()
star.mousePressEvent = lambda _, i=i: self.set_rating(i + 1)
star.enterEvent = self.star_mouse_event(i)
star.leaveEvent = self.star_mouse_event(i)
star.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
layout.addWidget(star)
self.stars.append(star)
self.setLayout(layout)
self.update()

def set_rating(self, rating: int|None):
if rating != self.rating:
self.rating = rating
self.update()
self.rating_changed.emit(rating)

def update(self):
for i, star in enumerate(self.stars):
rating = self.rating if self.rating is not None else 0
if i < rating:
star.setText('★')
else:
star.setText('☆')

if self.rating is None:
color = 'gray'
weight = 'normal'
elif i == self.hovered_star:
color = 'blue'
weight = 'bold'
else:
color = 'yellow'
weight = 'normal'

star.setStyleSheet(f'font-size: {self.font_size}px; color: {color};  font-weight: {weight}')

class StarRatingDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)

def paint(self, painter, option, index):
rating = index.data()
star_rating_widget = StarRatingWidget(10, self.parent())
star_rating_widget.set_rating(rating)
star_rating_widget.render(painter, option.rect.topLeft())

def main():
app = QApplication([])
main_window = MainWindow()
main_window.show()
QApplication.exec()

if __name__ == '__main__':
main()

Top