I'm using a QDateEdit to choose a specific date, but I would like to disbale weekends, I only want to choose week days.
self.date = QDateEdit(calendarPopup = True)
self.date.setDisplayFormat("dd-MM-yyyy")
self.date.setMinimumDate(QDate(2021,10,1))
self.date.setDate(QDate(datetime.today()))
QCalendarWidget only allows a single range of accepted dates, and all dates in that range can be selected.
The only solution I could think of (except for creating your own calendar from scratch) is to subclass QCalendarWidget, access the underlying QTableView (which is what shows the calendar) and do the following:
NoSelection
;dateForIndex
to retrieve the displayed date at a specific index of the table;SingleSelection
whenever the index at the mouse position is in work days, otherwise set it back to NoSelection
;class CalendarWidget(QtWidgets.QCalendarWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSelectionMode(self.NoSelection)
self.view = self.findChild(QtWidgets.QAbstractItemView, 'qt_calendar_calendarview')
self.view.installEventFilter(self)
self.view.viewport().installEventFilter(self)
def dateForIndex(self, index):
row = index.row()
column = index.column()
if self.horizontalHeaderFormat():
row -= 1
if self.verticalHeaderFormat():
column -= 1
if not 0 <= row <= 5 or not 0 <= column <= 6:
return QtCore.QDate()
day = index.data()
month = self.monthShown()
year = self.yearShown()
# day numbers bigger than 21 cannot be shown in the first 3 rows
if row <= 2 and day > 21:
month -= 1
if month <= 0:
month = 12
year -= 1
# day numbers smaller than 15 cannot be shown in the last 3 rows
elif row >= 3 and day < 15:
month += 1
if month >= 13:
month = 1
year += 1
date = QtCore.QDate(year, month, day)
if self.minimumDate() <= date <= self.maximumDate():
return date
return QtCore.QDate()
def moveCursor(self, key):
currentDate = self.dateForIndex(self.view.currentIndex())
delta = 1
if key == QtCore.Qt.Key_Up:
newDate = currentDate.addDays(-7)
elif key == QtCore.Qt.Key_Down:
newDate = currentDate.addDays(7)
elif key == QtCore.Qt.Key_Left:
newDate = currentDate.addDays(-1)
elif key == QtCore.Qt.Key_Right:
newDate = currentDate.addDays(1)
elif key == QtCore.Qt.Key_Home:
newDate = QtCore.QDate(currentDate.year(), currentDate.month(), 1)
delta = -1
elif key == QtCore.Qt.Key_End:
newDate = QtCore.QDate(currentDate.year(), currentDate.month(),
currentDate.daysInMonth())
delta = -1
elif key == QtCore.Qt.Key_PageUp:
newDate = currentDate.addMonths(-1)
delta = -1
elif key == QtCore.Qt.Key_PageDown:
newDate = currentDate.addMonths(1)
delta = -1
else:
return
newDate = max(self.minimumDate(), min(newDate, self.maximumDate()))
if currentDate != newDate:
# if it's a day of the weekend, add the delta until a work day is
# found; for Home/End/Page keys the delta is inverted, as we need to
# ensure that we stay in the days of the selected month, and if the
# function reaches a weekend it could skip a month
while newDate.dayOfWeek() > 5:
if newDate > currentDate:
newDate = newDate.addDays(delta)
else:
newDate = newDate.addDays(-delta)
if self.minimumDate() <= newDate <= self.maximumDate():
return newDate
def eventFilter(self, obj, event):
if (event.type() in (event.MouseButtonPress, event.MouseButtonRelease, event.MouseButtonDblClick)
and event.button() == QtCore.Qt.LeftButton):
index = self.view.indexAt(event.pos())
if index.isValid():
date = self.dateForIndex(index)
if date.dayOfWeek() <= 5:
self.setSelectionMode(self.SingleSelection)
else:
self.setSelectionMode(self.NoSelection)
elif event.type() == event.MouseMove and event.buttons() == QtCore.Qt.LeftButton:
index = self.view.indexAt(event.pos())
if index.isValid():
date = self.dateForIndex(index)
if not date.isValid() or date.dayOfWeek() > 5:
# ignore mouse move events for weekends
return True
elif event.type() == event.KeyPress:
newDate = self.moveCursor(event.key())
if newDate:
self.setSelectedDate(newDate)
return True
return super().eventFilter(obj, event)
The only issue with this implementation is that if the dateEditEnabled
is set (which is the default), there's no way to prevent selecting weekend days, except from connecting to the activated
and selectionChanged
signals and eventually reset the selected date to a valid day.