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
Related
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)));
}
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() );
});
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);
});
I am trying to perform interthread communication in Qt (C++). I have a worker thread which does some calculations and I want the workerthread to return its results to the main thread when done. I therefor use a connect, I know thanks to debugging, that the signal is successfully being emit but that it is the slot that isn t being executed and I don t understand why.
The relevant pieces of code:
webcamClass::webcamClass(QObject *parent) : QObject(parent)
{
workerThread = new QThread(this);
workerClassObj = new workerClass();
//connect for image
connect(workerClassObj, SIGNAL(mySignal(QPixmap)), this, SLOT(mySlot(QPixmap)));
//connect(&workerClassObj, workerClass::mySignal(QPixmap), this, webcamClass::mySlot(QPixmap));
connect( workerThread, SIGNAL(started()), workerClassObj, SLOT(getImage()) );
workerClassObj->moveToThread(workerThread);
}
void webcamClass:: foo()
{
workerThread->start();
}
void workerClass::getImage()
{
qint64 successFailWrite;
QImage img;
QPixmap pixmap;
... do some stuff with pixmap...
qDebug()<<"going to emit result";
emit mySignal(pixmap);
qDebug()<<"emitted";
}
void webcamClass::mySlot(QPixmap p)
{qDebug()<<"this message should be displayed"; }
The corresponding header files:
class workerClass : public QObject
{
Q_OBJECT
private:
public:
explicit workerClass(QObject *parent = nullptr);
signals:
void mySignal(QPixmap);
};
webcamClass::webcamClass(QObject *parent) : QObject(parent)
{
Q_OBJECT
public:
explicit webcamClass(QObject *parent = nullptr);
public slots:
void mySlot(QPixmap p);
private:
QThread *workerThread;
workerClass *workerClassObj;
};
The code above just outputs:
going to emit result
emitted
but unfortunately doesn t output this message should be displayed.
webcamClass belongs to the parent thread, while workerClass belngs to -you guessed it- the worker thread.
Could someone explain how to setup my connect so that mySlot() gets triggered?
Thanks!
In the code you pasted in pastebin.com/UpPfrNEt you have a getVideoFrame method that uses while (1). If this method is called, it runs all the time and blocks the event loop from handling signals. You can solve it in many ways, I think the best practice will be to replace the while(1) with something else.
If possible, I highly encourage you to use the new Signal Slot syntax:
connect( SOURCEINSTANCE, &CLASS::SIGNAL, TARGETINSTANCE, &CLASS::SLOT );
In your case, that could be:
connect( workerClassObj, &workerClass::mySignal, this, &webcamClass::mySlot );
Specificallyfor your case, if you want to pass Signals and Slots between threads, you have to be careful. First, check the connection type for the connect call, its acutally the last parameter.
connect( workerClassObj, &workerClass::mySignal, this, &webcamClass::mySlot, Qt::QueuedConnection );
For a detailed explanation look here:
http://doc.qt.io/qt-5/signalsandslots.html
If you want to pass custom types, you have to declare them as metatypes first.
Add e.G. this in your constructor:
qRegisterMetaType("MyDataType");
Please make sure, that your custom datatype has a default constructor and be aware that afaik, references cannot be passed across threads.
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.