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)));
}
Related
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
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());
}
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.
I have got problem in Qt. I have to make two windows:
In the first one you can click on 10 buttons and each button have to add an item(name of the button) in comboBox in the second window. But I can't refer to this comboBox. I am out of any ideas :(
I tried to make the variable protected and public, but it doesn't work. I had included window2.h to window1 and I'm trying to do something like this:
//this is in window1
void window1::on_button1_clicked() {
window2::combo->addItem("button1");
}
You can connect the button's click signals to a slot in the second window. This slot will add the information to the combobox.
To do that, you will need to distinguish the signals from each other. The best way to do this is to use a QSignalMapper.
class window1 {
Q_OBJECT
... // your other definitions...
QSignalMapper* signalMapper;
};
window1::window1 (/*your constructor's parameters*/) {
signalMapper = new QSignalMapper(this); // Will map each buttons' signals to a signal with a QString parameter.
// You can do an iteration instead of this if your buttons are on a container.
signalMapper->setMapping(button1, QString("button1"));
signalMapper->setMapping(button2, QString("button2"));
// ...
signalMapper->setMapping(button10, QString("button10"));
// Same comment as above applies here.
connect(button1, SIGNAL(clicked()),
signalMapper, SLOT(map());
connect(button2, SIGNAL(clicked()),
signalMapper, SLOT(map());
// ...
connect(button10, SIGNAL(clicked()),
signalMapper, SLOT(map());
connect(signalMapper, SIGNAL(mapped(QString)),
window2, SLOT(updateCombo(QString)));
}
class window2 {
Q_OBJECT
... // your other definitions...
public slots:
void updateCombo(QString);
};
void window2::updateCombo(QString str) {
combo->addItem(str);
}
Alternatively to the QSignalMapper approach you could name the button objects in window1 (setName("buttonXYZ")), connect the clicked signals to a slot in window2 and ask for the object name of the sender (sender()->name()).
So in the receiving slot you could do :
m_combo->addItem(sender()->name());
or
if(sender()->name() == "Button1") {
m_combo->addItem("Foo");
}
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
}