Qt - "No such slot", although there is - c++

I'm trying to connect a button to a function, so when I push the button, the function gets called with a specific parameter. I have
class FieldGridWidget : public QWidget
{
Q_OBJECT
public:
FieldGridWidget(QWidget *parent=0);
~FieldGridWidget();
public slots:
void resizeGrid(int n);
private:
QGridLayout* _gridLayout;
QVector<QPushButton*> _buttonGrid;
};
then the button
_button3 = new QPushButton("3x3", this);
and I'm trying to connect it so if clicked, the resizeGrid function gets called with the parameter 3. For this, I'm trying
connect(_button3, SIGNAL(clicked()), _fieldGrid, SLOT(resizeGrid(3))); //this is line 21
but I get the runtime error
QObject::connect: No such slot FieldGridWidget::resizeGrid(3) in ../filename.cpp:21
What am I doing wrong? Or how can I make it so if I press the button, resizeGrid(3) gets called?
Thank you!

You cannot pass a value to a slot in Qt directly, as in SLOT(resizeGrid(3)). The argument to SLOT should be just the signature of your method (resizeGrid()).
There are two ways how to add an argument to a slot. This question about how to pass arguments to a slot shows some solutions, repeated here. (Go upvote them!)
The modern way : Qt5 and C++11
Instead of connecting a slot, connect to functor, as described by Kuba Ober in this answer:
connect(action1, &QAction::triggered, this, [this]{ onStepIncreased(1); });
connect(action5, &QAction::triggered, this, [this]{ onStepIncreased(5); });
connect(action10, &QAction::triggered, this, [this]{ onStepIncreased(10); });
connect(action25, &QAction::triggered, this, [this]{ onStepIncreased(25); });
connect(action50, &QAction::triggered, this, [this]{ onStepIncreased(50); });
Old fashioned : QSignalMapper
You can use a QSignalMapper to perform what you want, as described by TonyK in this answer:
QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect (action1, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action5, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action10, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action25, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action50, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
signalMapper -> setMapping (action1, 1) ;
signalMapper -> setMapping (action5, 5) ;
signalMapper -> setMapping (action10, 10) ;
signalMapper -> setMapping (action25, 25) ;
signalMapper -> setMapping (action50, 50) ;
connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(resizeGrid(int))) ;

Signatures of signals and slots must match - if not fully, then at least partially.
For example, you can connect a signal(int, float) to a slot(int), but you can't connect a signal(void) to a slot(int).
It is "sort of possible" to specify a slot parameter in the connection syntax, providing you are using Qt 5:
connect(_button3, &QPushButton::clicked, [=](){ _fieldGrid->resizeGrid(3); });
Of course, you could use the signal mapper in this particular case, but it is a rather clumsy solution - extra verbose and limited to only a single int, string, widget or QObject parameter. With lambdas those limitations do not apply.

clicked() and resizeGrid() signatures should be the same, so you can create function, for example, resizeGridForButton(), where resizeGrid(3) will be executed, or change resizeGrid() signature and transfer int n = 3 into it.
And it's not possible (example in the answer of vsimkus) to send params in connect(...). Also you can send them in emit part through signal params.
Here is a good article about slots and signals.

Related

Game like clicker: Problem with increase value with timeout C++ [duplicate]

I want to override mouseReleaseEvent with a bunch of QActions and QMenus...
connect(action1, SIGNAL(triggered()), this, SLOT(onStepIncreased()));
connect(action5, SIGNAL(triggered()), this, SLOT(onStepIncreased()));
connect(action10, SIGNAL(triggered()), this, SLOT(onStepIncreased()));
connect(action25, SIGNAL(triggered()), this, SLOT(onStepIncreased()));
connect(action50, SIGNAL(triggered()), this, SLOT(onStepIncreased()));
So I want to pass an argument to the slot onStepIncreased (as you can imagine they are 1,5,10,25,50). Do you know how I can do it?
With Qt 5 and a C++11 compiler, the idiomatic way to do such things is to give a functor to connect:
connect(action1, &QAction::triggered, this, [this]{ onStepIncreased(1); });
connect(action5, &QAction::triggered, this, [this]{ onStepIncreased(5); });
connect(action10, &QAction::triggered, this, [this]{ onStepIncreased(10); });
connect(action25, &QAction::triggered, this, [this]{ onStepIncreased(25); });
connect(action50, &QAction::triggered, this, [this]{ onStepIncreased(50); });
The third argument to connect is nominally optional. It is used to set up the thread context in which the functor will execute. It is always necessary when the functor uses a QObject instance. If the functor uses multiple QObject instances, they should have some common parent that manages their lifetime and the functor should refer to that parent, or it should be ensured that the objects will outlive the functor.
On Windows, this works in MSVC2012 & newer.
Use QSignalMapper. Like this:
QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect (action1, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action5, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action10, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action25, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
connect (action50, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
signalMapper -> setMapping (action1, 1) ;
signalMapper -> setMapping (action5, 5) ;
signalMapper -> setMapping (action10, 10) ;
signalMapper -> setMapping (action25, 25) ;
signalMapper -> setMapping (action50, 50) ;
connect (signalMapper, SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int))) ;
The QObject::sender() function returns a pointer to the object that has signaled to the slot. You could use this to find out which action was triggered
Maybe you can subclass QAction with an m_increase member variable.
Connect the triggered() signal to a slot on your new QAction subclass and emit a new signal (e.g. triggered(int number)) with the correct parameter.
e.g.
class MyAction:public QAction
{
public:
MyAction(int increase, ...)
:QAction(...), m_increase(increase)
{
connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));
}
protected Q_SLOTS:
void onTriggered()
{
emit triggered(m_increase);
}
Q_SIGNALS:
void triggered(int increase);
private:
int m_increase;
};
You can use std::bind This is a functional object adapter that allows functional objects to be adaptee to a given number of parameters.
For example, you create your own chat server. That contains two classes: ChatServer and ServerWorker.
ChatServer is QTcpServer class and ServerWorker is QTcpSocket ( manage the socket on the server side).
Signals in ServerWorker header:
void error();
In your ChatServer header you define these private slots:
void userError(ServerWorker *sender);
In cpp file you create these object and in incomingConnection method that run after socket connect, you connect slots and signals using std::bind:
void ChatServer::incomingConnection(qintptr socketDescriptor)
{
//some code
connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker));
}
std::bind creates a functor with some fixed arguments. For example connect(worker, &ServerWorker::error, this, std::bind(&ChatServer::userError, this, worker)); will result in this->userError(worker); to be called every time the worker emits the error signal.
userErrorslot is executed every time a socket connected to a client encounters an error. It has signature:
void ChatServer::userError(ServerWorker *sender)
{
//some code
}
Example
QVector<QAction*> W(100);
W[1]= action1;
W[5]= action5;
W[10]= action10;
W[25]= action25;
W[50]= action50;
for (int i=0; i<100; ++i)
{
QSignalMapper* signalmapper = new QSignalMapper();
connect (W[i], SIGNAL(triggered()), signalmapper, SLOT(map())) ;
signalmapper ->setMapping (W[i], i);
connect (signalmapper , SIGNAL(mapped(int)), this, SLOT(onStepIncreased(int)));
}

Qt C++: static assertion failed: Signal and slot arguments are not compatible

I am trying to start a countdown timer in a workerthread when the user clicks a pushbutton. The value at which the timer starts the count down depends on the selected radius button from my GUI.
I use a signalmapper to pass the starting value from the timer as a parameter. However I get this error when trying to compile my code:
static assertion failed: Signal and slot arguments are not compatible
My code:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
connect(ui->btnTakeSnap, SIGNAL(clicked()), this, SLOT(startTimerWorker()) );
}
void MainWindow::startTimerWorker()
{
myThread = new QThread(this);
workerObj = new workerTimer();
QSignalMapper* signalMapper = new QSignalMapper (this) ;
connect(myThread, &QThread::started, signalMapper, SLOT(map()) );
if(ui->rdBtntimer1s->isChecked())
{signalMapper -> setMapping (myThread, 1000) ; }
if(ui->rdBtntimer3s->isChecked())
{signalMapper -> setMapping (myThread, 3000) ; }
if(ui->rdBtntimer5s->isChecked())
{signalMapper -> setMapping (myThread, 5000) ;}
connect(signalMapper, SIGNAL(mapped(int)), workerObj, SLOT(&workerTimer::countDown(int)));
workerObj->moveToThread(myThread);
myThread->start();
}
class workerTimer : public QObject
{
Q_OBJECT
public:
explicit workerTimer(QObject *parent = nullptr);
signals:
public slots:
void countDown(int selectedTimer);
};
Could someone explain me what I may be doing incorrectly?
Thanks
connect(myThread, &QThread::started, signalMapper, SLOT(map()) );
connect(signalMapper, SIGNAL(mapped(int)), workerObj, SLOT(&workerTimer::countDown(int)));
You are mixing 2 kinds of connections rendering the entire statement meaningless. see http://doc.qt.io/qt-5/signalsandslots.html
connect(myThread, SIGNAL(started()), signalMapper, SLOT(map()) );
connect(signalMapper, SIGNAL(mapped(int)), workerObj, SLOT(countDown(int)));
Beside IIBeldus's answer, one more reason for this error to happen is that you use the new syntax of QT's connect() function that is :
connect(
sender, &Sender::valueChanged,
receiver, &Receiver::updateValue
);
and the signal (valueChanged) arguments was different from the slot (updateValue) arguments, this will make the compiler to try implicit conversion of the signal and slot arguments and you have one of 2 possibilities:
1. The compiler will try to make auto conversion (implicit conversion) and the conversion succeeded.
2. The compiler will try to make auto conversion (implicit conversion) and the conversion did not succeed.
From QT documentation:
Possibility to automatically cast the types if there is implicit
conversion (e.g. from QString to QVariant)
I posted this answer because i faced the same problem and the accepted answer did not solve my problem, so try this answer if the accepted answer did not help you

Enabling QCheckBox when QCheckBox is checked

I'm new to Qt and GUI programming and am unsure of the best way to connect a signal to to a slot when the parameter list doesn't match. I have a settings dialog box made with Qt Designer and it contains a series of QCheckBoxes and QLineEdits, with the QLineEdits disabled by default. I want to enable a QLineEdit when the QCheckBox next to it is checked.
At first I thought to connect the QCheckBox::stateChanged signal to the QLineEdits::setEnabled slot, but when I looked I found they had different parameter types so this obviously won't work:
connect(checkBox1, SIGNAL(stateChanged(int)), lineEdit1, SLOT(setEnabled(bool)));
connect(checkBox2, SIGNAL(stateChanged(int)), lineEdit2, SLOT(setEnabled(bool)));
connect(checkBox3, SIGNAL(stateChanged(int)), lineEdit3, SLOT(setEnabled(bool)));
Next I thought to create setLineEditEnabled(int) function in the dialog box class to enable the appropriate QLineEdit when a QCheckBox is checked:
connect(checkBox1, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEditEnabled(int)));
connect(checkBox2, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEditEnabled(int)));
connect(checkBox3, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEditEnabled(int)));
When I came to write the setLineEditEnabled() function I realised there's no way to know which QCheckBox sent the signal, so I don't know which QLineEdit should be enabled:
void SettingsDialog::setLineEditEnabled(int checkState)
{
????->setEnabled(checkState == Qt::Checked);
}
The only solution I can think is to think of is to have a a series of functions in the dialog class, with one for each checkbox:
connect(checkBox1, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEdit1Enabled(int)));
connect(checkBox2, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEdit2Enabled(int)));
connect(checkBox3, SIGNAL(stateChanged(int)), settingsDialog, SLOT(setLineEdit3Enabled(int)));
void SettingsDialog::setLineEdit1Enabled(int checkState)
{
lineEdit1->setEnabled(checkState == Qt::Checked);
}
void SettingsDialog::setLineEdit2Enabled(int checkState)
{
lineEdit2->setEnabled(checkState == Qt::Checked);
}
void SettingsDialog::setLineEdit3Enabled(int checkState)
{
lineEdit3->setEnabled(checkState == Qt::Checked);
}
However, that seems a bit messy (there are actually seven QCheckBox-QLineEdit pairs so I'd need seven functions), and I feel I'm missing something that would make this easier. If I knew which object sent the signal I could do it with a single function, which would be tidier.
Is there a way get the object that sent the signal from the slot function?
If there's no way to get the signalling object, is there a better solution to this that doesn't involve having multiple functions in the dialog class for enabling the QLineEdits?
Thanks for your help.
In this case you can use the QCheckBox::toggled(bool) signal instead of stateChanged(int).
connect(checkBox1, SIGNAL(toggled(bool)), lineEdit1, SLOT(setEnabled(bool)));
connect(checkBox2, SIGNAL(toggled(bool)), lineEdit2, SLOT(setEnabled(bool)));
connect(checkBox3, SIGNAL(toggled(bool)), lineEdit3, SLOT(setEnabled(bool)));
However, inside a slot, you can get the QObject that sent the signal calling the sender() method. See QObject::sender()
Another option would be using a QSignalMapper:
// Set up a map, just for convenience.
std::map<QCheckBox*, QLineEdit*> widgetMap;
widgetMap.emplace(checkBox1, lineEdit1);
widgetMap.emplace(checkBox2, lineEdit2);
widgetMap.emplace(checkBox3, lineEdit3);
QSignalMapper signalMapper;
connect(signalMapper, SIGNAL(mapped(QWidget*)), SLOT(singleSlotHandlingThemAll(QWidget*)));
connect(checkBox1, SIGNAL(statusChanged(int)), signalMapper, SLOT(map()));
connect(checkBox2, SIGNAL(statusChanged(int)), signalMapper, SLOT(map()));
connect(checkBox3, SIGNAL(statusChanged(int)), signalMapper, SLOT(map()));
signalMapper->setMapping(checkBox1, checkBox1);
signalMapper->setMapping(checkBox2, checkBox2);
signalMapper->setMapping(checkBox3, checkBox3);
And here's the singleSlotHandlingThemAll() implementation:
void singleSlotHandlingThemAll(QWidget* widget)
{
// Provided widget is one of the check-boxes.
QCheckBox* checkBox = static_cast<QCheckBox8>(widget);
QLineEdit* relatedLineEdit = widgetMap[checkBox];
relatedLineEdit->setEnabled(checkBox->isChecked());
}

MDI window and QSignalMapper basics

First of all my apologies for big looking question but indeed it's not. I’m reading Foundation of qt development book and while reading fourth chapter author tells the basics of MDI window by showing this example :
MdiWindow::MdiWindow( QWidget *parent ) : QMainWindow( parent ) {
setWindowTitle( tr( "MDI" ) );
QWorkspace* workspace = new QWorkspace;
setCentralWidget( workspace );
connect( workspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(enableActions()));
QSignalMapper* mapper = new QSignalMapper( this );
//my problem is in this line
connect( mapper, SIGNAL(mapped(QWidget*)), workspace, SLOT(setActiveWindow(QWidget*)) );
createActions();
createMenus();
createToolbars();
statusBar()->showMessage( tr("Done") );
enableActions();
}
His this para of explanation completely eluded me (is it me or others having problem understanding it too?) :
Next, a signal mapping object called QSignalMapper is created and
connected. A signal mapper is used to tie the source of the signal to
an argument of another signal. In this example, the action of the menu
item corresponding to each window in the Window menu is tied to the
actual document window. The actions are in turn connected to mapper.
When the triggered signal is emitted by the action, the sending action
has been associated with the QWidget* of the corresponding document
window. This pointer is used as the argument in the mapped(QWidget*)
signal emitted by the signal mapping object.
My question : I still don’t get what is signal mapper class, how it’s used and what's functionality it's doing in the example above?. Can anyone please explain the above para using easy terms? also It’d be awesome if you could please teach me about mapper class’s basics with simple example? possibly in layman’s term?
P.S : A confusion is when we have MDI window, do menu changes (though actions are disabled/enabled) e.g suppose for one particular document we have menu “File/close” and for other document we have “File/remaper” ?
The QSignalMapper is used to re-emit signals with optional parameters. In other words (from the documentation):
This class collects a set of parameterless signals, and re-emits them
with integer, string or widget parameters corresponding to the object
that sent the signal.
A good example (also from the doc - take a look at it) is set as follows:
Suppose we want to create a custom widget that contains a
group of buttons (like a tool palette). One approach is to connect
each button's clicked() signal to its own custom slot; but in this
example we want to connect all the buttons to a single slot and
parameterize the slot by the button that was clicked.
So imagine you have a number of buttons encapsulated in a class, say ButtonWidget, with a custom signal void clicked(const QString &text). Here is the definition:
class ButtonWidget : public QWidget {
Q_OBJECT
public:
ButtonWidget(QStringList texts, QWidget *parent = 0);
signals:
void clicked(const QString &text);
private:
QSignalMapper *signalMapper;
};
The constructor could then be defined like the following:
ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
setLayout(gridLayout);
}
So what happens here? We construct a grid layout and our buttons of type QPushButton. The clicked() signal of each of these is connected to the signal mapper.
One of the forces using QSignalMapper is that you can pass arguments to the re-emitted signals. In our example each of the buttons should emit a different text (due to the definition of our signal), so we set this using the setMapping() method.
Now all that's left to do is map the signal mapper to the signal of our class:
connect(signalMapper, SIGNAL(mapped(const QString &)),
this, SIGNAL(clicked(const QString &)));
Assume we have a testing class called TestClass then ButtonWidget can be used thusly:
TestClass::TestClass() {
widget = new ButtonWidget(QStringList() << "Foo" << "Bar");
connect(widget, SIGNAL(clicked(const QString &)),
this, SLOT(onButtonClicked(const QString &)));
}
void TestClass::onButtonClicked(const QString &btnText) {
if (btnText == "Foo") {
// Do stuff.
}
else {
// Or something else.
}
}
By using the signal mapper this way you don't have to declare and manage all the buttons and their clicked signals, just one signal pr. ButtonWidget.
The buttom line is that the signal mapper is great for bundling multiple signals and you can even set parameters when it re-emits them. I hope that gave some intuition about the usage of QSignalMapper.
Your example code
The explanation (your "para") states that all the actions are each individually mapped to a specific QWidget*. When triggering an action its respective QWidget* will be passed to the slot QWorkspace::setActiveWindow(QWidget*) of workspace, which in turn activates the widget.
Also note that the mapping from action to widget has to happen somewhere in your code. I assume it is done in createActions() or enableActions() perhaps.
A QSignalMapper allows you to add some information to a signal, when you need it. This object internally have a map like QMap<QObject*,QVariant>. Then you connect an object to it, and when the slot is called, it re-emit the signal with the associated value.
Workflow:
mySignalMapper:
[ obj1 -> 42 ]
[ obj2 -> "obiwan" ]
[ obj3 -> myWidget ]
connect(obj1,mySignal,mySignalMapper,SLOT(map())); // idem for obj2 and obj3
(obj2 emits "mySignal")
-> (mySignalMapper::map slot is called)
-> (sender() == obj2, associated data = "obiwan")
-> (mySignalMapper emits mapped("obiwan"))
I was going to add a more detailed example, but Morten Kristensen was faster than me ;)

How to pass variables to slot methods in QT?

I'm making a little chat messenger program, which needs a list of chat channels the user has joined. To represent this list graphically, I have made a list of QPushButtons, which all represent a different channel. These buttons are made with the following method, and that's where my problem kicks in:
void Messenger::addToActivePanels(std::string& channel)
{
activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
pushButton = new QPushButton(activePanelsContents);
pushButton->setObjectName("pushButton");
pushButton->setGeometry(QRect(0, 0, 60, 60));
pushButton->setText("");
pushButton->setToolTip(QString(channel.c_str()));
pushButton->setCheckable(true);
pushButton->setChecked(false);
connect(pushButton, SIGNAL(clicked()), this, SLOT(switchTab(channel)));
}
(activePanelContents is a QWidget that holds the list.)
The point is that each button should call the switchTab(string& tabname) method when clicked, including the specific channel's name as variable. This implementation doesn't work though, and I haven't been able to find out how to properly do this.
For strings and integers, you can use QSignalMapper. In your Messenger class, you would add a QSignalMapper mapper object, and your function would look like:
void Messenger::addToActivePanels(std::string& channel)
{
activePanelsContents = this->findChild<QWidget *>(QString("activePanelsContents"));
pushButton = new QPushButton(activePanelsContents);
// ...
connect(pushButton, SIGNAL(clicked()), &mapper, SLOT(map()));
mapper.setMapping(pushButton, QString(channel.c_str()));
}
and after you have added all channels to your active panels, you call
connect(&mapper, SIGNAL(mapped(const QString &)), this, SLOT(switchTab(const QString &)));
Use QSignalMapper to pass variables;
QSignalMapper* signalMapper = new QSignalMapper (this) ;
QPushButton *button = new QPushButton();
signalMapper -> setMapping (button, <data>) ;
connect (signalMapper, SIGNAL(mapped(QString)), this,
SLOT(buttonClicked(QString))) ;
in slot i.e
void class::buttonClicked(QString data){
//use data
// to get sender
QSignalMapper *temp = (QSignalMapper *)this->sender();
QPushButton *btn = (QPushButton *)temp->mapping(data);
// use btn
}
Hope my ans may help you
Don't use the sender method unless you absolutely have to. It ties the function directly to being used only as a slot (can't be called directly). Retain the behavior of having the function accept a string and simply make a mechanism by which you can call it.
One method, among others you might find, is to leverage use of QSignalMapper. It will map objects to values and regenerate signals of the appropriate signature.
I would do it with "relay" objects:
Create TabSwitchRelay which is a sub-class of QObject with this constructor:
TabSwitchRelay::TabSwitchRelay(QObject *parent, Messanger * m, const QString & c)
: QObject(parent), m_messanger(m), m_channel(c)
{
}
It also has a slot clicked():
void TabSwitchRelay::clicked()
{
m_messager->switchTab(m_channel);
}
Now replace the line in your code that does connect with this:
TabSwitchRelay * tabRelay = new TabSwitchRelay(pushButton, this, channel);
connect(pushButton, SIGNAL(clicked()), tabRelay, SLOT(clicked()));
It's not tested but you get teh basic idea.
You could try having your switchTab slot take no argument and use QObject::sender to get the object that sent the signal.
Messenger::switchTab()
{
QObject* sender = this->sender();
QPushButton* button = qobject_cast<QPushButton*>(sender);
if(button)
{
// Do stuff...
}
}
if you're using Qt5, you can do it through lambda:
connect( sender, &Sender::valueChanged, [=](){ myMethod(5); } );
void myMethod(int value)
{
// do stuff
}