[PyQt] QDateEdit, QStandardItemModel and empty date fields

J Barchan jnbarchan at gmail.com
Tue Mar 5 08:43:49 GMT 2019


On Sun, 3 Mar 2019 at 15:57, Maurizio Berti <maurizio.berti at gmail.com>
wrote:

> Il giorno sab 2 mar 2019 alle ore 21:02 Sibylle Koczian <
> nulla.epistola at web.de> ha scritto:
>
>> How can I get a QDateEdit to show an empty date, or how can I clear the
>> date it shows?
>
> [...]
>
> Of course I can replace the QDateEdit with a QLineEdit and work with a
>> string representation of the date column throughout. Is that the only
>> solution?
>>
>
> QDateTimeEdit and its inherited QDateEdit/QTimeEdit all inherit from
> QAbstractSpinBox, which contains a "private" QLineEdit.
> While that is not publicly accessible, you can just use findChild() to get
> its reference.
> Here's a small example you can implement in a model view by using a
> delegate and checking for the data in the setEditorData() method:
>
> class ClearableDateEdit(QtWidgets.QDateEdit):
>     def __init__(self, *args, **kwargs):
>         QtWidgets.QDateEdit.__init__(self, *args, **kwargs)
>         self.lineEdit = self.findChild(QtWidgets.QLineEdit)
>         self.clear()
>
>     def clear(self):
>         self.lineEdit.setText('')
>
>
> class Widget(QtWidgets.QWidget):
>     def __init__(self):
>         QtWidgets.QWidget.__init__(self)
>         layout = QtWidgets.QGridLayout()
>         self.setLayout(layout)
>         dateEdit = ClearableDateEdit()
>         layout.addWidget(dateEdit)
>         clearButton = QtWidgets.QPushButton('Clear date')
>         layout.addWidget(clearButton)
>         clearButton.clicked.connect(dateEdit.clear)
>
> There are some things you've to take into account, anyway, most
> importantly whenever the focus is get or lost or the cell is changed, which
> will require some control over the DateEdit widget *changed() signals and
> methods like dateTimeFromText(), and finally check everything before using
> setModelData.
>
> Maurizio
>
> --
> È difficile avere una convinzione precisa quando si parla delle ragioni
> del cuore. - "Sostiene Pereira", Antonio Tabucchi
> http://www.jidesk.net
> _______________________________________________
> PyQt mailing list    PyQt at riverbankcomputing.com
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>

You cannot have an empty QDateEdit, as per my topic long ago at
https://forum.qt.io/topic/86749/qdateedit-qabstractspinbox-blank-empty-value-problem
.

I had to deal with this.  I did not take @Maurizio's approach.  I do not
know what his solution does about QDateEdit.setData/data() for an empty
date, and other things.  Until now I didn't know you could get at its
QLineEdit.  For my part I chose to have a QLineEdit plus an associated ...
button to its right which leads to a modal dialog.  I paste extracts below,
feel free to cannibalise it if you want my approach.  If @Maurizio wishes
to criticize mine, that's fine, I will read and consider!

class JDateEdit(QWidget):
    # Class to display an "editable date" widget
    # It consists of a QLineEdit plus a "..." QPushButton
    # which brings up a (modal) dialog to pick a date to copy into the line edit
    # We have to use our own class and not the native Qt
"QDateEdit"+setCalendarPopup(True)
    # because that does not allow a "blank/empty" date in any form,
    # and we do need blank all over the place

    # class variable for "editingFinished" signal
    editingFinished = QtCore.pyqtSignal(name='editingFinished')

    def __init__(self, parent=None):
        super().__init__(parent)

        self.minimumDate = None
        self.maximumDate = None

        # lineEdit holds the date
        self.lineEdit = JLineEdit(self)
        # "..." button, when clicked will bring up a dialog with a
QCalendarWidget
        self.button = JDotDotDotButton(self)

        # connect self.lineEdit.editingFinished signal to
self.editingFinished signal
        self.lineEdit.editingFinished.connect(self.editingFinished)

        layout = QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.lineEdit)
        layout.addWidget(self.button)
        layout.addStretch(1)
        self.setLayout(layout)

        self.button.clicked.connect(self.doDialog)
        self.dialog = None

    def doDialog(self):
        self.dialog = JCalendarDialog(self.button)
        if self.minimumDate:
            self.dialog.calendar.setMinimumDate(self.minimumDate)
        if self.maximumDate:
            self.dialog.calendar.setMaximumDate(self.maximumDate)
        date = self.date()
        if date is not None:
            self.dialog.setSelectedDate(pyDateToQDate(date))
        pos = self.button.rect().topLeft()
        pos = self.button.mapToGlobal(pos)
        self.dialog.move(pos)
        if self.dialog.exec():
            date = qDateToPyDate(self.dialog.selectedDate())
            # copy the selected date to the line edit
            self.setDate(date)
            # and emit the editingFinished signal
            self.lineEdit.editingFinished.emit()
        self.dialog = None

    def setReadOnly(self, ro: bool):
        self.lineEdit.setReadOnly(ro)
        if ro:
            self.button.hide()
        else:
            self.button.show()

    def setDate(self, date: typing.Union[datetime.date, None]):
        # Set the date
        # Accepts None for the date, and sets it blank
        if date is None:
            self.lineEdit.setText("")
        else:
            self.lineEdit.setText(dateToStr(date))

    def date(self) -> typing.Union[datetime.date, None]:
        # Return the date
        # If the date is blank, or if it fails to parse in
parseDateSoft(), return None
        text = self.lineEdit.text()
        if text == "":
            return None
        return parseDateSoft(text)

    def dateOrWarn(self) -> typing.Union[datetime.date, None]:
        # Return the date, which *should* be filled in
        # If the date is blank, or if it fails to parse,
        # put up an ErrorMsgBox() and then return None
        text = self.lineEdit.text()
        return parseDateHard(text, self.lineEdit)

    def dateOrError(self) -> datetime.date:
        # Return the date, which *must* be filled in
        # If the date is blank, or if it fails to parse,
        # put up an ErrorMsgBox() and then raise an exception
        text = self.lineEdit.text()
        date = parseDateHard(text, self.lineEdit)
        if date is None:
            raise ValueError("Valid date required")
        return date

    def clear(self):
        self.lineEdit.clear()

    def setText(self, a0: str):
        self.lineEdit.setText(a0)

    def text(self) -> str:
        return self.lineEdit.text()

    def setMinimumDate(self, date: typing.Union[datetime.date, None]):
        self.minimumDate = date

    def setMaximumDate(self, date: typing.Union[datetime.date, None]):
        self.maximumDate = date



-- 
Kindest,
Jonathan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190305/3d63b148/attachment-0001.html>


More information about the PyQt mailing list