PyQt5 - 我如何在 QDateEdit 上禁用周末

Posted

技术标签:

【中文标题】PyQt5 - 我如何在 QDateEdit 上禁用周末【英文标题】:PyQt5 - How can I disable weekend on a QDateEdit 【发布时间】:2021-12-19 13:57:31 【问题描述】:

我正在使用 QDateEdit 选择特定日期,但我想取消周末,我只想选择工作日。

self.date =  QDateEdit(calendarPopup = True)
self.date.setDisplayFormat("dd-MM-yyyy")
self.date.setMinimumDate(QDate(2021,10,1))    
self.date.setDate(QDate(datetime.today()))

【问题讨论】:

“禁用”是什么意思? “不可选择”? 没错,我不想选择周末的日子 【参考方案1】:

QCalendarWidget 只允许接受单个范围的日期,并且可以选择该范围内的所有日期。

我能想到的唯一解决方案(除了从头开始创建自己的日历)是子类化 QCalendarWidget,访问底层 QTableView(显示日历的内容)并执行以下操作:

设置选择模式为NoSelection; 在视图(过滤按键)视图的视口(过滤鼠标事件)上安装事件过滤器; 实现dateForIndex 以检索表格特定索引处显示的日期; 只要鼠标位置的索引为工作日,就将选择模式设置为SingleSelection,否则将其设置回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)

此实现的唯一问题是,如果设置了 dateEditEnabled(这是默认设置),则无法阻止选择周末日期,除非连接到 activatedselectionChanged 信号并最终重置所选日期为有效日期。

【讨论】:

以上是关于PyQt5 - 我如何在 QDateEdit 上禁用周末的主要内容,如果未能解决你的问题,请参考以下文章

Qt/PyQt4/PySide QDateEdit 日历弹窗掉屏

Qt QDateEdit QDateTimeEdit

QT5-控件-QDateEdit 和 日期类QDate

2.6.2 QDate类与QDateEdit控件介绍(日期处理)

如何更新 PyQt5?

如何在 windows python 上安装 Pyqt5