Search code examples
pythonpyside2qitemdelegateqcalendarwidget

The QItemDelegate is not rendered in the same way across columns QCalendarWidget


I try to make a corner in the cells of my QCalendarWidget. So I made a QItemDelegate in which I draw my triangle.
As the screenshot shows, the delegate works fine on the first column, but is completely broken on the others.
I don't understand where this comes from (I'm new to Qt, so maybe I did something wrong).
Here is the code:

# ---------------------------------
# ------ CalendarDayDelegate ------
class CalendarDayDelegate(QItemDelegate):
    def __init__(self, parent=None, projects=None):
        super(CalendarDayDelegate, self).__init__(parent=parent)
        self.projects = projects

    def paint(self, painter, option, index):
        painter._date_flag = index.row() > 0
        super(CalendarDayDelegate, self).paint(painter, option, index)

        if painter._date_flag:
            rect = option.rect
            corner = QPolygon([
                rect.topRight(),
                QPoint(rect.center().x() + (rect.center().x() / 2), rect.top()),
                QPoint(rect.bottomRight().x(), rect.center().y())
            ])

            painter.save()
            painter.setRenderHint(painter.Antialiasing)
            painter.setBrush(QBrush(QColor(Qt.darkGreen)))
            painter.setPen(QPen(QColor(Qt.darkGreen)))
            painter.drawPolygon(corner)
            painter.restore()

    def drawDisplay(self, painter, option, rect, text):
        if painter._date_flag:
            option.displayAlignment = Qt.AlignTop | Qt.AlignLeft
        super(CalendarDayDelegate, self).drawDisplay(painter, option, rect, text)

# ----------------------
# ------ Calendar ------
class MonthReviewCalendar(QCalendarWidget):
    def __init__(self, parent=None):
        super(MonthReviewCalendar, self).__init__(parent=parent)
        self._init_calendar()

    def _init_calendar(self):
        self.setVerticalHeaderFormat(
            self.verticalHeaderFormat().NoVerticalHeader
        )
        self.setFirstDayOfWeek(Qt.Monday)

        self.calendar_view = self.findChild(
            QTableView, "qt_calendar_calendarview"
        )
        self.calendar_delegate = CalendarDayDelegate(
            projects=self._raw_projects
        )
        self.calendar_view.setItemDelegate(self.calendar_delegate)

And the screenshot
screenshot of QCalendarWidget


Solution

  • The issue is with the second point of the polygon: since you're setting it based on the x of the center, every new column of the table will have a different coordinate reference.

    If you just print the rect, you'll see the result:

        def paint(self, painter, option, index):
            # ...
            if index.row() == 1:
                print(index.column(), rect.center())
    

    Which will output:

    >>> 0 PyQt5.QtCore.QPoint(15, 41)
    >>> 1 PyQt5.QtCore.QPoint(46, 41)
    >>> 2 PyQt5.QtCore.QPoint(77, 41)
    >>> 3 PyQt5.QtCore.QPoint(108, 41)
    >>> 4 PyQt5.QtCore.QPoint(139, 41)

    Since you're adding half of the "center x" coordinate everytime, the result is that the second point gets far and far away everytime you get a new rectangle based on a non-zero column.

    If you want that corner to start at 75% of the rect width, just use a the top right corner as a reference, and then subtract/add relative coordinates (QPoint) from/to it:

            ref = rect.topRight()
            corner = QPolygon([
                ref, 
                ref + QPoint(-rect.width() / 4, 0), 
                ref + QPoint(0, rect.height() / 2)
            ])