Qt get children from layout - c++

I try to hide all widgets in layout. But looks like findChildren doesn't
work for layout.
Here's my sample code:
QLayout * layout = widget -> findChild<QLayout *> (layoutName);
QList<QWidget *> list = layout -> findChildren<QWidget *> ();
cout << list.size() << endl;
size is 0, but inside this layout I have a few widgets.
But the same code works fine if I try to get widgets from parent widget.
How I can get them from appropriate layout?

The layout does not "inject" itself in the parent-child tree, so the widgets stay (direct) children of their parent widget.
You could use QLayout::count() and QLayout::itemAt() instead.

You can simply iterate over the layout's items, using itemAt(), then test whether the item is a widget:
for (int i = 0; i < gridLayout->count(); ++i)
{
QWidget *widget = gridLayout->itemAt(i)->widget();
if (widget != NULL)
{
widget->setVisible(false);
}
else
{
// You may want to recurse, or perform different actions on layouts.
// See gridLayout->itemAt(i)->layout()
}
}

It's very late but if anyone finds here like me, here is my solution:
I tried #braggPeaks answer(it's same as #Frank Osterfeld answer) but it failed. Then I modified like this and it works like a charm. (I have no idea why it works, because my layout has no null items but still I have to check if it has.)
for (int i = 0; i < this->layout->count(); ++i) {
QWidget *w = this->layout->itemAt(i)->widget();
if(w != NULL)
w->setVisible(false);
}

Since layout is not part of widget hierarchy, the widget has to be queried from parent but then indexOf can be used to see if it belongs and its location
QLayout * top_l= layout(); // The parent widgets layout
// Find your layout that you want to search inside
QHBoxLayout * hbox = top_l->findChild<QHBoxLayout*>(QString("horizontalLayout_2"));
if (hbox != 0) {
std::cout << "Found horizontalLayout_2!"<<std::endl;
QPushButton * st = findChild<QPushButton*>(QString("startButton"));
if (st != 0) {
std::cout << "Found startButton in top level widget"<<std::endl;
int idx = hbox->indexOf(st);
if (idx >=0) {
std::cout << "Found startButton in hbox layout at location : "
<<idx<<std::endl;
}
}
};

Responding to an old post, but I wanted a simple way to disable all widgets contained in a layout or any child-layout. This worked for my purposes:
void setEnabledWidgetsInLayout(QLayout *layout, bool enabled)
{
if (layout == NULL)
return;
QWidget *pw = layout->parentWidget();
if (pw == NULL)
return;
foreach(QWidget *w, pw->findChildren<QWidget*>())
{
if (isChildWidgetOfAnyLayout(layout,w))
w->setEnabled(enabled);
}
}
bool isChildWidgetOfAnyLayout(QLayout *layout, QWidget *widget)
{
if (layout == NULL or widget == NULL)
return false;
if (layout->indexOf(widget) >= 0)
return true;
foreach(QObject *o, layout->children())
{
if (isChildWidgetOfAnyLayout((QLayout*)o,widget))
return true;
}
return false;
}

I think it is easier to create a QWidget as a container and put your widgets inside of that "container widget", this way you could access your widgets by calling findChildren on the "container widget":
auto children = ui->containerWidget->findChildren<QWidget *>();
for (auto child : children) {
child->setVisible(false);
}

Did you try children() method instead of findChildren() ? Maybe you are getting a 'bad' layout from widget -> findChild<QLayout *> (layoutName) method. Try to find children right after creating the layout - so you are sure the layout is correct. Doing so you will be able do determine what function works wrong.

Related

Fetch values from QWidgets added to QTreeWidgets via QTreeWidgetItems

I've searched many places and found lots of interesting information, but none of that seems to work for what I want. I've tried to follow the solution shown at https://stackoverflow.com/a/9986293/11035837 to no avail.
Basics of my structure: I have a QTreeWidget. I add top level QTreeWidgetItems dynamically (upon the push of a button in a header button box). Each top level QTreeWidgetItem then gets other widgets added to it using:
QTreeWidget* treeWidget = new QTreeWidget;
QTreeWidgetItem* new_record = new QTreeWidgetItem;
QPushButton* add_child = new QPushButton;
QLineEdit* user_input = new QLineEdit;
treeWidget->setItemWidget(new_record,1,add_child);
treeWidget->setItemWidget(new_record,2,user_input);
The add_child button works perfectly. I have a display that inserts all my QLabels, QLineEdits, and QPushButtons in a tree tiered fashion. My buttons work for adding and removing the visual display of items even triggering the visibility of various other elements.
However, I cannot get the user input data out of the QLineEdits to process for anything (such as writing to an output file).
I have my output function iterate through the QTreeWidget:
QTreeWidgetItemIterator iter(treeWidget);
while (*iter)
{
stream.writeStartElement("record");
if ((*iter) != nullptr)
{
for (int i = 0; i < 12; i++)
{
if((*iter)->text(i) != nullptr) stream.writeAttribute("record_name", (*iter)->text(i));
}
}
for (int i = 0; i < 12; i++)
{
if ((*iter) != nullptr && (*iter)->child(i) != nullptr)
{
for (int j = 0; j < 12; j++)
{
if ((*iter)->child(i)->text(j) != nullptr) stream.writeAttribute("record_name", (*iter)->child(i)->text(j));
}
}
}
++iter;
}
This prints as many records with record_name displayed as were created, but it doesn't display any of the other data, because the pointer defined by (*iter)->child(i) is nullptr regardless of i
I then tried using data();
stream.writeAttribute("record_name ", (*iter)->data(2, Qt::UserRole).toString());
This doesn't err out because of nullptr, but it prints out record_name="" rather than record_name="<user_input>"
I'm able to get the user input for QLineEdit widgets that are not in the QTreeWidget, just not the ones in the tree. I assume if I can figure out how to get the data out of the QLineEdits within the tree that I should be able to adapt that to getting the QLineEdits out of a custom QWidget also within the tree.
I found a solution using information from https://www.qtcentre.org/threads/23228-typecast-of-QWidget
stream.writeAttribute("record_name", qobject_cast<QLineEdit*>(treeWidget->itemWidget((*iter), 2))->text() );
The issue was that the QLineEdit widgets were being recalled as QWidgets and not as QLineEdit widgets and as such the text() method/function was not available to it until I cast it back as the desired type.

can't remove custom QWidget from QVBoxLayout

This is a reasonably well documented issue but after some reading and trying, I am still struggling to remove a custom widget from a QVBoxLayout.
in the MyClass.h file, I am declaring a vector to hold QPointer objects:
vector< QPointer<MyWidget> > myWidgets;
In the MyClass.cpp file, I am creating the widget and adding it to the layout with:
// create object
void MyClass::createMyWidget() {
QPointer<MyWidget> myWidget(new MyWidget());
myWidgets.push_back(myWidget);
}
//display in layout
void MyClass::displayWidgets() {
for(int i = 0; i < myWidgets.size(); i++) {
myWidgets[i]->setId(i);
ui->myVBoxLayout->addWidget(myWidgets[i].data());
}
}
I have tried the following to remove the widgets from the layout:
void MyClass::removeMyWidget(int id) { // id of the custom widget. id is also position in the layout
for(int i = 0; i < myWidgets.size(); i++) {
if(items[i]->getId() == id) { //correctly targets the widget
// method 1
ui->myVBoxLayout->removeItem(ui->myVBoxLayout->takeAt(i));
//method 2
//ui->myVBoxLayout->removeWidget(items[i]);
}
}
}
Neither of the above works and I have not been able to work out why.
In Qt, a layout is just a manager for sizing and positioning widgets (and other items, like nested layouts or spacers).
When you remove an item from a layout, it is still a child-widget of the parent widget that is layed out by the layout, only that its geometry is no longer managed. Still, the widget is present and displayed, and the user can interact with it.
You also have to remove it or hide it. Try
void MyClass::removeMyWidget(int id){ // id of the custom widget. id is also position in the layout
for(int i=0;i<myWidgets.size();i++){
if(items[i]->getId() == id){ //correctly targets the widget
ui->myVBoxLayout->removeWidget(myWidgets[i]);
delete myWidgets[i]; // <= Delete widget
}
}
}

Qt: What is the best approach to update the widgets in the GUI?

I used a QTimer to control when should I update my widgets.
Many people suggested to do:
void uavTabGroup::updateMgr()
{
setUpdatesEnabled(false);
for (updateCtr = 0; updateCtr < max_num; updateCtr++)
{
if (infoView[updateCtr] != NULL)// its a child widget to the current widget
{
infoView[updateCtr]->updateDisplay();
}
}
setUpdatesEnabled(true);
}
But I found that to update the widgets sequentially give me better results:
void uavTabGroup::updateMgr()
{
if (updateCtr >= max_num)
updateCtr = 0;
if (infoView[updateCtr] != NULL)
{
infoView[updateCtr]->updateDisplay();
}
updateCtr++;
}
In this case the updateMgr() is called at higher frequency, but for each time only one of its child widget is updated.
What is the best way?

Empty scrollArea QT C++

I am loading data from xml executed by timer. xml is parsed and populated in entity object.
In a loop im taking the data from entity object and populate QCommandLinkButton's. and in the end a batch of buttons are set into the verticalLayout and then in scrollArea.
but every time data is loaded its appends to the old data. How can I empty the content of the srollArea before repopulating scrollArea.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->setLayout(ui->verticalLayout);
}
you can use setWidget replace setLayout.and then new data coming,you can call takeWidget to remove old data.
MainWindow::methodExecudedByTimer(){
foreach(int i, map.keys()){
QCommandLinkButton* buttonEmail = new QCommandLinkButton(this);
Email em = map[i];
buttonEmail->setText(em.__toString());
ui->verticalLayout->addWidget(buttonEmail);
}
ui->scrollArea->takeWidget();
QWidget *widget = new QWidget();
QSize size = ui->scrollArea->size();
widget->setMinimumSize(size.width(),size.height());
widget->setLayout(ui->verticalLayout);
ui->scrollArea->setWidget(widget);
}
I found the answer in the depths of Internet.
before foreach i added this:
qDebug() << "check if layout filled";
if(ui->verticalLayout->count() > 0){
qDebug() << "empty layout";
QLayoutItem *item = NULL;
while ((item = ui->verticalLayout->takeAt(0)) != 0) {
delete item->widget();
}
}else{
// on app first run
qDebug() << "layout empty";
}

Removing QWidgets from a QGridLayout

I have a QGridLayout where I add my custom QWidgets.
When I try to delete all of them they are supposedly removed from the layout (as the function layout.count() returns 0) but they are still shown in the interface and I can interact with them.
Here you have the way I add widgets:
void MyClass::addCustomWidget(CustomWidget *_widget, int r, int c)
{
layout->addWidget(_widget, r, c);
_widget->show();
}
And here the way I delete them:
void MyClass::clearLayout()
{
qDebug() << "Layout count before clearing it: " << layout->count();
int count = layout->count();
int colums = layout->columnCount();
int rows = layout->rowCount();
int i=0;
for(int j=0; j<rows; j++)
{
for(int k=0; k<colums && i<count; k++)
{
i++;
qDebug() << "Removing item at: " << j << "," << k;
QLayoutItem* item = layout->itemAtPosition(j, k);
if (!item) continue;
if (item->widget()) {
layout->removeWidget(item->widget());
} else {
layout->removeItem(item);
}
qDebug() << "Removed!";
}
}
qDebug() << "Layout count after clearing it: " << layout->count();
}
Any kind of help or tip to delete items/widgets correctly from a QGridLayout?
P.D. : I have seen on the internet that a lot of people deletes the widget directly (delete _widget) after removing them from the layout. In my case it is not possible as I need to mantain that widgets in memory.
You can also use deleteLater() to avoid issue with maintaining child
count during iterations:
for (int i = 0; i < gridLayout.count(); i++)
{
gridLayout.itemAt(i)->widget()->deleteLater();
}
Just to be clear. You didn't "delete" the widgets. You only removed them from layout. Removing from layout means only that widget will be no more managed (resized/positioned) by this layout BUT it doesn't mean that widget will be "deleted" (in C++ way). Also widget won't be magically hidden. Your widget after removing from layout still leaves in widget it was created / managed in. So owner of this layout still has this widget as child (visible child).
You have to
hide widget or if you're sure it will not be used anymore
delete widget with "delete" keyword
Also you don't need to call removeWidget(item->widget()); removeItem(item) will be enough for all layout items (even those with widget inside)
Try
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0);
It is supposed to be safe. If for any reasons it doesn't work, you can use a collection of widgets or layoutitems, which is updated every time you add a widget. Then to delete you loop on the collection and remove each element from the layout.
Header:
class Grid : QGridLayout
{
public:
Grid(QWidget* parent);
void addElement(QWidget* element, int x, int y);
void delElement(int x, int y);
void resize(int width, int height);
void clear();
protected:
QSize size;
};
void Grid::clear(){
for(int i = 0;i<size.width();i++){
for(int j = 0;j<size.height();j++){
QLayoutItem* item = takeAt(i*size.width() + j);
if(item != NULL) delete item;
}
}
}
None of these answers worked for me. In my situation, I have several object each with their own QChartView. The idea is that the user selects which object they want to view and a common area central in the main window is updated with the QChartView of the user-selected object. This should have been straightforward, simply remove the widget from the chart area, add a new one. What ended up working for me was this:
while( QLayoutItem* item = ui->mPlotArea->layout()->takeAt(0) )
{
item->widget()->setVisible(false);
ui->mPlotArea->layout()->removeItem(item);
}
ui->mPlotArea->layout()->addWidget( pv );
pv->setVisible(true);
Where mPlotArea is a QFrame and pv is a derived class of QChartView. I can't explain why the other answers did not worked, but I spent a couple of hours trying different widgets and different ways of removing without deleting, organizing, etc...