QTimer::singleShot only calling lambda when interval is 0 - c++

Note: My original post had an important omission: I left out that I had already instantiated the main QApplication instance at the beginning of main. Creating two QApplication instances is what caused the problem. Using the same QApplication instance instead of creating two fixed the issue.
My intention is to run a QApplication before the main application to iterate the available Bluetooth devices, to find a specific one. If the specific one is not found in a certain time limit, the QApplication is terminated. The first stored lambda (startDiscovery) is called as soon as QApplication::exec() is called, but the second stored lambda (cancelDiscovery) is never called! The relevant section is below:
#include <QtBluetooth/QBluetoothDeviceInfo>
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
#include <QtBluetooth/QBluetoothLocalDevice>
#include <QTimer>
#include <QString>
#include <QApplication>
#include <memory>
#define TARGET_BLUETOOTH_DEVICE_NAME "MyBluetoothDevice"
#define BLUETOOTH_DISCOVERY_TIMEOUT 5000 //5 second timeout
int main(int argc, char *argv[])
{
std::shared_ptr<QApplication> mainApplication{std::make_shared<QApplication>(argc, argv)};
//Error checking for no adapters and powered off devices
//omitted for sake of brevity
auto bluetoothAdapters = QBluetoothLocalDevice::allDevices();
std::shared_ptr<QBluetoothLocalDevice> localDevice{std::make_shared<QBluetoothLocalDevice>(bluetoothAdapters.at(0).address())};
std::shared_ptr<QBluetoothDeviceDiscoveryAgent> discoveryAgent{std::make_shared<QBluetoothDeviceDiscoveryAgent>(localDevice.get())};
std::shared_ptr<QBluetoothDeviceInfo> targetDeviceInfo{nullptr};
std::shared_ptr<QApplication> findBluetooth{std::make_shared<QApplication>(argc, argv)};
auto setTargetDeviceInfo = [=](QBluetoothDeviceInfo info) {
if (info.name() == TARGET_BLUETOOTH_DEVICE_NAME) {
targetDeviceInfo = std::make_shared<QBluetoothDeviceInfo>(info);
discoveryAgent->stop();
findBluetooth->exit(0);
}
};
auto cancelDiscovery = [=]() {
discoveryAgent->stop();
findBluetooth->exit(1);
};
auto startDiscovery = [=]() {
discoveryAgent->start();
};
QObject::connect(discoveryAgent.get(), &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, setTargetDeviceInfo);
QTimer::singleShot(0, startDiscovery); //startDiscovery get called fine
QTimer::singleShot(BLUETOOTH_DISCOVERY_TIMEOUT, cancelDiscovery); //cancelDiscovery never gets called!
findBluetooth->exec();
//Now check if targetDeviceInfo is nullptr and run the real application etc...
mainApplication->exec();
}

Answer: discoveryAgent->start(); is basically blocking your main thread. That is why, event which gets posted by QTimer::singleShot(BLUETOOTH_DISCOVERY_TIMEOUT, cancelDiscovery); never gets processed - application executing discoveryAgent->start() and have not opportunity to look into event loop.

My original post had an important omission: I left out that I had already instantiated the main QApplication instance at the beginning of main. Creating two QApplication instances is what caused the problem. Using the same QApplication instance instead of creating two fixed the issue.

Related

Qt GUI Easiest way to access MainWindow from another class

I am doing a blackjack program and I am keeping track of the cards in the players hand in another class ("hand.h") than the main window class.
In the hand class, for every card that I collect, I am also creating a QLabel that grabs the proper card image for the card and also sets the coordinates for where the card should appear on the main window.
The problem is that I am not able to create the QLabel based on the MainWindows object that is originally created at the main function. Is there any easy way that I am able to get that information fairly easily? Thanks for your help!
I have tried using QGuiApplication::topLevelWindows(), but haven't came to luck with using that. Here is my function that I am using.
#include <QRect>
#include <QApplication>
#include <iostream>
#include <QLabel>
#include "mainwindow.h"
#include <QMainWindow>
#include <QWindowList>
#include <QWidgetList>
#include "ui_mainwindow.h"
void Test() {
QList<QWindow*> Main_Window = QGuiApplication::topLevelWindows();
for (int i = 0; i < Main_Window.size(); ++i) {
if(Main_Window.objectName() == "mainWindow") // name is OK
break;
}
QMainWindow* mainWindow = static_cast<QMainWindow*>(Main_Window);
QLabel* temp;
temp = new QLabel(Main_Window);
temp->setPixmap(QString("Ten of Clubs.png"));
temp->setGeometry(290, 300, 350, 390);
temp->show();
}
Here is the main.cpp file that creates the mainwindow
int main(int argc, char *argv[])
{
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
I found the iterating code online and have been having issues from it.
I am having issues while trying to iterate through the list, but I have no idea how to identify the list and the error says that there is no objectName() function. Also, in the static cast line, there is an error that says that I cannot convert an QList to type QMainWindow. Any help would be greatly appreciated.
No way in general, because some applications may have several (toplevel) QMainWindow-s (and their list could change with time). So for that case you'll better pass the pointer to it (the particular QMainWindow you want to deal with) explicitly....
A possible way might be to have your specific subclass of QApplication (which is a singleton class, see QCoreApplication::instance to get its sole instance) and in your application subclass put, as fields, the explicit windows you want to deal with (maybe you even want to add some new signal or slot to your application class).
However, you could use QGuiApplication::topLevelWindows() or QGuiApplication::allWindows() to get the list of all such windows. Notice that a QWindowList is just a QList<QWindow *>. So see QList for how to traverse or iterate on that list.
Once you have found which QMainWindow you want, adding a QLabel into it is usual practice (but again, signals & slots could be helpful).
BTW, each (displayed) widget has its window, see QWidget::window()
About your code:
Your Main_Window is really poorly named (and the name is so confusing that I cannot use that). It is a list not a window. So code first:
QMainWindow* mainWindow = nullptr;
{
QList<QWindow*> topwinlist = QGuiApplication::topLevelWindows();
int nbtopwin = topwinlist.size();
for (int ix=0; ix<nbtopwin; ix++) {
QWindow*curwin = topwinlist.at(ix);
if (curwin->objectName() == "mainWindow")
mainWindow = dynamic_cast<QMainWindow*>(curwin);
}
}
I did not test the above code and I am not sure it is correct or even can compile. But why don't you just have a global pointer to your main window:
MainWindow*mymainwinp = nullptr;
and initialize it appropriately in your main body:
int main(int argc, char *argv[]) {
srand(time(NULL));
QApplication a(argc, argv);
MainWindow w;
mymainwinp = &w;
w.show();
int r = a.exec();
mymainwinp = nullptr;
return r;
}
then use mymainwinp elsewhere (e.g. in your Test)? If you want more elegant code, define your own subclass of QApplication and have mymainwinp be a field in it.

Why is it allowed to create more than one QCoreApplication objects?

Looking at the following code:
#include <QDebug>
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
qDebug() << QCoreApplication::instance(); // prints QCoreApplication(0x7ffd39656150)
QCoreApplication app2(argc, argv);
qDebug() << QCoreApplication::instance(); // prints QCoreApplication(0x7ffd39656160)
return 0;
}
It looks like that it's possible to create more than one QCoreApplication objects, but that should be a singleton. What does happen to the first created QCoreApplication object? Is it destroyed or will be there two event loops when exec is called for two objects?
That's illegal. You're probably running against a release build of Qt, with asserts disabled.
https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qcoreapplication.cpp.html#742 makes it very clear that you cannot have two QCoreApplication alive at the same time:
void QCoreApplicationPrivate::init()
{
Q_Q(QCoreApplication);
initLocale();
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
QCoreApplication::self = q;
....
It's not allowed. It's not always the compiler's job to inform you of your mistakes. You erred by assuming that just because some code compiles, it's somehow on the compiler if the code is wrong.

Qt 5.3 Signals and Slots, Simple Function and Lambda Expression

I tried to Write a program using Qt 5.3 and I try to using signals and slots as practice. I wrote the following code (part of the code) :
void exitToWin()
{
exit(0);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QMessageBox EndBox;
QObject::connect((EndBox.button(QMessageBox::Ok)),SIGNAL(clicked()),exitToWin);
w.show();
EndBox.show();
return a.exec();
}
I even change the declaration of the function to static and I checked the expression with parentheses and without them while I am writing the connect command. but although what Qt documented and what its IDE guided to. also I read here and I tested it.
Moreover I tried with lambda expression as below:
QObject::connect((EndBox.button(QMessageBox::Ok)),SIGNAL(clicked()),[=](){
exit(0);
});
but still I receive errors indicate "No matching function call".
And after all I have to say that I am using Microsoft Windows 7.
This works on Qt 5.3:
#include <QtWidgets>
void exitToWin()
{
exit(0);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
QMessageBox endBox;
endBox.addButton(QMessageBox::Ok); // (2)
endBox.connect(endBox.button(QMessageBox::Ok),
&QAbstractButton::clicked, exitToWin); // (1)
/* This works, too:
endBox.connect(endBox.button(QMessageBox::Ok),
&QAbstractButton::clicked,
[] () { exit(0); });
*/
w.show();
endBox.show();
return a.exec();
}
Here's why:
(1) You can use endBox's QObject to do the connection between the QAbstractButton's clicked signal and your exitToWin simple function. You also can't connect a SIGNAL to a simple function or a lambda, so we use the member function variety, instead.
(2) endBox doesn't actually get an OK button by default. When you mention it on line (1) in your code, it creates it, but not in time (apparently) to pass the pointer back to connect, so we create it first here.
Your code won't work for two reasons:
Firstly, QMessageBox does not have such a signal. See the documentation for the signals it does have.
Secondly, when making connections from a signal to a slot (or lambda function), you must define the function signatures, not specific values.
If a signal can pass a variety of values and you only want your slot to perform a certain function on a selection of those values (in this case, only if the value QMessageBox::Ok is passed) it is up to the slot to interrogate the values, not the connect statement.
Since the connect() method is from QObject it has to be called from a QObject child containing the Q_OBJECT macro in its declaration. Running qmake prepare the class to send signals and receive slots.

How to do things upon quitting a Qt app

To my understanding, the following will execute an application which will exit when all associated windows are closed.
int main(int argc, char *argv[])
{
QApplication cal(argc, argv); //Application instance
MainWindow window; //Create window in the stack
window.show(); //Show the window
return cal.exec(); //Execute event loop
}
This will also quit said application.
quitButton = new QPushButton("Quit", this);
connect(quitButton, SIGNAL(clicked()), QApplication::instance(), SLOT(quit()));
How should I go about cleaning up (closing hardware connections and libraries etc.) so that I can be sure everything that needs to happen happens no matter how the app it exited, nor at what point in execution it is exited?
I will have a library open throughout the whole app, and a connection to a USB device at most times. I am able to close this connection easily if the process runs its course, but I want the connection to be closed safely if somebody decides to hit quit or close all the windows before it is done.
There is a virtual function called closeEvent for a QWidget (see Documentation, which is called when the widgets close() operation is called.
Alternatively, if you don't want to have your own Base widget, you can hook yourself to the signal QApplication::lastWindowClosed (see here). It is emitted when the last visible window of your application closes.
You can do the following:
int main(int argc, char *argv[])
{
QApplication cal(argc, argv);
// Allocate resources
int ret = cal.exec(); // start the event loop and wait until it quits
// Free the resources
return ret;
}
The proper C++ way of doing it is:
Ensuring that all of your object instances get destructed.
Ensuring that any resources you hold are held via RAII.
Then you don't need to do anything special. As the objects get destructed when main returns, things get cleaned up.
For example:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QObject foo;
QObject * bar = new QObject(&app);
Widget w;
w.show();
return app.exec();
}
Here, both foo and bar are properly destructed before main returns. Now suppose you hold some non-memory resources, like a file:
class Worker : public QObject {
QFile m_file;
...
void doSomething() {
if (m_file.open()) {
...
}
// No file closing!
}
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker foo;
Widget w;
w.show();
return app.exec();
}
Again, the file gets closed before main returns.
That's how RAII works, in a nutshell. If you have your own classes that wrap OS or other resources, simply ensure that such resources are freed when the classes get destructed - and that the classes are always destructible (it's always safe to destruct them). My pet peeve is QThread - it breaks this expectation, and you have to implement a wrapper to fix it.
For resources that you need to allocate on the heap, you're supposed to be using smart pointers or QObject memory management.
There is a QApplication signal that you can use for cleaning up before the application quits:
QObject::connect(&app, SIGNAL(aboutToQuit()), &MyCleaner, SLOT(cleanUp()));
Have fun!

How to verify that a condition variable has initialized?

I am having some trouble resolving a race condition that arises when acquiring a lock. I have a large computation that is triggered asynchronously. I need my previous synchronous task to end before the large task begins. The large task launches and waits on a condition variable and, in an ideal world, the small tasks will notify the condition variable when they are done. My problem comes from the large task starting too soon, and the small tasks trigger a condition variable that has yet to full initialize and thus does not actually trigger, causing the program to be locked.
I have boiled it down to this minimal example.
I would assume this is a common problem. How can I check that my condition variable has actually got hold of a mutex and is locked?
#include <QCoreApplication>
#include <QObject>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
#include <QWaitCondition>
class workUnit: public QObject
{
Q_OBJECT
public:
workUnit(QObject *parent)
{
m = new QMutex();
m->lock();
w=new QWaitCondition();
}
QWaitCondition* w;
QMutex* m;
public slots:
void runMe()
{
w->wait(m);
m->unlock();
//perform long computation
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
workUnit* mUnit=new workUnit(&a);
QFutureWatcher<void> w;
QObject::connect(&w, SIGNAL(finished()), &a, SLOT(quit()));
QFuture<void> f = QtConcurrent::run(mUnit,&workUnit::runMe);
w.setFuture(f);
_sleep(1000);//with this delay it works, without the delay it doesn't
mUnit->w->wakeAll();//This would be triggered by another process
return a.exec();
}
The documentation for QWaitCondition::wait states that:
The mutex must be initially locked by the calling thread.
You should remove m->lock(); from the constructor and put it in the runMe() function before the call to wait.