QT push button / stop button - qt

I have a push button that I'd like to change to a stop button when clicked. Currently the button's text says "auto fire", it runs an endless loop and it's text changes to "stop auto fire" when clicked. My problem is breaking the endless loop by clicking/pressing this button again after the text changes.
Code so far:
void Cpp_Fire::on_auto_fire_clicked()
{
while(true)
{
ui->auto_fire->setText("Stop Auto Fire");
on_manual_fire_clicked();
}
}
I tried inserting a different slot into the loop above that runs when after the button is pressed (it runs when the button is released to be precise) but I couldn't get it to work.
I know this could be done with signals/slots and a separate stop button but I'm unfamiliar with that method and I prefer the method I described.

The problem with your endless loop is that nothing else gets a chance to work.
One approach you could use is to use a QTimer with a short interval to call the on_manual_fire_clicked() method, then have the on_auto_fire_clicked() method be responsible for changing the text on the button and enabling / disabling the timer.
The ui should get enough time to respond to clicks etc if you do it that way.
edit:
For more info on using QTimer have a look at this page:
How to use QTimer
or this tutorial:
http://www.bogotobogo.com/Qt/Qt5_QTimer.php
Here's some code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
void timerslot();
private:
Ui::MainWindow *ui;
QTimer* myTimer;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QTimer>
#include<QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myTimer = new QTimer(this);
myTimer->setInterval(500);
myTimer->setSingleShot(false);
connect(myTimer, SIGNAL(timeout()), this, SLOT(timerslot()));
myTimer->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timerslot()
{
qDebug() << "timeslot";
}
void MainWindow::on_pushButton_clicked()
{
if ( this->myTimer->isActive() == true ) {
this->myTimer->stop();
ui->pushButton->setText("Start");
} else {
this->myTimer->start(500);
ui->pushButton->setText("Stop");
}
}
I hope you get the idea and can convert it to your needs.

I fully agree Michael's answer.
This will also affect the repaint! (Try to put some windows over your application, while in endless-loop: you should see repaint problems).
Don't use endless-loops, specially not within slots!
Try QTimer, or move object to a QThread.
While in such loop: Give GUI-Thread some time. You can call QCoreApplication::processEvents().. But be careful with this.
A simple (still poor) solution with QTimer could be:
(I found, Michael entered an example in his answer. - Use it.).
//have a QTimer 'timer' in the class, and a connect signal
//timer.timeout() to 'onSingleShotFired()'
void Cpp_Fire::on_auto_fire_clicked()
{
if ( ui->auto_fire->text() == "Stop Auto Fire" )
{
timer.stop();
ui->auto_fire->setText("Start Auto Fire");
}
else
{
//MSEC_AUTOFIRE_DELAY is the delay between the autofire-shots
timer.start( MSEC_AUTOFIRE_DELAY );
ui->auto_fire->setText("Stop Auto Fire");
}
}

Related

The Qt Tray application closes by itself after performing any action in the menu

I'm new in QT and I ran into a problem.
I decided to try writing a small application using Qt and QSystemTrayIcon. To start, I decided to reproduce the example from the official site of Qt.
https://doc.qt.io/qt-5/qtwidgets-desktop-systray-example.html?
This is my header file:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSystemTrayIcon>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
QMenu *trayIconMenu;
QAction *launchAction;
QAction *quitAction;
QSystemTrayIcon *trayIcon;
public slots:
void changeEvent(QEvent*);
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
void trayActionExecute();
void setTrayIconActions();
void showTrayIcon();
};
#endif // MAINWINDOW_H
This is my source code file:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setTrayIconActions();
this->showTrayIcon();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showTrayIcon() {
trayIcon = new QSystemTrayIcon(this);
QIcon trayImage(":/images/trayIcon.png");
trayIcon->setIcon(trayImage);
trayIcon->setContextMenu(trayIconMenu);
connect(trayIcon,
SLOT(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
trayIcon->show();
}
void MainWindow::trayActionExecute() {
QMessageBox::information(this, "TrayIcon", "Info text");
}
void MainWindow::trayIconActivated(QSystemTrayIcon::ActivationReason
reason) {
switch (reason) {
case QSystemTrayIcon::Trigger:
case QSystemTrayIcon::DoubleClick:
this->trayActionExecute();
break;
default:
break;
}
}
void MainWindow::showMsg() {
QMessageBox::information(this, "Tray message", "Hello world!");
}
void MainWindow::setTrayIconActions() {
launchAction = new QAction("Launch", this);
quitAction = new QAction("Exit", this);
connect (launchAction, SIGNAL(triggered()), this, SLOT(showMsg()));
connect (quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
// Setting system tray's icon menu
trayIconMenu = new QMenu(this);
trayIconMenu -> addAction (launchAction);
trayIconMenu -> addAction (quitAction);
}
void MainWindow::changeEvent(QEvent *event) {
QMainWindow::changeEvent(event);
if (event->type() == QEvent::WindowStateChange) {
if (isMinimized()) {
this->hide();
}
}
}
When I perform any action in the context menu, the application simply closes.
I thought that the point was to override the method QCloseEvent and that would fix the situation. So, i reimplent it:
void MainWindow::closeEvent(QCloseEvent *event) {
#ifdef Q_OS_OSX
if (!event->spontaneous() || !isVisible()) {
return;
}
#endif
if (trayIcon->isVisible()) {
QMessageBox::information(this, tr("Systray"),
tr("The program will keep running in the "
"system tray"));
hide();
event->ignore();
}
}
What can be wrong?
Thanks.
The issue can be reproduced by minimizing the main window, which hides it as per your changeEvent() implementation, and subsequently clicking "Launch" in the system tray icon menu. The message box is displayed, and afterwards the application simply closes.
This is caused by a "feature" of QT called QuitOnLastWindowClosed which is enabled by default. If it is enabled, the application quits when the last non-hidden window is closed - in your example, this is the message box spawned by the "Launch" command (which counts as a window)!
You can solve the issue by calling QApplication::setQuitOnLastWindowClosed(false); at some point after creating the QApplication, e.g. in the main.cpp which likely sets up your application:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
MainWindow w;
w.show();
return a.exec();
}
On a side note, your connect() syntax in showTrayIcon() is wrong: It should spell SIGNAL(activated(QSystemTrayIcon::ActivationReason)) instead of SLOT(activated(QSystemTrayIcon::ActivationReason)).
But I would highly recommend to use the new signal/slot syntax (https://wiki.qt.io/New_Signal_Slot_Syntax) instead:
connect(trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated);
This has the advantage of showing you any mistakes (like unknown signals/slots or incompatible parameters) at compile-time instead of runtime.

Can not activate trayicon when dialog is open

(Update on 2019-01-02: Simplify my example)
I am working on a program that can minimize to tray and hide the main window. I expect the mainwindow to show when I click on the tray icon. This usually works, but I find that if the program minimizes to tray when I call QFileDialog::getOpenFileName() to select file without closing the dialog, I cannot activate the trayicon. Does anyone know how to solve it? I am using Qt 5.12.0 (was using Qt 5.9.0) on Manjaro Linux.
Minimal, Complete, and Verifiable example:
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h:
class MainWindow : public QMainWindow
{
Q_OBJECT
QSystemTrayIcon *trayIcon;
QMenu *trayContextMenu;
QAction *actShow;
QPushButton *button;
public:
MainWindow(QWidget *parent = 0);
protected:
void changeEvent(QEvent *);
private slots:
void click();
void trayIcon_activated(QSystemTrayIcon::ActivationReason reason);
void actShow_Triggered();
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#include "mainwindow.h"
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QPushButton *button = new QPushButton("button", this);
setCentralWidget(button);
connect(button,&QPushButton::clicked,
this,&MainWindow::click);
trayIcon=new QSystemTrayIcon;
trayIcon->setIcon(QIcon("hmtimer.png"));
trayContextMenu=new QMenu;
actShow=trayContextMenu->addAction(tr("Show"));
trayIcon->setContextMenu(trayContextMenu);
connect(actShow,&QAction::triggered,
this,&MainWindow::actShow_Triggered);
connect(trayIcon,&QSystemTrayIcon::activated,
this,&MainWindow::trayIcon_activated);
}
void MainWindow::changeEvent(QEvent *event)
{
if(event->type()==QEvent::WindowStateChange){
if(isMinimized()){
this->hide();
trayIcon->show();
}
}
else{
QMainWindow::changeEvent(event);
}
}
void MainWindow::click()
{
QFileDialog::getOpenFileName(this,QString());
}
void MainWindow::trayIcon_activated(QSystemTrayIcon::ActivationReason reason)
{
if(reason==3){ //reason==Trigger
this->show();
trayIcon->hide();
}
}
void MainWindow::actShow_Triggered()
{
this->show();
trayIcon->hide();
}
The example can be downloaded here
It seems that if the program minimizes to tray when QFileDialog::getOpenFileName(this,QString()) is not closed, I cannot make the mainwindow appear by click on the tray icon or right click to show the context menu.
How to reproduce:
Open the program
Click on the button to open file dialog
Minimize to tray
Try clicking and right-clicking on the tray icon
I found a way to show the main window. I can do this by double clicking the icon with the following code of MainWindow::trayIcon_activated:
void MainWindow::trayIcon_activated(QSystemTrayIcon::ActivationReason reason)
{
if(reason==QSystemTrayIcon::DoubleClick){
this->show();
trayIcon->hide();
}
}
I still don't know how to show the mainwindow with single click or show the context menu with right click. If anyone knows, please tell me.
EDIT: Seems that this no longer works. I tried both my previous example and new example. Don't know what is going wrong.

What is the basic working of qtimer?

I created a rectangle and want it to move with QTimer , I want to know how the methods of QTimer exactly work. This code is running but the figure i drew is not moving.
.h
Header file
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
~Widget();
private slots:
void update();
private:
Ui::Widget *ui;
};
this is the .cpp file
.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QTimer>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawRect(50,80,70,80);
}
void Widget::update()
{
update();
}
You say "the figure i drew is not moving". In your code, I don't see code for a moving figure, I only see a static rectangle being drawn.
In addition, the update() function calls itself, leading to infinite recursion. Remove the update() from your code, the base class implementation QWidget::update() does the correct thing (scheduling a call to paintEvent()), no need to reimplement update().
First of all, update() slot method already has a specific meaning and purpose, you should not override it for other purposes. Further, it is not virtual, which tells you it is not even meant to be overriden (and doing it can lead to very confusing situations). So rename your own method to... updateAnimation() or something.
Then you need to add private member variables for your rectangle position, say rectX, rectY, rectWidth, rectHeight (or just single QRect if you prefer). Some code snippets help you get the idea:
void Widget::paintEvent(QPaintEvent *event)
{
// default setting is that Qt clears the widget before painting,
// so we don't need to worry about erasing previous rectangle,
// just paint the new one
QPainter painter(this);
painter.drawRect(rectX, rectY, rectWidth, rectHeight);
}
void Widget::updateAnimation()
{
// modify rectX, rectY, rectWidth and rectHeight here
update(); // make Qt do redrawing
}

Qt debug pointer not following expected flow in 2nd and successive iterations

I want to display a "generating image..." kind of modal dialog, other than the main GUI. This "generating image..." dialog should be temporary, and be displayed and disappear without user intervention.
For displaying this dialog, the Qt code should check for existence of a .txt file in a specific location in the PC's hard disk. If the .txt file exists, then the dialog should pop-up.
For making this dialog disappear, the Qt code should check whether that .txt file contains the string "OK" in the first line. The dialog should disappear only when this "OK" is found, until then it should continue to display "generating image..."
A good way to do this is to use signal slot mechanism. I would like to know, what functions should be used as SIGNALS in both the cases, of displaying and removing the dialog.
So far, I could manage a simple code, illustrating a "generating image..." using signal slot mechanism, but with setValue() and pressing a push button(i.e. involving user intervention), and not with the checking of .txt file or the "OK" string inside that .txt file(user non-intervention).
Please advise me, whether my logic can be implemented? If yes, how? Also, what SIGNALs should be used?
************************UPDATED SECTION(as of Feb 24th '14):****************************************************
I have revised the code according to Dmitry Sazonov's suggestions. I am able to display the loading GIF whenever a new file is created/deleted in a designated directory. Now I want to close this loading qDialog, when the usbResponse.txt file has "ok" inside it. I tried using signal slot, to implement hide(), but could not get it.
I do not get errors, but the qDialog window does not close as expected. I tried both, secDialog.close() and secDialog.hide(), but the window didn't close. Perhaps because the secDialog object is not the same in both the SLOTs. So I also made secDialog, a global object, but I received an error as follows:-
QWidget: Must construct a QApplication before a QWidget
I looked it up: https://qt-project.org/forums/viewthread/12838
Changed the build modes, accordingly, but that didn't help either. Please tell me how do I close my qDialogs, when I find the "ok" in usbResponse.txt file.
************************UPDATED SECTION(as of Mar 14th '14):****************************************************
I could close the qDialog containing the GIF using hide(). I have done a total overhaul of the code. As mentioned above, the qDialog containing the GIF should appear whenever a text file called usbResponse.txt exists at a designated location. Also taking #Dmitry Sazonov's advice, I am able to close the GIF whenever that txt file i.e. usbResponse.txt is modified, using FileSystemWatcher.
I'm continuously scanning for the existence of the .txt using threads. When I find the file, I display the loading GIF. When the .txt is modified the GIF should disappear. This works fine for the first iteration, i.e. when
(the following are observations after debugging)
the usbResponse.txt exists => GIF is displayed
when usbResponse.txt is modified => GIF is hidden & the .txt is deleted.
THe problem, in next iteraiton,(i.e. all iterations after the first)
the usbResponse.txt is created => the GIF is displayed.
when usbResponse.txt is modified, the debug pointer continues to remain in
afterFileHasBeenFound()
whereas it should have gone in
closeModified(const QString &str)
What is my mistake here?
Here is my code:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFile>
#include <QDebug>
#include <QFileSystemWatcher>
#include "dialog.h"
#include "mythread.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void afterFileHasBeenFound();
void closeModified(const QString &str);
private slots:
private:
Ui::MainWindow *ui;
Dialog *pDialog;
MyThread *mThread;
};
#endif // MAINWINDOW_H
dialog.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QMovie>
#include <QLabel>
#define GIF_PATH "E:\\QT1\\timeStampPopUp\\timeStampPopUp\\loading.gif"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void displayLoadingGif();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtCore>
#include <QDebug>
#define FILE_PATH "E:\\QT1\\dialogClose2\\dialogClose2\\usbResponse.txt"
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void run();
QString name;
int exec();
void checkFile();
signals:
void testSignal(QString message);
void fileFoundDisplayGif();
public slots:
};
#endif // MYTHREAD_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
displayLoadingGif();
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::displayLoadingGif()
{
QMovie *pMovie = new QMovie(GIF_PATH);
ui->loadingGifLabel->setMovie(pMovie);
pMovie->start();
}
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
void MyThread::run()
{
exec();
}
int MyThread::exec()
{
while(1)
{
checkFile();
emit(testSignal("hello world!!"));
sleep(1);
}
}
void MyThread::checkFile()
{
QFile file(FILE_PATH);
if(file.exists())
{
qDebug()<<"exists";
emit(fileFoundDisplayGif());
}
else
qDebug()<<"doesn't exist";
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mThread = new MyThread(this);
mThread->name = "mThread";
connect(mThread, SIGNAL(fileFoundDisplayGif()), this, SLOT(afterFileHasBeenFound()), Qt::QueuedConnection);
mThread->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::afterFileHasBeenFound()
{
if(pDialog != NULL)
return;
pDialog = new Dialog();
pDialog->setModal(true);
pDialog->show();
}
void MainWindow::closeModified(const QString &str)
{
Q_UNUSED(str)
if(pDialog != NULL)
{
pDialog->hide();
}
QFile file(FILE_PATH);
file.remove();
pDialog = NULL;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QFileSystemWatcher fileWatcher;
fileWatcher.addPath(FILE_PATH);
QStringList fileList = fileWatcher.files();
Q_FOREACH(QString file, fileList)
qDebug() << "File name " << file;
MainWindow* mc = new MainWindow;
QObject::connect(&fileWatcher, SIGNAL(fileChanged(QString)), mc, SLOT(closeModified(QString)));
mc->show();
return a.exec();
}
Do not use timers for checking file. Use QFileSystemWatcher for traking file modifications.
Implement a slot that will check file content on modification. And call hide() method, when your "OK" text appeared.
IMHO: your solution is to messy. There are a lot of other syncronization mechanisms between processed. Can you modify code of tool that generates image? Should it really work as another process?

QDateEdit calendar popup

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.
Enable "setCalendarPopup ( bool enable )" in QDateTimeEdit allows to popup the calendar
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
#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;
}
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.
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.

Resources