Как отключить другие месяцы дней в QCalendarWidget
Я хочу отключить возможность того, что пользователи могут нажимать на дни, не относящиеся к текущему месяцу, в QCalendarWidget
, поэтому я вложил в виджет виджет, чтобы сделать это. До сих пор я мог сделать так, чтобы те дни вообще не отображали текст (отлично). Это код:
class QCustomCalendar(QCalendarWidget):
"""Create my own Calendar with my own options."""
def __init__(self, parent=None):
"""Initializing functions"""
QCalendarWidget.__init__(self, parent)
self.setEnabled(True)
self.setGeometry(QRect(0, 0, 320, 250))
self.setGridVisible(False)
self.setHorizontalHeaderFormat(QCalendarWidget.SingleLetterDayNames)
self.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader)
self.setNavigationBarVisible(True)
self.setDateEditEnabled(True)
self.setObjectName("calendarWidget")
def paintCell(self, painter, rect, date):
"""Sub-class this and repaint the cells"""
# Render only this-month days
month = "{0}-{1}".format(str(self.yearShown()), str(self.monthShown()).zfill(2))
day = str(date.toPython())
if not day.startswith(month):
return
QCalendarWidget.paintCell(self, painter, rect, date)
Однако, если я нажимаю на день без рендеринга, он все равно считает и запускает clicked
событие. Пример: я сфотографировал красный квадрат, где щелкнув по нему, он выбрал бы 4 июня (хотя на скриншоте мы находимся в мае).
Как отключить те дни, чтобы не быть выбираемыми?
Я старался setDateRange
на currentPageChanged
событие, но оно не работает, как ожидалось:
def __init__(self, parent=None):
# some code
self.currentPageChanged.connect(self.store_current_month)
self.clicked.connect(self.calendar_itemchosen)
def store_current_month(self):
self.CURRENT_MONTH = "{0}-{1}".format(str(self.yearShown()), str(self.monthShown()).zfill(2))
def calendar_itemchosen(self):
day = str(self.selectedDate().toPython())
print(day)
if day.startswith(self.CURRENT_MONTH):
selection = self.selectedDate()
# some code
self.close()
Результат нажатия на этот красный квадрат с этим кодом:
2018-06
2018-06-04
Итак, я думаю, Qt сначала вызывает currentPageChanged
событие, когда вы выбираете дату на другой месяц. setDateRange
не будет работать, потому что если я добавлю его, чтобы ограничить выбор только этим месяцем, то кнопки в верхней части календаря "перейти к следующему или предыдущему месяцу" не будут работать, и мне нужно, чтобы пользователь мог менять месяцы, Я просто не хочу, чтобы в календаре показывались дни, которые не принадлежат этой странице месяца.
1 ответ
Одним из решений является фильтрация события mousePressEvent объекта QTableView, который есть у QCalendarWidget внутри. Для этого мы используем фильтр событий:
from PyQt5 import QtCore, QtWidgets
class CalendarWidget(QtWidgets.QCalendarWidget):
def __init__(self, parent=None):
super(CalendarWidget, self).__init__(parent, gridVisible=False,
horizontalHeaderFormat=QtWidgets.QCalendarWidget.SingleLetterDayNames,
verticalHeaderFormat=QtWidgets.QCalendarWidget.NoVerticalHeader,
navigationBarVisible=True,
dateEditEnabled=True)
self.setEnabled(True)
self.setGeometry(QtCore.QRect(0, 0, 320, 250))
self.clicked.connect(print)
self.table_view = self.findChild(QtWidgets.QTableView, "qt_calendar_calendarview")
self.table_view.viewport().installEventFilter(self)
self.setFirstDayOfWeek(QtCore.Qt.Monday)
def referenceDate(self):
refDay = 1
while refDay <= 31:
refDate = QtCore.QDate(self.yearShown(), self.monthShown(), refDay)
if refDate.isValid(): return refDate
refDay += 1
return QtCore.QDate()
def columnForDayOfWeek(self, day):
m_firstColumn = 1 if self.verticalHeaderFormat() != QtWidgets.QCalendarWidget.NoVerticalHeader else 0
if day < 1 or day > 7: return -1
column = day - int(self.firstDayOfWeek())
if column < 0:
column += 7
return column + m_firstColumn
def columnForFirstOfMonth(self, date):
return (self.columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7
def dateForCell(self, row, column):
m_firstRow = 1 if self.horizontalHeaderFormat() != QtWidgets.QCalendarWidget.NoHorizontalHeader else 0
m_firstColumn = 1 if self.verticalHeaderFormat() != QtWidgets.QCalendarWidget.NoVerticalHeader else 0
rowCount = 6
columnCount = 7
if row < m_firstRow or row > (m_firstRow + rowCount -1) or column < m_firstColumn or column > (m_firstColumn + columnCount -1):
return QtCore.QDate()
refDate = self.referenceDate()
if not refDate.isValid():
return QtCore.QDate()
columnForFirstOfShownMonth = self.columnForFirstOfMonth(refDate)
if (columnForFirstOfShownMonth - m_firstColumn) < 1:
row -= 1
requestedDay = 7*(row - m_firstRow) + column - columnForFirstOfShownMonth - refDate.day() + 1
return refDate.addDays(requestedDay)
def eventFilter(self, obj, event):
if obj is self.table_view.viewport() and event.type() == QtCore.QEvent.MouseButtonPress:
ix = self.table_view.indexAt(event.pos())
date = self.dateForCell(ix.row(), ix.column())
d_start = QtCore.QDate(self.yearShown(), self.monthShown(), 1)
d_end = QtCore.QDate(self.yearShown(), self.monthShown(), d_start.daysInMonth())
if d_start > date or date > d_end:
return True
return super(CalendarWidget, self).eventFilter(obj, event)
def paintCell(self, painter, rect, date):
d_start = QtCore.QDate(self.yearShown(), self.monthShown(), 1)
d_end = QtCore.QDate(self.yearShown(), self.monthShown(), d_start.daysInMonth())
if d_start <= date <= d_end:
super(CalendarWidget, self).paintCell(painter, rect, date)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = CalendarWidget()
w.show()
sys.exit(app.exec_())