Qt: How to make Incompatible sender/receiver arguments compatible? - c++

Here is the code where I use connect,
connect(myTimer, SIGNAL(timeout()), ui.widget_2, SLOT(paintEvent(QPaintEvent *)));//draw lines
I use it to go to the slot paintEvent(QPaintEvent *) where I can draw some lines. But I have a message while debugging or running the program which is
QObject::connect: Incompatible sender/receiver arguments
QTimer::timeout() --> MapWidget::paintEvent(QPaintEvent*)
mainWindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
myTimer = new QTimer(this);
...
connect(myTimer, SIGNAL(timeout()), ui.widget_2, SLOT(paintEvent(QPaintEvent *)));//draw lines
...
}
ui.widget_2 is an instance of mapWidget.
mapWidget.cpp
void MapWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
...
}
I know that the arguments of the signal and slot are incompatible, but I don't know how to make them compatible. It seems that the arguments of MapWidget::paintEvent(QPaintEvent*) can't be ignored, nor can I add arguments to timeout().
I'm so confused.

Don’t try to connect to paintEvent(QPaintEvent *); that method isn’t a slot-method, so calling it via a signal wouldn't work even if you were somehow able to get the right arguments passed to it. Instead connect to the update() slot, which will see to it that the paintEvent(QPaintEvent *) method is called from the proper context, ASAP.

OP's specific use-case: Using a QTimer to paint a widget.
Jeremy Friesner's answer is correct for this use case.
General answer to title question, "How to make Incompatible sender/receiver arguments compatible?"
Use the new Qt 5 version of QObject::connect(): https://doc.qt.io/qt-5/signalsandslots-syntaxes.html
The Qt 5 style connect can do implicit conversions:
// OK: The new version of connect() lets the compiler convert int to double
connect(slider, &QSlider::valueChanged,
doubleSpinBox, &QDoubleSpinBox::setValue);
// ERROR: The old version of connect() needs the parameter types to be exactly the same
connect(slider, SIGNAL(valueChanged(int)),
doubleSpinBox, SLOT(setValue(double)));
Furthermore, you can use a lambda expression to connect to a slot whose parameters are truly incompatible with the signal parameters:
// Using QTimer::timeout() to trigger a call to QLabel::setText(const QString&)
connect(timer, &QTimer::timeout, [=] {
label->setText( QTime::currentTime()->toString() );
});

Related

How QT signals with parameters works

can someone tell me how exactly signals with parameters work? I mean... if i have declared signal f.e.:
void sign1(int)
how should i specify what integer i want to send with that signal? Also, can i declare signal with multiple arguments? Like:
void sign2(int, int)
And again... i want to send with sign2 two out of four variables that i have. Is that possible, and how it should be done? To specify my question below is a little more detailed example:
class Board
{
signals:
void clicked(int, int);
private:
int x1{4}; int x2{4}; int x3{5}; int x4{8};
}
and there is board.ui file with pushbutton. After pushbutton is clicked i want to send to the slot for example x1 and x3. Example:
connect(ui->button, SIGNAL(clicked(int, int)), obj2, slot2);
I hope that it's somehow clear. I will really appreciate your help.
QObject::connect() works like this (in the general case, not using lambda):
connect(obj1, obj1_signal, obj2, ob2_slot)
Since there is no signal clicked(int, int) in the class QPushButton (which I assume you are using), it cannot be use for the connection.
If you want to have the signal clicked(int, int) in a button, you can subclass QPushButton, add the signal, and using emit to send the signal where the click event is handled.
However, that is not a good design, since you will have to store a Board object (or at least a reference to it) in the button class, which is irrelevant to the class.
Instead, you can have a slot Board::buttonClicked(), connected to QPushButton::clicked(bool). Then in that slot, you can do emit Board::clicked(int, int).
The rule for signal/slot connection may be formulated as the following:
You can ignore signal arguments, and you cannot create slot arguments from nothing
What does it mean?
If your signal has n arguments, your slot shall have at most n arguments as well (beware of the types). See tabular below.
On first line, you have a signal with two arguments, thus, your slot can have two arguments (using all the signal arguments), or one argument (ignoring one argument of the signal) or no argument (ignoring both signal arguments)
On the second line, you have a signal valueChanged(int) with one argument. Your slot may have one or no argument (ignoring the signal argument) but may not have two
or more arguments as you cannot create values.
On the third line the signal textChanged(QString) cannot be connected with setValue(int) because we cannot create an int value from from QString.
The fourth line follow these rules. If the signal has no argument, the connected signal cannot create new arguments, thus update() is correct, setValue(int) isn't.
Another point that shall be looked at is overloading of signal/slots. It is the case where many signals/slots with the same name but with different numers or different types of arguments.
You may have the class QLCDNumber, where the slot display has many overload. In this cas, you have to explicitly defined which pair of signals slots, you would like to use as explained here
You can try out the following example:
Example :
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = new QWidget();
window->setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout *topLayout = new QVBoxLayout(window);
//Set up of GUI
QSlider *slider = new QSlider(Qt::Horizontal);
slider->setRange(0, 100);
QSpinBox *spin = new QSpinBox;
spin->setReadOnly( true );
QHBoxLayout *horizontalLayout = new QHBoxLayout;
horizontalLayout->addWidget(slider);
horizontalLayout->addWidget(spin);
topLayout->addLayout(horizontalLayout);
// using pointer to member function
QObject::connect(slider, &QSlider::valueChanged,
spin, &QSpinBox::setValue);
// set the slider position and hence the QSpinBox value too
slider->setValue(40);
// Uncommenting the following connect will result in a compile time error.
// The signal passes no arguments whereas the slot is expecting a single
// argument.
// By using function pointers we get compile time parameter list checking.
// Using the old-style SIGNAL/SLOT macros this would have been detected
// as a run time warning only.
//QObject::connect(slider, &QSlider::sliderPressed,
// spin, &QSpinBox::setValue);
QTextEdit *textEdit = new QTextEdit();
textEdit->setAttribute(Qt::WA_DeleteOnClose);
// Uncommenting the following connect will result in a compile time error.
// The signal is passing an incompatible parameter to the slot.
// By using function pointers we get compile time parameter type conversion.
// Using the old-style SIGNAL/SLOT macros this would have been detected
// as a run time warning only.
//QObject::connect(slider, &QSlider::sliderMoved,
// textEdit, &QTextEdit::setFontFamily);
window->show();
return app.exec();
}

Qt no such slot for method

I am trying to learn Qt (using CMake/c++). Here is piece of code that I wrote. I am trying to use connect functionality. There is no compiling error. I used also following command to re-generate moc file.
moc -o moc_SimulationRunner.cpp SimulationRunner.h
However I am still receiving this error while running my project:
QObject::connect: No such slot SimulationRunner::_OpenFileDialog(file_address)
my header file is:
class SimulationRunner : public QWidget
{
Q_OBJECT
public:
explicit SimulationRunner(QWidget *parent = nullptr);
private:
QGroupBox *createFirstExclusiveGroup();
private slots:
bool _OpenFileDialog(QLineEdit* i_line_edit, std::string = "");
};
and my cpp file is:
QGroupBox *SimulationRunner::createFirstExclusiveGroup()
{
QGridLayout *grid = new QGridLayout;
....
QLineEdit *file_address= new QLineEdit();
file_address->setPlaceholderText("File address");
QPushButton *file_address_button = new QPushButton("Browse...");
file_address_button ->setFixedWidth(75);
grid->addWidget(file_address, 0, 0, 1, 1);
grid->addWidget(file_address_button , 1, 1, 1, 1);
group_box->setLayout(grid);
QObject::connect(file_address_button , SIGNAL(clicked()), this, SLOT(_OpenFileDialog(file_address)));
return group_box;
}
bool SimulationRunner::_OpenFileDialog(QLineEdit* i_line_edit, std::string /* = "" */)
{
if (!i_line_edit)
return false;
QString filename = QFileDialog::getOpenFileName(
this,
"Open Document",
QDir::currentPath(),
"All files (*.*) ;; Document files (*.doc *.rtf);; PNG files (*.png)");
if (!filename.isNull())
{
qDebug() << "selected file path : " << filename.toUtf8();
}
i_line_edit->setText(filename);
return true;
}
The arguments (signature) of the signal and the slot have to match.
If you use the old string-based Syntax you have to define the arguments in the SIGNAL() and SLOT() macros. So you could connect for example like this:
QObject::connect(file_address_button, SIGNAL(clicked(bool)), this, SLOT(_ExampleSlotFunction(bool)));
You cannot connect to a slot with more or different arguments. However you can connect to a slot with fewer arguments, e.g. like this:
QObject::connect(file_address_button, SIGNAL(clicked(bool)), this, SLOT(_AnotherExampleSlotFunction()));
If you use the new QT5 Syntax (which is recommended because it should warn you already when compiling) you don't need to specify the arguments yourself:
QObject::connect(file_address_button, &QPushButton::clicked, this, &ExampleClass::_ExampleSlotFunction);
QObject::connect(file_address_button, &QPushButton::clicked, this, &ExampleClass::_AnotherExampleSlotFunction);
QT5 / C++11 workaround
To pass a different argument, you could connect a lambda function to the signal, in which you call the slot or function. So to pass your QLineEdit:
QObject::connect(
file_address_button, &QPushButton::clicked,
this, [file_address]() { _OpenFileDialog(file_address); }
);
Further explanations:
New Syntax
Default Arguments
You're misunderstanding the meaning of the argument in the connect.
the signature of your slot is
OpenFileDialog(QLineEdit*, std::string);
and the signature of your connect is
OpenFileDialog(QLineEdit*)
that's why you're getting the warning. You're not calling a function (so it can be resolved with the default parameter), you're pointing to a function, so the signature has to be the same.
Tip: unless you have to use Qt4, use the Qt5 connect syntax:
QObject::connect(file_address_button, &QPushButton::clicked, this, &SimulationRunner::OpenFileDialog);
for your specific case:
if you always pass file_address in your slot, then just remove the argument from the slot and use it directly in the body.
Otherwise, if you're using Qt4, the only workaround is to make an additional slot with the call with no object.
Qt4 way:
private slots:
void _dummySlot(){
_OpenFileDialog(file_address);
}
QObject::connect(file_address_button , SIGNAL(clicked()), this, SLOT(_dummySlot()));
Qt5 way, use lambda:
QObject::connect(file_address_button, &QPushButton::clicked, this, [this](){
_OpenFileDialog(address_name);
});

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

Qt: Upcasting a signal's parameter to the slot

I have a signal that is emitted w/a parameter taken from a subclass, but in one slot implementation, I don't need a special handler for the subclass; just the parent class. I thought that connect() would upcast that parameter, but unfortunately, I get:
QObject::connect: Incompatible sender/receiver arguments
emitter::mysignal(messageSubclass *) --> slotter::generalHandler(messageSuperclass *)
In emitter I have:
emit mysignal((messageSubclass *)msg_ptr );
There are various places in the code were I need to handle this messageSubclass specifically. But, in one place, a whole family of signals can be handled by just one slot method, so I have:
connect(emitter_ptr, SIGNAL(mysignal(messageSubclass *)), this, SLOT(generalHandler(messageSuperclass *)));
I really don't want to have to have generalHandler() be replaced by a couple of dozen specific handlers, that all do the same thing. Putting it another way, I'm trying to avoid something tedious like:
connect(emitter_ptr, SIGNAL(mysignal(messageSubclass *)), this, SLOT(specificHandler(messageSubclass *)));
connect(emitter_ptr, SIGNAL(mysignalA(messageSubclassA *)), this, SLOT(specificHandlerA(messageSubclassA *)));
connect(emitter_ptr, SIGNAL(mysignalB(messageSubclassB *)), this, SLOT(specificHandlerB(messageSubclassB *)));
connect(emitter_ptr, SIGNAL(mysignalC(messageSubclassC *)), this, SLOT(specificHandlerC(messageSubclassC *)));
...
void handlerClass::specificHandler(messageSubclass *msg_ptr) {
generalHandler((messageSuperclass *)msg_ptr);
}
void handlerClass::specificHandlerA(messageSubclassA *msg_ptr) {
generalHandler((messageSuperclass *)msg_ptr);
}
void handlerClass::specificHandlerB(messageSubclassB *msg_ptr) {
generalHandler((messageSuperclass *)msg_ptr);
}
void handlerClass::specificHandlerC(messageSubclassC *msg_ptr) {
generalHandler((messageSuperclass *)msg_ptr);
}
And again, I need to have
signals:
mysignal(messageSubclass *);
for other slots which need messageSubclass.
I'm using Qt 4.8

QT4 no such slot error

I know there are many many questions that are just the same, but none of them helps me:
class Form1 : public QMainWindow {
Q_OBJECT
public:
Form1();
virtual ~Form1();
public slots:
void langChange(const char* lang_label);
private:
Ui::Form1 widget;
void setLangStrings();
};
From1 constructor:
Form1::Form1() {
widget.setupUi(this);
connect(widget.btnL0, SIGNAL(clicked(bool)), this, SLOT(langChange("en")));
connect(widget.btnL1, SIGNAL(clicked(bool)), this, SLOT(langChange("fr")));
setLangStrings();
}
And I also have this langChange function implemented:
void Form1::langChange(const char* lang_label)
{
GL_LANG = lang_label;
setLangStrings();
}
I get this stupid error when the connect function is called:
No such slot Form1::langChange("sl") in Form1.cpp:15
I'm using NetBeans with QDesigner for the UI. I must say this QT4 is very difficult to learn.
You simply can't connect SIGNAL with bool as argument to SLOT with const char* as argument. To do this kind of stuff you have to use QSignalMapper. You have an example how to use it inside documentation. In your case, it's very simple, so you should handle it easly.
The SLOT function must have the same signature than the SIGNAL function
Edit: From the official Qt documentation (http://qt-project.org/doc/qt-4.8/signalsandslots.html):
The signature of a signal must match the signature of the receiving
slot. (In fact a slot may have a shorter signature than the signal it
receives because it can ignore extra arguments.)