Can I somehow attach one QTreeWidgetItem to two (or more) nodes at once? Just like this:
parent1
└child1
parent2
└child1
If I just do addChild() on both parents, the child appears only on the first parent. Is that even possible? Or such result can be achieved only by completely copying of descendant?
The instructions say:
If the child has already been inserted somewhere else it won't be
inserted again.
This means that only option is to copy the child completely. Roughly so:
QTreeWidgetItem* copy(QTreeWidgetItem* item)
{
if (item == nullptr)
{
return nullptr;
}
QTreeWidgetItem* out = new QTreeWidgetItem(*item);
for (int i = 0; i < item->childCount(); i++)
{
out->addChild(copy(item->child(i)));
}
return out;
}
Related
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.
In sitecore I want to delete the newly added child.
item.DeleteChildren();
Delete all the children under item But I want to delete the most updated child or the newly added child.
I would just loop through the items and see which one was created most recently. Something like this:
Item newestItem = null;
foreach(Item child in parent.Children)
{
if (newestItem == null || child.Statistics.Created > newestItem.Statistics.Created)
{
newestItem = child;
}
}
if (newestItem != null)
{
newestItem.Delete();
}
I've used Item.Statistics.Created here, but Item.Statistics.Updated is also available
You can also use Linq:
var newestItem = item.Children.OrderByDescending(child => child.Statistics.Created).FirstOrDefault();
If (newestItem != null)
newestItem.Delete();
I have a list of layouts with items inside. It forms a custom table.
To clean the table i loop through all the layouts and take out the items one by one. Then delete the layout.
// Delete all items
QHBoxLayout* row = NULL;
while( !rowLyts_.isEmpty() && (row = rowLyts_.takeAt(0)) != 0 )
{
QLayoutItem *item;
while ((item = row->takeAt(0)) != 0)
delete item;
delete row;
}
This seems to work. But when i start filling the table again i see "ghosts" of the items that were there before cleaning. Most of the times they are between lines, behind the new objets. And they still work.
This happens too when you use only a layout with widgets.
I just want to clean the whole layout of layouts without deleting the content widget. A safe way to clean a layout!.
You are deleting the layout items, but not the widgets that the items used to manage. You must delete the widgets. All of the non-layout items will be deleted automatically when you delete the layout itself.
QHBoxLayout* row;
while(!rowLyts_.isEmpty() && (row = rowLyts_.takeAt(0)))
{
QLayoutItem *item;
while ((item = row->takeAt(0))) {
// The item will be deleted when the layout itself is
// destructed. Items such as spacers will return a null
// widget, its deletion is a safe no-op.
delete item->widget();
// We don't handle recursion into sublayouts.
// We check for it so that we won't leak the layout.
Q_ASSERT(!item->layout());
}
delete row;
}
The assert is there to make sure the layout that the code works on matches the implied precondition: there must be no sub-layouts, since the code as written doesn't recurse into them. Any sub-layouts, with their widgets, would leak (not memory leak, but resource leak). The assert will abort the execution if the precondition is violated.
clearing the layout can be done by:
QLayoutItem* item;
while ( ( item = row->takeAt( 0 ) ) != NULL )
{
delete item->widget();
delete item;
}
I have a QVBoxLayout inside a scrollArea. I dynamically add QFormLayouts.
widgetTreeStruct* tree = new widgetTreeStruct(QString::number(numberOfGraphs));
QFormLayout* layout = tree->getTree(); // get QFormLayout
ui->verticalLayout_2->addLayout(layout); //add to the vertical layout
At one point I need to remove all the added QFormLayouts from the QVBoxLayout.
I tried several ways to do this.
Using qDeleteAll()
qDeleteAll(ui->verticalLayout_2->children());
2.delete item one by one
QLayoutItem* child;
while((child = ui->verticalLayout_2->takeAt(0)) != 0)
{
if(child->widget() != 0)
{
delete child->widget();
}
delete child;
}
But nothing happened. Only thing is when I try to add items to QVBoxLayout again new items are added on top of the previously added items.
I sense that I have to redraw, repaint, update, refresh or something. I tried ui->verticalLayout_2->update(); but didn't work for me.
So, What should I do?
I recursively deleted all the children and it worked for me.
This is my code.
void Widget::remove(QLayout* layout)
{
QLayoutItem* child;
while(layout->count()!=0)
{
child = layout->takeAt(0);
if(child->layout() != 0)
{
remove(child->layout());
}
else if(child->widget() != 0)
{
delete child->widget();
}
delete child;
}
}
remove(ui->verticalLayout_2);
Probably the widgets's parent is the containing widget, not their layout (what is passed to their constructors for the parent parameter?).
Maybe QObject::dumpObjectTree() can help you to understand the parent-child relationships.
What happens with your approach 2 (which does not rely on the widgets being children in the QObject-sense of the layout) is that it removes all items from the layout with the takeAt() method but deletes none of them: The children of your toplevel QVBoxLayout are the QFormLayouts, so calling widget() on their QLayoutItems returns 0. Just use delete child unconditionally to delete the child QLayouts. However, this still does not delete the child widgets. You could either recursively call takeAt() on the child layouts or delete all children of the parent widget (your QScrollArea) or keep a list of widgets and/or layouts yourself.
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.