QDateEdit calendar popup

30.9k Views Asked by At

I'm trying to get a QDateEdit to allow the QCalendarWidget to show when requested (rather than just on clicking the down arrow). For example, somewhere in my class I should be able to say:

ui.datepicker.showCalendar()

and it should load up the calendar that appears right below the date picker.

It looks like I need to sub-class QDateEdit, as this doesn't work:

QDateEdit *de = new QDateEdit();
de->calendarWidget()->show();

I've also tried sending keyboard commands as dictated when you go through the QDateTimeEdit.cpp source for Qt, but seems my keyboard shortcuts are disabled or something.

Any ideas on what I have to do to sub-class to get this to work? I was thinking of something like:

class MyDateEdit : QDateEdit
{
  Q_OBJECT

protected:
  void mouseEvent(QEvent *event) {
    this.calendarWidget().show();
  }
};

But alas that also doesn't seem to compile in or work correctly.

5

There are 5 best solutions below

0
On BEST ANSWER

I was able to figure it out on my own - still no sure how to get QDateEdit to work properly, but I used a QLineEdit and it suited my needs. Just connect QCalendarWidget's "onClick(QDate)" to a slot you create that does a:

setText(date.toString("M/d/yyyy"));
ui->calendar->hide();

Then add an event filter to the QLineEdit using the "OnFocusIn" event that does a "ui->calendar->show();" See: Get a notification/event/signal when a Qt widget gets focus

1
On

Enable "setCalendarPopup ( bool enable )" in QDateTimeEdit allows to popup the calendar

0
On

Here is my hacky approach to the issue. After fighting for quite a while to have something clean, I read the source code of QDateEditor (which in fact is just a simplified QDateTimeEditor) and it seems to be no clean solution. The following is code for toggle() rather than show(), but still:

// Enable the calendar popup
date_editor->setCalendarPopup(true);

// Show the calendar popup by default
// There seems to be no proper interface to achieve that
// Fake a mouse click on the right-hand-side button
QPointF point = date_editor->rect().bottomRight() - QPointF{5, 5};
QCoreApplication::postEvent(
    date_editor,
    new QMouseEvent(QEvent::MouseButtonPress, point, Qt::LeftButton,
                    Qt::LeftButton, Qt::NoModifier));

Using something like this you can keep relying on the editor's validation features.

BTW, another annoying thing about the built-in editor that makes a QLineEdit tempting is that (at least in my case) the keyboard cursor is not shown by default. This is very confusing. To solve this I did:

// Select a section so that the cursor is be visible
date_editor->setSelectedSection(QDateTimeEdit::DaySection);

This or course selects the day section of the date, but if you use keyboard arrows the selection vanished, but you can see the keyboard cursor.

1
On

@Rob S answer

You were right with event filter approach we would do same with QDateEdit.

I am writing the code which extends your approach with QDateEdit :

In mainwindow.h I created a QCalendar pointer (Using QtCreator)

Following is the code of mainwindow.cpp (I am giving out fullcode so that rookies like me can benifit from it)

Make sure you set buttonSymbol and calendarpopup property to false to make it work correctly

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCalendarWidget>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->dateEdit->setDate(QDate::currentDate());

    widget=new QCalendarWidget(); //widget is QCalendar pointer

    ui->verticalLayout->addWidget(widget);
    widget->setWindowFlags(Qt::Popup); // we need widget to popup 

    ui->dateEdit->installEventFilter(this);
    connect(widget,SIGNAL(clicked(QDate)),ui->dateEdit,SLOT(setDate(QDate)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::InputMethodQuery)
    {
        if (object == ui->dateEdit)
        {

          if(widget->isVisible()==false && ui->dateEdit->calendarWidget()->isVisible()==false) // this done to avoid conflict
          {
                qWarning(QString().number(event->type()).toStdString().c_str());
                qWarning(object->objectName().toLatin1().data());
                widget->move(ui->dateEdit->mapToGlobal(QPoint(0,ui->dateEdit->height())));
                widget->show();
          }

        }

    }
    return false;
}

OR :: Alternatively we can use QCalendarWidget provided by dateEdit, though its not much efficient as turing it to Popup will mess with its internal. Give it a shot if you want

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCompleter>
#include <QCalendarWidget>
#include <QMouseEvent>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->dateEdit->setDate(QDate::currentDate());

    widget = ui->dateEdit->calendarWidget();
    widget->setWindowFlags(Qt::Popup);

    ui->dateEdit->installEventFilter(this);

    //connecting widget with dateedit
    ui->dateEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ui->dateEdit->setCalendarPopup(true);

    connect(widget,SIGNAL(clicked(QDate)),ui->dateEdit,SLOT(setDate(QDate)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
    if (object == ui->dateEdit)
    {
        if (event->type() == QEvent::FocusIn || event->type() == QEvent::MouseButtonPress)
        {    
           // WE NEED MOUSE EVENT TO AVOID INTERFERNCE WITH CALENDAR POPUP BUTTON SITUATED AT CORNER OF dateEdit WIDGET
            if(widget->isVisible()==false && ( ((QMouseEvent* )event)->x()< (ui->dateEdit->width()-10)))
            {
                widget->move(ui->dateEdit->mapToGlobal(QPoint(0,ui->dateEdit->height())));
                widget->show();
            }
        }    
    }
    return false;
}
0
On

I'd like to offer option similar to @Dr. Xperience's answer that encapsulates calendar widget in QDateEdit subclass:

#include <QDateEdit>
#include <QCalendarWidget>

class DateEdit : public QDateEdit {
    Q_OBJECT

public:
    explicit DateEdit(QWidget *parent = nullptr);

protected:
    virtual void focusInEvent(QFocusEvent *event) override;

private:
    QCalendarWidget *calendar = new QCalendarWidget(this);
};

DateEdit::DateEdit(QWidget *parent) : QDateEdit (parent) {
    setButtonSymbols(QAbstractSpinBox::NoButtons);
    setCalendarPopup(false);
    setDate(QDate::currentDate());

    calendar->setWindowFlags(Qt::Popup);
    connect(calendar, &QCalendarWidget::clicked, this, [&](const QDate &date) {
        setDate(date);
        calendar->hide();
    });
}

void DateEdit::focusInEvent(QFocusEvent *event) {
    if (!calendar->isVisible()) {
        calendar->setSelectedDate(date());
        calendar->move(mapToGlobal(QPoint(0, height())));
        calendar->show();
    }

    return QDateEdit::focusInEvent(event);
}

Warning: If you place this widget using QtDesigner, it will override buttonSymbols and calendarPopup properties, so you have to set it manually to hide QDateEdit's buttons.