PyQt5 實現可空值的 QDateTimeEdit

一代咩神發表於2021-05-13

QDateTimeEdit 預設不允許為空,也就是不能存放空串 。網上搜尋了很久始終沒找到答案,自行研究下,發現重寫兩個方法可以實現可空值的 QDateTimeEdit

def validate(self, input: str, pos: int) -> Tuple[QValidator.State, str, int]
    """
    判斷 QDateTimeEdit 的輸入是否有效,無效則不會輸入到 QDateTimeEdit 中。

    返回值 QValidator.State 有三種狀態:
        QValidator.Acceptable:允許;
        QValidator.Intermediate:無法確定(即中間值);
        QValidator.Invalid:拒絕。
    str: 返回輸入框應該顯示的文字(非最終文字),預設為 input;
    int: 返回輸入框游標偏移量,預設為 pos。
    """

def textFromDateTime(self, dateTime: QDateTime) -> str
    """每當要顯示 dateTime 時會呼叫此函式,返回最終被顯示在輸入框中的內容。"""

知道這兩個方法的作用後開始一頓騷操作:

class DateTimeEdit(QDateTimeEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.lineEdit().clear()  # 初始化時讓輸入框為空

def validate(self, input: str, pos: int) -> Tuple[QValidator.State, str, int]:
    if not input:  # 允許接受空串
        return QValidator.Intermediate, '', 0
    return super().validate(input, pos)

def textFromDateTime(self, dateTime: QDateTime) -> str:
    return super().textFromDateTime(dateTime) if self.text() else ''

就這麼簡單!隨後你會驚奇的發現,如果初始化後不設定時間 ( setDatesetDateTime … 等方法 ) 的話,點選 步進按鈕彈出日曆按鈕 時,居然沒有文字顯示了?莫慌,只要再重寫 mousePressEvent 方法:

def mousePressEvent(self, evt: QMouseEvent) -> None:
    super().mousePressEvent(evt)
    if not self.text():
        self.setDateTime(QDateTime.currentDateTime())

即可!如果設定了 setCalendarPopup(True) ,並且要求在彈出日曆時先不設定文字,而是等選擇了日曆的時間後再設定,則需要額外定製。具體我說一下思路,可以根據需求微調。

因為設定 calendarWidget 的時間時 QDateTimeEdit 的文字也會跟著改變,反之也是如此 ( calendarWidget 時間也會隨著 QDateTimeEdit 變化 ) 。可以在 mousePressEvent 方法中阻止 calendarWidget 發射訊號,為其設定上指定的日期後,再解除阻止。然後在連線了 calendarWidget 的訊號方法 ( selectionChangedclicked … 等) 中自行設定 QDateTimeEdit 的顯示內容。為保證訊號只連線一次 ,可以重寫 setCalendarPopup 方法:

def mousePressEvent(self, evt: QMouseEvent) -> None:
    super().mousePressEvent(evt)
    if not self.text():
        # 阻止 calendarWidget 自行改變 QDateTimeEdit 的顯示時間
        (w := self.calendarWidget()).blockSignals(True)
        w.setSelectedDate(QDate.currentDate())
        w.blockSignals(False)

def setCalendarPopup(self, enable: bool) -> None:
    super().setCalendarPopup(enable)
    if enable:
        # 保證訊號只連線一次
        if not (w := self.calendarWidget()).receivers(w.clicked):
            w.clicked.connect(self.setDate)

另附上便捷設定 QDateTimeEdit 時間的方法:

def setDateTime(self, dateTime: Union[QDateTime, str]):
    self._set_time(dateTime, QDateTime)

def setDate(self, date: Union[QDate, str]):
    self._set_time(date, QDate)

def setTime(self, time: Union[QTime, str]):
    self._set_time(time, QTime)

def _set_time(self, t: Union[QDate, QTime, QDateTime], cls):
    fmt = self.displayFormat()
    t = cls.fromString(t, fmt) if isinstance(t, str) else t
    # 這裡不用擔心給定的字串會顯示出錯,
    # 因為中間還會經過 validate、textFromDateTime 方法,
    # 驗證後返回最終被顯示的文字,驗證無效則不會顯示在輸入框中。
    self.lineEdit().setText(t.toString(fmt))
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章