With Qt 4.8 I create a number of input widgets (QSpinBox, QSlider) programmatically.
In the end, I would like to have a single method to handle changes of any of these input widgets, ideally by index.
However, these widgets only have a Signal with parameter, e.g. valueChanged(int).
This is not compatible with QSignalMapper()'s Slot map().
As it was pointed out in the comments, the connection does work!
connect( spinbox, SIGNAL( valueChanged(int) ),
signalMapper, SLOT( map() )
);
Now I just need to get the value, but this cannot be done via the sender() method anymore, because this now is the SignalMapper.
Original question:
Is there another way besides (re)implementing QSignalMapper with additional parameters or a parameter-less valueChanged() for the widget or using objectName and QObject::sender() in order for the Slot to see which element changed (and get the new value)?
You can use QAbstractSpinBox::editingFinished() and QAbstractSlider::sliderReleased() as your signals, they are parameterless.
Unfortunately there is no parameterless version of QAbstractSlider::valueChanged() so if you want a signal emitted continuously as the slider moves, you may need to subclass QSlider and create it. E.g.
class MySlider : public QSlider
{
...
private slots:
void HandleValueChanged(int) { emit valueChanged(); }
signals:
void valueChanged();
};
MySlider::MySlider(...)
{
connect(this, SIGNAL(valueChanged(int)), this, SLOT(HandleValueChanged(int)));
}
Though I admit, this may not be the most elegant solution.
If I understand your question and what you have until now correctly, you are missing two parts.
mapper->setMapping(<spinbox>, <id or name or pointer to the spinbox);
connect(mapper, SIGNAL(mapped(<datatype you used in setMapping()>),
this, SLOT(HandleValueChanged(<datatype you used in setMapping()>)));
So the HandleValueChanged() slot will receive an identifier of your sender, then you can directly access the value of the sender with the appropriate getter.
The method setMapping() takes either and integer, QString or a pointer to the widget itself as second argument. This is then forwarded via the mapped() signal of the mapper, so you can later identify which widget emitted the signal.
Related
I have a question about QSignalMapper.
I have simple application, a calculator. And I have something like this, I click button and I want to display it. But I have problem, I don't know how to assign string to a button. It only want to work with integers. But i know it is possible to do it with strings. And I need to do it on strings, because then I want to convert it to double type. I have idea how do rest of things I want to do, but this QSignalMapper is killing me.
QSignalMapper *signalMapper = new QSignalMapper(this);
connect(ui->Button0, SIGNAL(clicked()),
signalMapper, SLOT(map()));
signalMapper->setMapping(ui->Button0, '0');
I tried to do something with QString but it did not help.
I will be grateful for any help.
I don't think you need signalMapper to accomplish what you are looking for. A standard connect() function call will likely work.
Try using Qt5 syntax for creating the connection
Change your connect function call to include units if you are using the old way.
Edited - Example syntax for connect
New way. Note that param datatypes are not included in the connect
call.
connect(
sender, &Sender::valueChanged,
receiver, &Receiver::updateValue
);
Old way
connect(
sender, SIGNAL( valueChanged( QString, QString ) ),
receiver, SLOT( updateValue( QString ) )
);
Edited - Method 1 --> Using two known C++ objs
Create a signal function emitting a QString
Create a slot function recieving a QString
Connect the two QStrings together
Handle as desired
Edited - Method 2 --> Using QML exclusively
Declare signal(string str) in QML
Use existing btn onClicked event and call declared signal
Handle to signal via slot and do as needed with text
Edited - Method 3 --> Use QSignalMapper if dealing with MVC and indexs
1. See Qt documentation
Use lambda method to connect button click and text
connect(button, &QPushButton::clicked, [this, text] { clicked(text);
});
Hopefully one of these methods provides some help to solving your problem!
I want to get a QTreeView widget to emit a clicked(const QModelIndex&) signal whenever a pushbutton is clicked. This is so that I can get a list of all the items that are selected within the QTreeView at the time of clicking the pushbutton. Now I thought I could connect two signals with distinct arguments (Qt Connect signals with different arguments), however when I try to call
connect(ui.pbAddVideo, SIGNAL(clicked()), ui.treeView_video, SIGNAL(clicked(const QModelIndex&)));
I get the error message:
QObject::connect: Incompatible sender/receiver arguments QPushButton::clicked() --> QTreeView::clicked(QModelIndex)
Have I misunderstood the whole signal forwarding concept?
As always, many thanks.
Firtsly, what index you must send by clicking a button from your tree?
Secondly, since c++11 standart you can do something like that:
connect(ui.pbAddVideo, &QPushButton::clicked, [=] { emit ui.treeView_video->clicked(ui.treeView_video->currentIndex()); });
I would solve your problem with the following approach:
First you have to handle the button click:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(onLoadVideo()));
In the slot you need to get the list of selected items from the tree view and do something with them:
void MyClass::onLoadVideo()
{
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex *video, selectedVideos) {
// Play the video.
}
}
You are connecting one SIGNAL() to another SIGNAL() which is perfectly ok but their parameters should match. In your case the second signal has a parameter(i.e QModelIndex) that the first signal does not have.
Have I misunderstood the whole signal forwarding concept?
Yes.
When a signal is emitted, Qt takes the signal's arguments and passes them to the connected slot/signal.
Valid signal-slot connection
For example, suppose you connect the QSlider::valueChanged(int) signal to the QSpinBox::setValue(int) slot. When the valueChanged() signal is emitted, this is effectively how the slot gets called:
// "Emitting a signal" == "Calling the signal function"
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
receiver->setValue(sliderSignalValue);
}
Valid signal-signal connections
Similarly, if you connect the QSlider::valueChanged(int) signal to the QSpinBox::valueChanged(int) signal, the code behaves like this:
QSlider::valueChanged(int sliderSignalValue)
{
QSpinBox *receiver = findReceiver();
emit receiver->valueChanged(sliderSignalValue);
}
Now, if you want to connect in the opposite direction (connect(ui.treeView_video, SIGNAL(clicked(const QModelIndex&)), ui.pbAddVideo, SIGNAL(clicked()));, it's perfectly fine:
QTreeView::clicked(const QModelIndex& viewSignalValue)
{
QPushButton *receiver = findReceiver();
emit receiver->clicked(); // No problem. The viewSignalValue is simply ignored.
}
Invalid signal-slot connection
However, for the connection that you wanted to make, the code will need to behave like this:
QPushButton::clicked()
{
QTreeView *receiver = findReceiver();
emit receiver->clicked(/*???*/); // ERROR: You need to pass a QModelIndex value!
}
You've got a parameter mismatch. QTreeView::clicked() requires a QModelIndex value, but QPushButton::clicked() cannot provide this. Therefore, you cannot connect these two together.
Does this make sense?
Many thanks to #vahancho whose answer I have adopted. There is no point in using the "clicked()" signal from the qTreeView since I do not need to wait for this to access the data inside. Hence:
connect(ui.pbAddVideo, SIGNAL(clicked()), this, SLOT(addVideo()));
void VigilWidget::addVideo() {
QItemSelectionModel *selectionModel = ui.treeView_video->selectionModel();
QModelIndexList selectedVideos = selectionModel->selectedIndexes();
foreach (QModelIndex video, selectedVideos) {
qDebug().nospace() << video.data(0);
}
}
As to my question about how signal to signal connections work, thanks to everyone for taking the time to explain this :)
I want to send two integers, string and fret, to a SLOT that will process the location of the button that was pressed. The SIGNAL and SLOT argument have to match so I am thinking I need to reimplement the QPushButton::clicked event method. Problem is I am new to Qt and could use some direction.
connect(&fretBoardButton[string][fret], SIGNAL(clicked()), this, SLOT (testSlot()));
If you use the C++11 connection syntax you can use a lambda with calls testSlot with your string and fret arguments:
connect(&fretBoard[string][fret], &QPushButton::clicked, [this, string, fret]() {
testSlot(string, fret);
});
This code creates a lambda using the [captures, ...](arguments, ...) { code } syntax. When you make the connection it captures the string and fret variable values, and will then pass them on to testSlot when the button is clicked.
There are Two approaches you could use to add the string and fret information. one is to use the sender() function to get the button which emitted the signal. you can the access fret and string if they are members of your button class so in the SLOT you would have.
MyPushButton *button = (MyPushButton *)sender();
button.getFret();
button.getString();
However since you are already subClassing QPushButton you could use a private SLOT to catch the buttonClicked signal and re-emit a signal with the right values.
In the constructor
connect(this, SIGNAL(clicked()), this, SLOT(reemitClicked()));
and then the reemit SLOT
void MyPushButton::reemitClicked()
{
emit clicked(m_fret, m_string);
}
be sure to add the appropriate private slot and public signal to you class
https://doc.qt.io/archives/qq/qq10-signalmapper.html see this artical for a good discussion on various ways to add an argument to signal.
I want to get a signal to call a function with certain arguments, like the example below.
QPushButton *yes = new QPushButton("Yes");
yes->connect(yes, SIGNAL(clicked()), NULL, SLOT(printf("You pressed a button")));
How do I accomplish this?
An often overlooked way to reverse signal/slot relationships is QObject::sender. You can call it in the receiving slot to get a handle on the QPushButton (using qobject_cast) and get the text from there. Alternatively you can use QSignalMapper to augment signals.
It seems very inefficient but you could create a new signal with a QString argument, which you connect to your pushbutton. The text contained will be defined on your emit call.
eg.
connect(yes, SIGNAL(clicked()), this, SLOT(emitHelloWorldText());
connect(this, SIGNAL(emitText(QString)), receiver, SLOT(dostuffWithText(QString)));
then your emitHelloWorldText method can be something like
void emitHelloWorldText() {
emit emitText("Hello world");
}
Then this can be picked up by your receiver class
void doStuffWithText(const QString &text) {
Unfortunately, the slot and the signal must have matching arguments. If you really need to stick with this interface, you could create an intermediary slot to propagate the received signal, but there’s no real way around.
I want to connect a signal clicked() from the button to a slot of different object.
Currently I connect signal to helper method and call desired slot from there:
connect(button, SIGNAL(clicked()), this, SLOT(buttonClicked()));
void buttonClicked() { // Helper method. I'd like to avoid it.
someObject.desiredSlot(localFunc1(), localFunc2());
}
But maybe there is a more simple and obvious way to do this?
is this what you want to do:
the signal clicked should be connected to the "desiredSlot" which takes two arguments that are returned by localFunc1 & 2 ??
this is not possible, as you can read in the QT docs. A slot can take less arguments than provided by the signal - but not the opposite way! (The documentation says "This connection will report a runtime error")
This ought to work with the new signal/slot mechanism in qt 5:
connect( button, &QPushButton::clicked, [&](){ someObject.desiredSlot( localFunc1(), localFunc2() ); } );
You will need to adjust the lambda capture to your needs.
In some cases, default arguments may help, e.g. declare desiredSlot as:
desiredSlot(int a=0, int b=0)
You cannot access members in default argument though.
That is not the way to connect signals and slots in QT. You should use:
connect(button, SIGNAL(clicked()), receiver, SLOT(slotToBeCalled());
Have a look at the QT documentation.