How to call a slot on quit - c++

I want to update my database just before my Qt application closes.
I want something like connect(this, SIGNAL(quit()), this, SLOT(updateDatabase()))
One way could be to introduce a quit button, but is it possible to achieve this functionality if user presses Alt+F4?

Use signal aboutToQuit() instead.
This signal is emitted when the application is about to quit the main
event loop, e.g. when the event loop level drops to zero. This may
happen either after a call to quit() from inside the application or
when the users shuts down the entire desktop session.
The signal is particularly useful if your application has to do some
last-second cleanup. Note that no user interaction is possible in this
state.
For example :
connect(this, SIGNAL(aboutToQuit()), this, SLOT(updateDatabase()));

There is another way to do it, not aboutToQuit() signal, but to re-implement the closeEvent(QCloseEvent *event). You can call you slot before the statement event->accept();
like this:
void MainWindow::closeEvent(QCloseEvent *event)
{
call_your_slot_here();
// accept close event
event->accept();
}

Related

Programatically executing mouse events in QT

I want to transfer a mouse event from one widget and execute it on another, I'm sending the mouse event through a signal.
void Eventhost::mousePressEvent(QMouseEvent* event)
{
emit SignalControl(event);
}
This event gets sent to the widget where it is processed here;
void EventReceiver::mousePressEvent(QMouseEvent * event)
{
event->accept();
QWidget::mousePressEvent(event);
}
However even though the event makes it to the eventreceiver the mouse event doesn't seem to execute.
What am I missing? I'd appreciate any input
Try it without using event->accept(); in your EventReceiver. As explained in Qt Docs:
Events that can be propagated have an accept() and an ignore() function that you can call to tell Qt that you "accept" or "ignore" the event. If an event handler calls accept() on an event, the event won't be propagated further; if an event handler calls ignore(), Qt tries to find another receiver.
This is the reason QWidget::mousePressEvent(event); is not being called inside void EventReceiver::mousePressEvent(QMouseEvent * event).
You need to connect a signal emitter to a slot receiver. The SignalControl method must be declared under signals: in the header for the Eventhost class, mousePressEvent must be declared under slots: in the header for the EventReceiver class.
Eventhost* hostObj = new Eventhost(...
EventReceiver* recvObj = new EventReceiver(...
QObject::connect(hostObj, &Eventhost::SignalControl,
recvObj, &EventReceiver::mousePressEvent);
See the docs here:
https://doc.qt.io/qt-5/signalsandslots.html
Also see:
Qt: How to handle custom events with connect?

Qt - does a QTimer::timeout() signal result in a QEvent?

Qt doc:
If no event loop is running, events won't be delivered to the object.
For example, if you create a QTimer object in a thread but never call
exec(), the QTimer will never emit its timeout() signal. Calling
deleteLater() won't work either. (These restrictions apply to the main
thread as well.)
Does this mean that signal void QTimer::timeout() will also issue a QEvent?
If so, where does the Qt doc state this?
where does the Qt doc state this?
Nowhere, because it shouldn't matter to the user of QTimer. The timer event is an implementation detail. It is delivered to the timer object itself, so you'd really have to go out of your way to intercept it. Here's how QTimer works:
class QTimer : public QObject {
Q_TIMER
QBasicTimer m_timer;
protected:
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId())
emit timeout();
}
/*...*/
};
If you think about it, there's no way of emitting any signals without running the code that emits the signals, and the only way to safely run such code that emits things asynchronously is to code for run-to-completion chunks that cede control to the event loop at every opportunity. The event loop is notified by the platform that a timer has timed out, and emits a signal right then. You'd be in deep trouble if Qt issued signals such as timer timeouts from intrusive asynchronous callbacks like Unix signals: just read about how few things you can do while in a signal handler - it'd be no different than an interrupt handler.

Qt: How to close a dialog window opened with exec()?

I'm making a c++ application in Qt, and need to programmatically close a dialog window (opened with this->exec();) via code after a certain function finishes executing.
I'm using Qt 5.6.
Thanks in advance!
Here is an example of my code, that doesn't work (Worker is the dialog Class):
void MainWindow::on_pushButton_2_clicked()
{
//When Start button clicked:
Worker worker;
worker.exec();
//worker.run(1);
worker.accept();
}
So when pushButton_2 is clicked, I want a dialog to open that gives out the current progress, and when that is done, I want it to close.
Edit:
Now you posted more code....
worker.exec();
worker.accept(); // or worker.close();
exec() starts QDialog events processing loop and will return only when completed (after accept(), reject() or done(int) is called). So worker.accept() will not be reached (you should see that if using your debugger). It must be called by worker itself after a user action (button click by instance).
What you meant to do is:
worker.show();
QThread::sleep(2); // wait for 2 seconds
worker.accept();
Then, worker.accept() will be executed at some point. Dialog is shown, but it's modal.
Old post (before edit):
You can call accept() to do as if user clicked OK or reject() to do as if user clicked Cancel.
Note that those are slots, so you can fire them by connecting a signal to them (signal to be emitted when you function finishes executing for instance).
Example:
void MyDialog::doSomethingAndClose()
{
// do your stuff here
accept(); // will close the dialog
}
or:
void MyDialog::doSomethingAndClose()
{
// do your stuff here
emit weAreDone();
}
If you earlier connected (in MyDialog constructor for instance):
connect( this, SIGNAL(weAreDone()), this, SLOT(accept()) );
Just connect your custom signal with QDialog::done(int) and emit signal after your function finishes executing.
As I've just learned, the issue is caused by the gui not updating automatically.
Here is a link to a SO question that fixes this issue.

QT QDialog not hiding properly when show/hide called quickly

I have a QDialog on my main thread and I have some logic that happens on a separate thread. When the logic begins, a signal is emitted connected to show() on the dialog. When the logic ends, a signal is emitted that is connected to hide() on the dialog. When the logic actually does work, the dialog is show/hide properly. If the logic does "nothing" and the signals are just emitted sequentially, the dialog doesn't always show/hide properly.
My connections are made similar to this:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0) :
Ui(new Ui::MainWindowUi),
Transferer(new DataTransferer()),
TransferProgress(this),
TransferThread()
{
Ui->setupUi();
connect(&Transferer, SIGNAL(Begin()), &TransferProgress, SLOT(show()));
connect(&Transferer, SIGNAL(End()), &TransferProgress, SLOT(hide()));
Transferer.moveToThread(&TransferThread);
TransferThread.start();
Transferer.DoStuff(true);
}
virtual ~MainWindow()
{
TransferThread.quit();
TransferThread.wait(1000);
delete Ui;
Ui = NULL;
}
private:
Ui::MainWindowUi* Ui;
DataTransferer Transferer;
TransferProgressDialog TransferProgress;
QThread TransferThread;
}
The logic looks similar to this:
class DataTransferer : public QObject
{
Q_OBJECT
public:
DataTransferer(QObject *parent) : QObject(parent) {}
virtual ~DataTransferer() {}
void DoStuff(bool dontDoStuff)
{
emit Start();
if (!dontDoStuff)
{
QThread::sleep(1);
}
emit End();
}
}
When the DataTransferer does stuff, everything works fine. When the dialog is shown and hidden in rapid succession, I get the ghost dialog approximately every other time I call DoStuff().
I used QThread::currentThreadId() and verified that the dialog and logic are running on separate threads.
Why would my dialog not hide properly in this case? Should I just force my logic to always run for at least a few hundred milliseconds (that solution is bad)? Is there a way I can have my dialog make sure it's fully loaded before trying to hide itself? Should I handle these signals/slots differently?
EDIT: I've currently resigned to just putting a QThread::sleep(1) after I emit the signal to show() the dialog. I don't like this solution, but nothing else has seemed to work. The sleep(1) allows the dialog to come all the way up before hiding it. I was also able to get this to work with QThread::msleep(10), but that still resulted in the ghost dialog about 1 in 6 tries.
I tried using a member QMutex in the dialog logic whenever I called either show() or hide(), but this didn't work.
I changed all cross-thread connections to use Qt::BlockingQueuedConnection and Qt::QueuedConnection and neither attempt was successful.
I tried moving the slot connections from the dialog to the object that sets up the connections and then calling the slots directly, but that didn't prove successful either.
I have the same issue, show dialog and when get some signals to close it, when time less than 20ms(which means quickly hide the dialog), it will left a ghost dialog.
So, i just use
QTimer::singleShot(50, this, [this](){
hide(); //hide dialog
});
in close handler function. It seems work well.
My guess is that the problem occurs because "show" and "hide" calls interlace. To verify this, use a semaphore - lock the object until show have finished, and wait on it in hide. Also look at the top-voted answer here for another possible (perhaps better) solution: connecting signal/slot across different threads between QObjects
Use Qt::BlockingQueuedConnection to connect signals to slots. Be sure that event loop of main thread is not blocked. Also, if your worker thread uses a lot of cpu time - you may call QThread::yeldCurrentThread() call.

How to catch the QSystemTrayIcon quit event?

I have a QSystemTrayIcon subclass. Inside it, I have:
quitAction = new QAction(tr("&Quit"), m_parent);
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
CloseEvent doesn't work here. How can I catch this quit event? I want to save data before closing.
You should connect your cleanup/save code to the QCoreApplication::aboutToQuit() signal.
This signal is emitted when the application is about to quit the main event loop, e.g. when the event loop level drops to zero. This may happen either after a call to quit() from inside the application or when the users shuts down the entire desktop session.