5

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.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
Rob S
  • 103
  • 1
  • 2
  • 5
  • I'm interested to know what your use case is for this. After you display it, will the user still be expected to interact with it? Why not just display a QCalendarWidget by itself? – Arnold Spence Aug 11 '11 at 20:52
  • The use case is that I'd like to require the user to use the qcalendarwidget as the only way to set the date, yet I don't want the calendar always shown (to conserve space) - it should only show once they click on the QDateEdit (or it could be a QLabel if that's a better option) and then hide once they click elsewhere or select a date. – Rob S Aug 15 '11 at 15:26
  • Oh, so you want the QDateEdit to not be editable directly and the QCalendarWidget to be displayed when any part of the QDateEdit is clicked (not just the down arrow)? – Arnold Spence Aug 15 '11 at 16:18
  • Ideally yes, but I'd be okay with the QDateEdit being editable or using a QLabel or some other solution - the key is that onFocus, the calendar gets displayed, rather than on clicking the down arrow. – Rob S Aug 15 '11 at 16:50
  • Ideally, if you want to change the behavior of a widget, you should subclass the widget and override as needed. If anything, this will result in better encapsulation but in this case, it is probably the only option. Browsing the source code will likely give you the hints you need. – Arnold Spence Aug 16 '11 at 20:53

5 Answers5

12

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

Ansif
  • 182
  • 2
  • 14
  • 1
    works fine for QDateEdit as well, I use `QDateEdit *de = new QDateEdit();` and `de->setCalendarPopup(true);` I guess that is all you need? – Wim Jun 29 '14 at 08:32
2

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

Community
  • 1
  • 1
Rob S
  • 103
  • 1
  • 2
  • 5
1

@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;
}
Community
  • 1
  • 1
Dr. Xperience
  • 475
  • 1
  • 5
  • 18
1

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.

vel4eg
  • 11
  • 1
0

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.

jarzec
  • 459
  • 4
  • 8