0

I am trying to override QCalendarWidget's paintCell() method to paint a red outline on today's date cell and draw events that will be defined by the user. For my calendar, I used a QItemDelegate to change the alignment of the date flags so I would have more room to draw events. However, I can't seem to get both the QItemDelegate and paintCell() working together. I can only have one or the other work at one time. If I try to do both, only the Delegate shows and nothing is painted.

from PySide2.QtWidgets import QMainWindow, QCalendarWidget, QApplication, QItemDelegate, QTableView
from PySide2.QtGui import QPen
from PySide2.QtCore import Qt
import sys


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__()
        self.calendar = CustomCalendar()
        self.calendarview = self.calendar.findChild(QTableView, "qt_calendar_calendarview")
        self.calendardelegate = CalendarItemDelegate(self.calendarview)
        self.calendarview.setItemDelegate(self.calendardelegate)
        self.setCentralWidget(self.calendar)
        self.show()


class CustomCalendar(QCalendarWidget):
    def __init__(self, parent=None):
        super().__init__()

    def paintCell(self, painter, rect, date):
        QCalendarWidget.paintCell(self, painter, rect, date)
        pen = QPen()
        pen.setColor(Qt.red)
        painter.setPen(pen)
        if date == date.currentDate():
            painter.save()
            painter.drawRect(rect.adjusted(0, 0, -1, -1))
            painter.restore()


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

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


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

How can I get both to work at once?

enter image description here

Delegate

enter image description here

PaintCell

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Streeter
  • 23
  • 5

1 Answers1

1

When a custom item delegate is set on the calendar widget, the default paintCell is just ignored, as it's responsibility of the (private) delegate to call it.

Since you're using a QItemDelegate, you can take advantage of the drawFocus() function and check if the option.state has the State_Selected flag set (technically, you could do it in the drawDisplay() too, since the function is called anyway and the option has the same values):

    def drawFocus(self, painter, option, rect):
        super().drawFocus(painter, option, rect)
        if option.state & QStyle.State_Selected:
            painter.save()
            painter.setPen(Qt.red)
            painter.drawRect(rect.adjusted(0, 0, -1, -1))
            painter.restore()
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Calling paintCell from the delegate interests me. How could I do that? – Streeter Jan 07 '21 at 18:49
  • 1
    Qt does that by using an internal delegate: it has a reference to the calendar widget, so it gets the date using the current month, the row, and the column, then calls `paintCell()` with the painter, the `option.rect` and the date computed before. – musicamante Jan 08 '21 at 16:11
  • 1
    @Streeter do note that the `paintCell` *actually* calls back the delegate painting, so it doesn't make a lot of sense. What you could do, instead, is to find out if the current index *is* the current date. It's a bit tricky, but it can be done by reimplementing in python the original C++ implementation. – musicamante Jan 08 '21 at 16:54