Qt 4.0 Signals and Slots with minimal OOP - c++

I'm relatively new to Qt; I'm writing a small program and I don't want to get into making my own classes for each widget and such. So far I haven't had many difficulties, but I'm slightly confused about signals and slots. I want to make a signal that triggers when a user types into a text box (QLineEdit) and presses enter. I would prefer the slot to be a function that accepts the text inputted by the user.
So far I've come up with this:
textBox.connect(&textBox,SIGNAL(textBox.returnPressed()),/*What to put here?*/,processText(/*Here?*/))
I apologize if this piece of code is terribly wrong; as I said I'm relatively new to Qt.
Help would be very much appreciated.

Signals and slots must be methods of a QObject (/QObject subclass). You can't use free functions as slots. "Minimal OOP" here probably would mean QObject singleton(s) containing the slots.
Also a signal with signature (A, B, C) can only be connected to slots with signatures (), (A), (A, B), (A, B, C), i.e. one can discard/ignore trailing arguments. A slot cannot have more/other arguments than the signal, nor can you bind slot arguments to a certain value when connecting (Where QSignalMapper covers the most common use case I think).

Sadly, I don't think there is a way to achieve this without inheritance - if you want the text to go with the signal or some custom handling to be done in the slot that is called.
If not, you can just use the "returnPressed" signal emitted from the QLineEdit and some pre-existing slot I couldn't possibly know.
Here's my solution using inheritance.
To make your own slot, you need to make an inherited class. Inherit it from Qobject, directly or indirectly.
//myClass.h
#ifndef myclass_h_
#define myclass_h_
#include <QObject>
class myClass: public QObject
{
// important
Q_OBJECT;
....
public slots:
void takeText(QString p_value);
};
#endif
And to get a signal like that from a QLineEdit, you need something custom as well
//myLineEdit.h
#ifndef mylineedit_h_
#define mylineedit_h_
#include <QLineEdit>
class myLineEdit: public QLineEdit
{
// important
Q_OBJECT;
....
public slots:
void handleReturnPressed();
signals:
void newText(QString p_value);
};
#endif
And then do these
myLineEdit::myLineEdit()
{
connect(this, returnPressed(),
this, handleReturnPressed());
}
void myLineEdit::handleReturnPressed()
{
emit(newText(text());
}
After this, create a myClass object, and connect the signal to it.
myClass * thing = new myClass();
myLineEdit* lineEdit = new myLineEdit();
connect(lineEdit, SIGNAL(newText(QString),
things, SLOT(takeText(QString));

Related

C++ Qt Signals and Slots

I have difficulty connecting to SLOTs defined in a different class. I have 2 classes - Computations and MainWindow. MainWindow is supposed to handle the GUI part of the program only and Computations handles the calculations. I am creating a calculator.
Basically, I want to understand how I can connect to SLOTs in the Computations Class from the MainWindow Class.
I guess you already checked the Qt Signals & Slots page. To implement what you want you need to have an instance of your class in the other one.
So for example in your MainWindow class, you can include the Computations class and create a member variable of it:
#include "computations.h"
class MainWindow : public QMainWindow
{
Q_ObBJECT
public:
//..
private:
Computations *_computation;
};
and then in the constructor of MainWindow after initializing the _computation object (_computation = new Computations();) you do the connections like this (works for Qt5):
QObject::connect(_computation, &Computations::somethingHappened, this, &MainWindow::doSomeGuiStuff);
QObject::connect(this, &MainWindow::guiThingHappened, _computation, &Computations::doSomeComputation);
depending on which way it should go.
I hope this helps.
This is another version how to use, I think can be easier to understand for beginners
You need define signal and slots in your classes.
Add to header of your class, for example signals to MainWindow, slots to Computations
public slots:
void something();
signals:
void something_happend();
Then, in anywhere, where you want use it, in your example in mainwindow.cpp, you need to connect signal and slot. Do this by QObject::connect :
QObject::connect(who_emit,SIGNAL(what_signal),who_receive,SLOT(what_slot))
Example:
mainwindow.h
signals:
void something_happend();
computations.h
public slots:
void something_happend();
mainwindow.cpp
Computations *c = new Computations(this);
QObject::connect(this,SIGNAL(something_happend()),c,SLOT(something()));
If you want to pass some arguments, SIGNAL and SLOT that you want to connect need have same types of arguments:
public slots:
void something(int c);
signals:
void something_happend(int c);
QObject::connect(this,SIGNAL(something_happend(int)),c,SLOT(something(int)));
Such connections belong at a level where both the UI and the controller (computation object) are available. Thus, either in the body of main, or in a class that composes that various elements of the application at a high level (such a class usually shouldn't derive from QApplication, though).
It is almost always too tight of a coupling if the UI class knows of the existence of the computation object, or is somehow tied to its details. I usually design the UI to have an interface composed of signals and slots of as generic a nature as practicable, and then tie it to one or more controller objects via signal/slot connections. I also leverage the property system to expose UI-relevant properties in a generic manner, often using viewmodel objects to interface a UI-agnostic controller to a concrete kind of a UI.
In your case, I'd suggest that neither MainWindow know of Computations, nor vice versa:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Computations comp;
MainWindow ui;
QObject::connect(&comp, ..., &ui, ...);
/* more connections here */
ui.show();
return app.exec();
}
For more concrete examples, see answer #1 or answer #2.
you need slots and signals because those work together, like this:
your file.h
public slots:
void start();
signals:
void levelChanged(int level);
implementing:
void MainBoard::start()
{
isStarted = true;
clearBoard();
emit levelChanged(1);
}
now you need to link a button
startButton = new QPushButton(tr("&Start"));
startButton->setFocusPolicy(Qt::NoFocus);
connect(startButton, &QPushButton::clicked, board, &MainBoard::start);

QGraphicsScene selectionChanged() event

I need to know when a QGraphicItem is selected from my Scene. I'm using the signal from the method selectionChange() but this does nothing. This my code:
scene.h
class Scene : public QGraphicsScene{
public:
Scene(QGraphicsScene *scene = 0);
~Scene();
private slots:
void test();
};
scene.cpp
Scene::Scene(QGraphicsScene* scene):QGraphicsScene(scene)
{
connect(this, SIGNAL(QGraphicsScene::selectionChanged()), this, SLOT(test()));
}
void Scene::test() {
qDebug() << "I'm here ";
}
I suppose that the problem is that my scene inherits from QGraphicScene, or that it's a bad idea define the connection in the constructor.
SIGNAL and SLOT are macros and thus text-based processing, which makes them quite picky. It's generally a good idea to assert that all your connections succeed. In your case, the problem is the extraneous qualification. Drop it:
connect(this, SIGNAL(selectionChanged()), this, SLOT(test()));
As mentioned by #Angew, the problem is in the text passed to the SIGNAL macro.
If you're using Qt 5, the preferred method would be to use the newer connection syntax, which benefits from compile time error checking
connect(this, &GraphicsScene::SelectionChanged, this, &Scene::Test);
This connection method uses addresses of functions, which has the additional benefit of being able to connect to functions that haven't been declared as a SLOT. However, it may be desirable to still define slots, as discussed here.

QObject::connect: No such slot (Qt, C++)

I can run the program but the button cannot access the send function. I get this hint:
QObject::connect: No such slot Mail::send(emailInput, pwdInput)
Someone knows what's my mistake?
mail.h:
#ifndef MAIL_H
#define MAIL_H
#include <QWidget>
namespace Ui {
class Mail;
}
class Mail : public QWidget
{
Q_OBJECT
public:
explicit Mail(QWidget *parent = 0);
~Mail();
public slots:
void send(std::string email, std::string pwd);
private:
Ui::Mail *ui;
};
#endif // MAIL_H
mail.cpp:
Mail::Mail(QWidget *parent) :
QWidget(parent)
{
QLineEdit *edt1 = new QLineEdit(this);
grid->addWidget(edt1, 0, 1, 1, 1);
std::string emailInput = edt1->text().toStdString();
...
QObject::connect(acc, SIGNAL(clicked()),this, SLOT(send(emailInput, pwdInput)));
}
void Mail::send(std::string email, std::string pwd){
...
}
In fact, you have 2 mistakes in your code:
the SLOT macro takes the type of arguments as parameter not their name, then the code should be : SLOT(send(std::string, std::string)).
You try to connect a SIGNAL which has less argument than the SLOT which is impossible.
In order to avoid all these problems you can use the new signal/slot syntax (if your are using Qt5):
QObject::connect(acc, &QLineEdit::clicked, this, &Mail::onClicked);
I also invite you to use the QString class instead of std::string when working with Qt, it is a lot easier.
That depends on what you want to do:
If emailInput and pwdInput come from widgets, you have to write an intermediate slot that will get the values and call send.
If they are local variables, the easiest is probably to use a lambda.
Should be
QObject::connect(acc, SIGNAL(clicked()),this, SLOT(send(std::string, std::string)));
Macros SIGNAL and SLOT expect method's signature as argument(s).
Additionally, you can connect a signal to a slot of less arity, not the vice versa; here, QObject would not simply know what should be substituted for slot's arguments. You can use the overload of connect that accepts an arbitrary Functor (an anonymous closure, most likely) as slot:
QObject::connect(acc, SIGNAL(clicked()), [=](){ send(std::string(), std::string()); });
Thirdly, were you to use QString instead of std::string, you would not have that heavy copy overhead when passing by value.

using another class object for signal and slots inside qt

Consider the following scenario:
I have integrated QT in my c++ app. I wish to enter Data from a GUI rather than terminal. For this purpose, i created a function for QT. My dialog window consists of three text lines and a button, upon the click of which i want to call a particular method of some other class. I am having trouble with SINGAL and SLOTS.
Consider the following files:
main.cpp has
a.h -> a.cpp
a.cpp has
a.h
myslots.h
and the QT app method inside a.cpp as:
int A::inputData(){
...
A a
myslots ms;
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
....
}
myslots.h has:
a.h and inherited from A as:
class myslots : public QObject, public A {
Q_OBJECT
public slots:
void clickButton(A &a);
signals:
void buttonClicked();
};
myslots.cpp has:
myslots.h and the following method
void myslots::clickButton(A &a) {
cout <<"I am called successfully"<<endl;
a.perform_action(-2.3, 4.5, 4.4);
emit this->buttonClicked();
}
I get the following error:
QObject::connect: No such slot myslots::clickButton(&a)
Actually i want to pass three double values from three textlines say: 1.3, 2.4, 4.5 to a function by clicking the button, where the function to be called is in another class that is inherited by myslots.h and accepts three parameters.
Currently am just testing whether i am able to call the function properly or not but am not.
Thanks
This will not work. In SIGNAL or SLOT must be a type, not an object or its reference. I mean this
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
must be
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(A)));
But then you must catch another error - where is A in your SIGNAL? Why do you use A in the signal at all?
Some issues:
1 - When tou derive from QObject, always put the Q_OBJECT macro in the private section of your class, do something like
class MyClass: public QObject {
Q_OBJECT
public:
...
this Q_OBJECT macro is important for classes that have signals and slots.
2 - In the connect statement you have to tell Qt the type of the parameters, not the names you use, that is, if you slot function is
void myslots::clickButton(A &a);
The you connect to it with
connect(emiterObject, SIGNAL(someSignal(A)), targetObject, SLOT(clickButton(A)));
Since Qt 5.x you are able to use a lambda expression as slot.
So your connect statement should look like:
QObject::connect(button, &QPushButton::clicked, [a, ms](){ms.clickButton(&a);});
But, using a and ms as local variables is not realy a good thing, as stated by Karsten Koop.
The error is in your :
QObject::connect(button, SIGNAL(clicked()), &ms, SLOT(clickButton(&a)));
it has to be:
QObject::connect(button, SIGNAL(clicked(A)), &ms, SLOT(clickButton(A)));
the type that you send in the SIGNAL must be in SLOT that receives that type.

Object::connect: No such signal when obj is returned as shared_ptr

Using Qt's signal & slot, I get error:
Object::connect: No such signal FvOverlayPolygon::mouseHoveredOnElemSig(rf::AbstractFvOverlay*)
My other connects work fine and I've checked everything I can think of (refered to 20 ways to debug Qt signals and slots too). Because I personally for the first time use shared_ptr for Qt for this sample, I suspect there might be something wrong in how I use shared_ptr. I really appreciate your opinions.
concreteFvOverlay.cpp
#include "rf_common/abstractFvOverlay.h"
void FvScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
rf::AbstractFvOverlay::Ptr item_overlay;
item_overlay = this->createOverlay(myItemMenu, &pixmap_overlay); // this inherits Class A
bool con1 = connect(item_overlay.get(), SIGNAL(mouseHoveredOnElemSig(rf::AbstractFvOverlay*)), this, SLOT(mouseHoveredOnElem(rf::AbstractFvOverlay*)));
}
}
This overlay is instantiated in this:
abstractFvOverlay.cpp
boost::shared_ptr<rf::AbstractFvOverlay> FvPolygonScene::createOverlay(QMenu *menu, QPixmap *pixmap_overlay)
{
return boost::shared_ptr<rf::AbstractFvOverlay>(new FvOverlayPolygon(menu, *pixmap_overlay));
}
overlay.h
#include <QGraphicsItem>
#include <boost/shared_ptr.hpp>
class AbstractFvOverlay : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
AbstractFvOverlay(QMenu *contextMenu, QGraphicsItem *parent, QGraphicsScene *scene);
virtual ~AbstractFvOverlay();
typedef boost::shared_ptr<AbstractFvOverlay> Ptr;
signals:
void mouseHoveredOnElemSig(AbstractFvOverlay *i);
For your interest, the reason I use shared_ptr here is I want to do interface-based programming (not sure if this is an official way to call this style but what I mean is defining behavior in abstract classes and only for some behaviors I describe them in concrete classes, which Java allow).
You must use full scope names of types in signal declaration even if you're in same scope. Replace signal void mouseHoveredOnElemSig(AbstractFvOverlay *i); with void mouseHoveredOnElemSig(rf::AbstractFvOverlay *i); or use AbstractFvOverlay without scope in your connect.