How to used itemExpanded with a subclass of QTreeWidgetItem - c++

I do have a connection related to SIGNAL/SLOT use in Qt/C++
I have wrote an app using a QTreeWidget + QTreeWidgetItems. For the need of the app, I had to subclass QTreeWidgetItem to MyQTreeWidgetItem in order to add some parameters.
Since I move from QTreeWidgetItem class to MyQTreeWidgetItem class,
connect(this, SIGNAL(itemExpanded(MyQTreeWidgetItem*)),
this, SLOT(onSubTreeDisplay(MyQTreeWidgetItem*)));
is not working anymore.
The issue is
QObject::connect: No such signal PulsTreeWidget::itemExpanded(MyQTreeWidgetItem*) in ../puls_connect/pulstreewidget.cpp:33
I think that the issue is coming from the fact that itemExpanded expect QTreeWidgetItem and not MyQTreeWidgetItem. But if I replace MyQTreeWidgetItem by QTreeWidgetItem such as
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
this, SLOT(onSubTreeDisplay(MyQTreeWidgetItem*)));
the run complain that SIGNAL/SLOT do not have the same type and this normal.
My item is defined through MyQTreeWidgetItem and not QTreeWidgetItem as I have subclass it.
The connect is done in the QTreeWidget part
Any idea ?
thanks

You can do the signal slot connection like below
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(onSubTreeDisplay(QTreeWidgetItem*)));
Then inside onSubTreeDisplay(), dynamic cast QTreeWidgetItem argument to MyQTreeWidgetItem like below
void onSubTreeDisplay(QTreeWidgetItem* item)
{
MyTreeWidgetItem* myItem = dynamic_cast<MyTreeWidgetItem*>(item);
if (myItem)
{
//cast is successful. you can use myItem
}
}

Related

Connecting two signals with different arguments

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 :)

Adding QListWidgetItem To QListWidget

So I have a class SnapshotPanel : public QListWidget that I am trying to add a QListWidgetItem to dynamically, however it when ever I try I get a segfault. I have verified that my code to add the item is correct as I can add to the list when I construct my SnapshotPanel. However I cannot add to the panel when the code is called via signals and slot, insight into what I am missing would be apprecited.
Here is the constructor:
SnapshotPanel::SnapshotPanel(QWidget *parent):QListWidget(parent)
{
this->setViewMode(QListWidget::IconMode);
this->setIconSize(QSize(256,256));
this->setResizeMode(QListWidget::Adjust);
QIcon icon("icon.jpeg");
QListWidgetItem *widget = new QListWidgetItem(icon,"Earth");
this->addItem(widget);
}
So is there any reason I wouldn't be able to use the following code when called via signals and slots:
{
QIcon icon("icon.jpeg");
QListWidgetItem *widget = new QListWidgetItem(icon,"Earth");
this->addItem(widget);
}
I think it should just work. "Slots are normal C++ functions" according the documentation.
If you are using multiple threads you need to look into the connection mechanism. Perhaps you need to use queued connections. You would change your connect statements from:
connect(button, &QPushButton::clicked, this, &MainWidget::on_button_clicked);
to
connect(button, &QPushButton::clicked, this, &MainWidget::on_button_clicked, Qt::QueuedConnection);
But read the official documentation here. A SO question (basically pointing you back to the documentation) is here.

QT Signal / Slot

I've got a question about signals and slots. In my app, I want to connect a signal from one object to a textEdit in a dialog window. My signal emits a QString; if I violate encapsulation (by making the UI public instead of private) and connect the signal directly to the textEdit it works. But I feel that it's not the right way. If I make something like the following:
connect(m_osgWidget->picker.get(), SIGNAL(setX(QString)), m_addAgentDlg, SLOT(getX(QString)));
where:
void getX(QString)
{
this->ui.textEdit(QString);
}
It gives me an error that I can't use QString in this this->ui.textEdit(QString); I need the QString from setX() signal pasted into the textEdit of m_addAgentDlg. How this can be done? Where did I make a mistake?
I am sorry to say this, but you need to learn basic C++. The proper syntax is this for such things in C++ with Qt:
connect(m_osgWidget->picker.get(), SIGNAL(setX(const QString&)), m_addAgentDlg, SLOT(getX(const QString&)));
// Why do you call it getX? Should it be called setText instead?
void getX(const QString& string)
{
ui->textEdit->setText(string);
}

Qt: Connecting multiple input widgets with valueChanged(int) signals to single slot?

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.

Signals and Slots with Qtoolbutton

I have created a ToolButton with my qt designer and Im trying to connect it to a slot.
I wrote this
connect(ui->toolButton_addfiles, SIGNAL(triggered()), this, SLOT(changeDirectory()));
Im able to run the program but when I press the button I see the following log into my qt Application Output :
Object::connect: No such signal QToolButton::triggered() in ../RightDoneIt/rightdoneit.cpp:10
Object::connect: (sender name: 'toolButton_addfiles')
Object::connect: (receiver name: 'RightDoneIt')
If I change the toolButton_addfile to some action like (actionChange_addfile) it will work fine.
How can I make this connection work ?
As the error says, there's no signal triggered() but triggered(QAction*) in the QToolButton.
Edit
In the connect function you must have the signal signature like triggered(QAction*) since QToolButton class has no signal triggered() (with no parameter) declared
You could use the auto-connection process of Qt.
In the class referencing your UI, create a slot called :
on_toolButton_addfiles_clicked();
Exemple :
See : A Dialog With Auto-Connect
class ImageDialog : public QDialog, private Ui::ImageDialog
{
Q_OBJECT
public:
ImageDialog(QWidget *parent = 0);
private slots:
void on_okButton_clicked();
};
Hope this helps !
Edit : No triggered signals in qAbstractButton. See http://doc.qt.nokia.com/4.7/qabstractbutton.html
QToolButton() has the signal method triggered(QAction *), this signal is to be addressed if triggering of the related QAction connected to the QToolbutton is of interest. At the same time QToolButton is inherited from QAbstractButton(), which has the toggled(bool checked) signal. If you want to catch the signal triggered by pressing/unpressing the tool button, you may do as following:
auto toolbutton = new QToolButton(this);
connect(toolbutton , &QAbstractButton::toggled, this, []() { // your code });
Alternatively (I have not checked if this solution works), you may define explicitly, what signal to be caught
connect(toolbutton , qOverload<bool>(&QToolButton::toggled), this, [](bool val) { // your code});
I'm guessing you're creating a QAction, adding it to the QToolButton and trying to connect it to a slot in your own class?
You can connect your slot to either the QToolButton::triggered(QAction*) signal or directly to the QAction::triggered() signal. Either way, the QAction must be added to the QToolButton through QWidget::addAction(QAction*), the slot's method signature must match the signal's signature, and the connect invocation must include the signal/slot parameters, not just the names of the signal and slot.