I am able to find the QRadioButton
this way:
for(int i = 0; i < ui->verticalLayout->count(); i++)
{
QRadioButton* r = qobject_cast<QRadioButton*>(ui->verticalLayout->itemAt(i)->widget());
if(r->isChecked())
//found it!
}
But I don't like this way of iterating over elements and would like to use the foreach
construct.
My first attempt fails:
foreach(QRadioButton* child, ui->verticalLayout->findChildren<QRadioButton*>())
{
if(child->isChecked())
//found it!
}
Problem is that the ui->verticalLayout->findChildren<QRadioButton*>()
returns zero elements. It also returns no elements with findChildren<QObject*>()
. Can someone please explain this behaviour?
Note: the title of this question is almost identical to mine, but it is related to python Qt, and does not contain any helpful information for me.
Experimentally I figured that ui->verticalLayout->children().count()
returns zero where as ui->verticalLayout->count()
returns the number of elements I have in the verticalLayout
. This implies that itemAt(i)
and findChild<QRadioButton*>()
do not access the same list. Looking at the Qt documentation on children()
did not help me.
Can someone point me to a good material on Qt child parent concepts? I am assuming that this has nothing to do with accesing nested objects which is what I am trying to accomplish.
As suggested by Kuba Ober, the answers to this question contains valuable information on another topic, whereas his answer makes clarification on my question about children of layout. Thus this is not a duplicate question.
The widgets are not children of the layout in the sense of being QObject
children - they are children of the parent widget. A QWidget
can only be a child of another QWidget
- thus you can't ever expect widgets to be layout's children. While new QWidget(new QWidget())
works, new QWidget(new QHBoxLayout())
won't compile.
You could iterate a widget's children of a given type as follows:
// C++11
for (auto button : findChildren<QRadioButton*>()) if (button->isChecked()) {
...
}
// C++98
Q_FOREACH (QWidget * button, findChildren<QRadioButton*>())
if (button->isChecked()) {
...
}
If you're using C++11, you should use the range-based for loop, not the now obsolete foreach
or Q_FOREACH
.
To iterate the child widgets managed by a layout, you need an iterator adapter for the layout. For example:
#include <QLayout>
#include <QDebug>
#include <QPointer>
#include <utility>
template<class WT> class IterableLayoutAdapter;
template<typename WT>
class LayoutIterator {
QPointer<QLayout> m_layout;
int m_index;
friend class IterableLayoutAdapter<WT>;
LayoutIterator(QLayout * layout, int dir) :
m_layout(layout), m_index(dir>0 ? -1 : m_layout->count()) {
if (dir > 0) ++*this;
}
friend QDebug operator<<(QDebug dbg, const LayoutIterator & it) {
return dbg << it.m_layout << it.m_index;
}
friend void swap(LayoutIterator& a, LayoutIterator& b) {
using std::swap;
swap(a.m_layout, b.m_layout);
swap(a.m_index, b.m_index);
}
public:
LayoutIterator() : m_index(0) {}
LayoutIterator(const LayoutIterator & o) :
m_layout(o.m_layout), m_index(o.m_index) {}
LayoutIterator(LayoutIterator && o) { swap(*this, o); }
LayoutIterator & operator=(LayoutIterator o) {
swap(*this, o);
return *this;
}
WT * operator*() const { return static_cast<WT*>(m_layout->itemAt(m_index)->widget()); }
const LayoutIterator & operator++() {
while (++m_index < m_layout->count() && !qobject_cast<WT*>(m_layout->itemAt(m_index)->widget()));
return *this;
}
LayoutIterator operator++(int) {
LayoutIterator temp(*this);
++*this;
return temp;
}
const LayoutIterator & operator--() {
while (!qobject_cast<WT*>(m_layout->itemAt(--m_index)->widget()) && m_index > 0);
return *this;
}
LayoutIterator operator--(int) {
LayoutIterator temp(*this);
--*this;
return temp;
}
bool operator==(const LayoutIterator & o) const { return m_index == o.m_index; }
bool operator!=(const LayoutIterator & o) const { return m_index != o.m_index; }
};
template <class WT = QWidget>
class IterableLayoutAdapter {
QPointer<QLayout> m_layout;
public:
typedef LayoutIterator<WT> const_iterator;
IterableLayoutAdapter(QLayout * layout) : m_layout(layout) {}
const_iterator begin() const { return const_iterator(m_layout, 1); }
const_iterator end() const { return const_iterator(m_layout, -1); }
const_iterator cbegin() const { return const_iterator(m_layout, 1); }
const_iterator cend() const { return const_iterator(m_layout, -1); }
};
template <class WT = QWidget>
class ConstIterableLayoutAdapter : public IterableLayoutAdapter<const WT> {
public:
ConstIterableLayoutAdapter(QLayout * layout) : IterableLayoutAdapter<const WT>(layout) {}
};
It's used as follows:
#include <QApplication>
#include <QLabel>
#include <QHBoxLayout>
int main(int argc, char ** argv) {
QApplication app(argc, argv);
tests();
QWidget a, b1, b3;
QLabel b2;
QHBoxLayout l(&a);
l.addWidget(&b1);
l.addWidget(&b2);
l.addWidget(&b3);
// Iterate all widget types as constants
qDebug() << "all, range-for";
for (auto widget : ConstIterableLayoutAdapter<>(&l)) qDebug() << widget;
qDebug() << "all, Q_FOREACH";
Q_FOREACH (const QWidget * widget, ConstIterableLayoutAdapter<>(&l)) qDebug() << widget;
// Iterate labels only
qDebug() << "labels, range-for";
for (auto label : IterableLayoutAdapter<QLabel>(&l)) qDebug() << label;
qDebug() << "labels, Q_FOREACH";
Q_FOREACH (QLabel * label, IterableLayoutAdapter<QLabel>(&l)) qDebug() << label;
}
Some rudimentary tests look as follows:
void tests() {
QWidget a, b1, b3;
QLabel b2;
QHBoxLayout l(&a);
IterableLayoutAdapter<> l0(&l);
auto i0 = l0.begin();
qDebug() << i0; Q_ASSERT(i0 == l0.begin() && i0 == l0.end());
l.addWidget(&b1);
l.addWidget(&b2);
l.addWidget(&b3);
IterableLayoutAdapter<> l1(&l);
auto i1 = l1.begin();
qDebug() << i1; Q_ASSERT(i1 == l1.begin() && i1 != l1.end());
++i1; qDebug() << i1; Q_ASSERT(i1 != l1.begin() && i1 != l1.end());
++i1; qDebug() << i1; Q_ASSERT(i1 != l1.begin() && i1 != l1.end());
++i1; qDebug() << i1; Q_ASSERT(i1 != l1.begin() && i1 == l1.end());
--i1; qDebug() << i1; Q_ASSERT(i1 != l1.begin() && i1 != l1.end());
--i1; qDebug() << i1; Q_ASSERT(i1 != l1.begin() && i1 != l1.end());
--i1; qDebug() << i1; Q_ASSERT(i1 == l1.begin() && i1 != l1.end());
IterableLayoutAdapter<QLabel> l2(&l);
auto i2 = l2.begin();
qDebug() << i2; Q_ASSERT(i2 == l2.begin() && i2 != l2.end());
++i2; qDebug() << i2; Q_ASSERT(i2 != l2.begin() && i2 == l2.end());
--i2; qDebug() << i2; Q_ASSERT(i2 == l2.begin() && i2 != l2.end());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With