PyQt5 实现可空值的 QDateTimeEdit

Python
501
0
0
2022-04-14

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, '', 0return 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))