【问题标题】:How to disable other months days in QCalendarWidget如何在 QCalendarWidget 中禁用其他月份
【发布时间】:2019-05-12 04:06:20
【问题描述】:

我的目标是禁止用户在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 事件。示例:我对一个红色方块进行了 photoshop 处理,点击它,它会选择 6 月 4 日(即使我们在屏幕截图中是 5 月)。

如何让那些日子无法选择?

我在currentPageChanged 事件上尝试了setDateRange,但它没有按预期工作:

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 不起作用,因为如果我添加它以将选择限制在本月,那么日历顶部的“转到下个月或上个月”按钮将不起作用,我需要用户能够更改月份。我只是不希望日历显示不属于本月页面的日期。

【问题讨论】:

  • 我了解您想要什么,但是如果其他月份或日期被禁用(这就是您想要的),当您按下顶部用于 go 的按钮时,您会观察到什么到下个月还是上个月
  • 什么都不会发生,如果我使用setDateRange 这些按钮将被禁用。我希望这些按钮不会被禁用。
  • 我想你不明白我的意思,我明白这不是解决方案,我的意思是这些按钮也必须被禁用,因为它们根本不会为用户服务,因为下一个或上一个页面不会有任何内容。
  • 既然你提到了,那是有道理的。那我应该采取什么方法呢?
  • 我的想法是禁用按钮以及其他月份的剩余天数。

标签: python python-3.x pyqt5 pyside2 qcalendarwidget


【解决方案1】:

一种解决方案是过滤 QCalendarWidget 内部具有的 QTableView 的 mousePressEvent 事件。为此,我们使用事件过滤器:

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_())

【讨论】:

  • 到目前为止它工作得很好,除了一个案例。每个月的第一天是不可选择的(下个月的第一天是可选择的,它不应该)。我认为这是因为if d_start &lt;= date &lt;= d_end 所以我会自己修复它。只是让你知道。一旦我确定这是我需要的,我就会接受这个答案是有效的。
  • @Saelyth 你确定吗?,我没有那个问题,你可以说出产生这个问题的确切日期你测试过我的代码或对你的代码的改编吗?
  • 我不得不修改代码,因为我的日历非常复杂(它使用大量 QPainterPath、RoundedRect 和类似的东西来设置特定日期的特定注释)。我对这个答案的第一条评论是错误的。 paintCell 函数按预期工作。问题一定出在其他地方,我一直在调查。
  • @Saelyth 我看到你的一周从星期一(Lunes)开始,但我的星期从星期日(Domingo)开始,我认为这是错误的。 :-)
  • 奇怪的是,我不得不将它转换为整数才能工作:column = day - int(self.firstDayOfWeek()) 我想这是因为我在 Windows 上,或者我不明白。还有 PySide 5.12。现在一切都很好。
猜你喜欢
  • 2023-01-08
  • 1970-01-01
  • 2022-07-11
  • 1970-01-01
  • 2018-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多