Qt 5.3 Signals and Slots, Simple Function and Lambda Expression - c++

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.

Related

Not able to connect signals and slots

I wrote a basic C++ program for simulating the signal and slot process. I made a push button "button", a QVBoxLayout "layout". I added the button in the layout. Everything is fine till now. But when push button is made to connect by signal and slot, there's are two problem/errors.
"no instance of overloaded function "QObject::connect matches the argument list"."
'QObject::connect': none of the 3 overloads could convert all the argument types.
Question:
I am sure that there's something missing, which I am not able to decipher. Here, the push button "button" has to invoke the function "connectFunc". Instead it gives me above two errors. How to make the button invoke the function?
This is my code.
#include "signalsslots.h"
#include <QtWidgets/QApplication>
#include<qpushbutton.h>
#include<qboxlayout.h>
#include<iostream>
using std::cout;
using std::endl;
void connectFunc()
{
cout << endl << "connected " << endl;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton* button = new QPushButton("Press Here");
QAction* bAction = new QAction;
button->addAction(bAction);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(button);
//layout->SetFixedSize(200, 150);
QObject::connect(bAction, SIGNAL(QPushButton::clicked()), &a,(connectFunc()));
QWidget w;
w.setLayout(layout);
w.setWindowTitle("Signal and Slot Example !!!");
w.show();
return a.exec();
}
FYI, I am using VS 2019 for writing Qt widget applications.
The QObject::connect() function that you use, connects a signal (i.e. a method) of an object to a slot (i.e. a method) of another object.
What you are telling QT is to connect the clicked() method of your QAction object, to the connectFunc() of your QApplication object. Both methods do not exist on those objects!
What you probably want is to connect the clicked() method of your QButton button to the free function connectFunc(). Like this:
QObject::connect(button, &QPushButton::clicked, &connectFunc);
Note that this requires QT5. (See Is it possible to connect a signal to a static slot without a receiver instance?)

Qt : pass values from a window to another

I have a Qt App running two windows, a login window and a data window. I need to be able to pass a couple of values from the first to the second, but i can't figure out a way to do it, since - on my main.cpp file - i have :
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
LoginWindow loginWindow;
loginWindow.show();
return a.exec();
}
What i'd want would be :
LoginWindow loginWindow;
loginWindow.who();
// wait for user to input data in LoginWindow.......
// fetch datas from loginWindow
SecondWindow secondWindow;
secondWindow.show();
So, as soon as my LoginWindow is closed (after the login), the whole app is terminated. Plus, i can't extract datas drom LoginWindow to the main function, since i don't know when they'll be available.
How can i make this work ?
Thanks.
I would declare a local variable in Main() and pass it by-reference to the login window's constructor, i.e.:
string username;
LoginWindow loginwindow(username);
loginwindow.show();
Where the constructor is declared like this:
LoginWindow::LoginWindow(string& username);
Then LoginWindow can modify the string however it needs to, and when the call to show() returns, the user name will be stored in the local variable username. You can then pass that to the constructor of secondWindow in the same way.
That's a proper place for Qt's signals and slots. The simplest way to get going - just add one signal to LoginWindow class and one slot to DataWindow class:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
LoginWindow loginWindow;
DataWindow dataWindow;
QObject::connect(&loginWindow, SIGNAL(loginAcquired(LoginData)),
&dataWindow, SLOT(getLoginData(LoginData)));
QObject::connect(&loginWindow, SIGNAL(loginAcquired(LoginData)),
&dataWindow, SLOT(show()));
loginWindow.show();
return a.exec();
}

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!

Qt/C++ : Centralise parameters in one class

I'm trying to centralise some parameters in one class. I looked on several forums and tutorials without finding any real answer. Maybe I don't use Qt correctly.
I have a Qt class Application which handles theses parameters.
Application.h
class Application : public QApplication
{
public:
Application(int, char**);
~Application();
bool setFilesPath(QString path);
QString getFilesPath();
private:
QString filesPath;
}
main.cpp
int main(int argc, char *argv[])
{
Application app(argc, argv);
MainWindow mainWindow;
mainWindow.show();
return app.exec();
}
I'd like to access the setFilesPath() and getFilesPath() methods from everywhere (for exemple in another class. Therefore a made Application inherit from QApplication, hoping that qApp.setFilesPath() would work. But it doesnt.
It seems to be a quite usual thing to access some parameters from everywhere. So how could one do this ?
I've found this three ways :
Global variables
Static methods
Including an instance of Application in every class needing the method (but it seems wrong)
Which is more commonly used (and why) ? Why does my way doesn't work ?
Thank you.
EDIT:
In this topic, they use singelton design pattern.
So calling
Application *app = Application::getInstance();
everywhere I need it. Is it better ?
You can access your class using qApp pointer. According to the Qt documentation:
A global pointer referring to the unique application object. It is
equivalent to the pointer returned by the QCoreApplication::instance()
function except that, in GUI applications, it is a pointer to a
QApplication instance.
So, your code somewhere could look like:
Application *myApp = qobject_cast<Application *>(qApp);
QString path = myApp->getFilesPath();
You can have your classes in the main independently and connect a signal from each class that needs to access the value in your class to the slot that returns the value :
int main(int argc, char *argv[])
{
Application app(argc, argv);
MainWindow mainWindow;
MyClass myclass1;
QObject::connect(&mainWindow,SIGNAL(askFilesPath()),myclass1,SLOT(getFilesPath()));
mainWindow.show();
return app.exec();
}
This way when you call the signal askFilesPath() in your MainWindow, the slot in your custom class gets called and the value is returned:
QString filePath = askFilesPath();
You should note that if the two classes are in different threads then the connection type should be Qt::BlockingQueuedConnection.

Connect all of an objects signals to a single slot

Been looking around a bit and people seem to have a similar issue but with multiple signals from different sources etc. My situation is that I have a object that signals if it succeded, failed or got canceled. These signals are passed along to another class and are'nt connected to a slot, just a signal. Now I would like to fix so that no matter what signal the object sends (failed, succeded, canceled) a slot will be called that will delete the object. In short, I want a way of connecting every signal of an object to a slot.
Want to do something like this:
connect(myObject, allSignals, this, handleObjectDone());
void handleObjectDone() {
myObject->deleteLater();
}
Is there any way of doing this? Or should I just pass two signals everytime it does something, for example emit readyToBeDeleted() and emit succeded()?
Thanks!
Setting aside any qualms about whether connecting all the signals in one object to a single slot in another object is actually a wise thing to do, below is a function that does that, along with a unit test to verify that it works.
If you watch stdout while you run this, you will see it print out all the connections it is making. When it runs, clicking on the QLineEdit will cause the QLineEdit to emit a signal, which will (of course) cause the QApplication::quit() slot to be called, so the application will exit.
#include <stdio.h>
#include <QApplication>
#include <QLineEdit>
#include <QMetaMethod>
#include <QMetaObject>
void ConnectAllSignalsToSlot(QObject * sourceObject, QObject * targetObject, const char * slotName)
{
const QMetaObject * mo = sourceObject->metaObject();
if (mo)
{
int numMethods = mo->methodCount();
int firstMethod = mo->methodOffset(); // set to 0 instead if you want to connect signals from superclasses too
for (int i=firstMethod; i<numMethods; i++)
{
QMetaMethod mm = mo->method(i);
if (mm.methodType() == QMetaMethod::Signal)
{
QString signalString = QString("2") + mm.signature();
printf("Connecting up signal [%s] on object %p to slot [%s] on object %p\n", signalString.toUtf8().constData(), sourceObject, slotName, targetObject); // just so we can see what it's doing
QObject::connect(sourceObject, signalString.toUtf8().constData(), targetObject, slotName);
}
}
}
else printf("Error, sourceObject has no QMetaObject????\n");
}
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
QWidget * testSource = new QLineEdit;
testSource->show();
ConnectAllSignalsToSlot(testSource, &app, SLOT(quit()));
return app.exec();
}
You can connect any number of signals to any number of slots (as well as other signals). It makes perfect sense to connect the signals to two slots for this purpose. The slots are called in the order they are connected. Emitting two signals consecutively is perfectly reasonable as well. Of course readyToBeDeleted() should be emitted after succeeded() so that the object isn't deleted before emitting its result signal.
Unless I'm misunderstanding you, it's that simple.