I've created some dialog and on this dialog I have a vertical layout consisting of few checkboxes. I'd like to access those checkboxes during runtime via layout containing them. Is this possible?
If you have a dialog called myDialog you can access its layout via
myDialog->layout()
the layout itself inherits from QObject and like that has access to the QObject::children() method.
For example you could do something like this:
QCheckBox* currentCheckBox;
foreach( QObject* child, myDialog->layout()->children() )
{
currentCheckBox = qobject_cast< QCheckBox* >( child );
if( !currentCheckBox ) continue;
// do anything with the checkbox here
}
#Tim: had a similar question and found your answer very helpful.
But at least using PyQt4 I have found that layout itself seems to have no children (children() gives an empty list ) -- instead, they are "parented" by the widget that our layout is sitting on.
So in Python, that would be myDialog.children() instead of myDialog.layout().children().
And then, obviously, there could be other elements among these children, not necessarily coming from our layout (at least in my case there were).
Moreover, in a more general case, the order of QObject's children could be unstable -- though I think for checkboxes it's unlikely to be the case.
So, I have rather used the layout's .itemAt() method.
I suppose in C++ it would be something like
for( int i=0; ++i; i<layout->count() )
{
widget = layout->itemAt(i)->widget()
// ... do whatever you want
}
In Python, it was
for i in xrange( layout.count() ) :
widget = layout.itemAt( i ).widget()
# ... whatever )
May be there's something that has changed here between Qt3 and Qt4 ?
Related
you might be able to tell that I'm pretty new to QT...
My program contains a window with several Widgets in a QGridLayout. Some of these Widgets have a layout and child widgets themselves. Pressing the Tab key moves the focus like I expect it to, in the order I created the widgets.
Problems occur when a widget changes it's content (I do that by deleting all child widgets and then constructing new ones.) If I do that, new widgets are moved to the end of the tab chain (that indicates to me, that tab order is kind of global for a window). I have tried to use QWidget::setTabOrder() to reorder all widgets (I tried both, setting tab order for only the contends of the main window and setting it for the children too) but the actual order doesn't change. I did this by emitting a signal when the contend of a widget changes and connecting that to a slot on my main Window.
I think I understand the way the setTabOrder() function should work. I do something similar to this:
QWidget* a = firstWidget;
QWidget* b = secondWidget;
QWidget::setTabOrder(a,b);
for (int i = 0; i < widgets.size(); ++i) {
a = b;
b = widgets[i];
QWidget::setTabOrder(a,b);
}
Is there anything special one has to do when changing the tab order?
I also tried to reimplement focusNextPrevChild(bool next) and focusInEvent(QFocusEvent* e) similar to what can be found at this site. 1
I managed to mess up tab order a lot more like this... is this approach a step in the right direction?
I'm sorry if this is something simple that I managed to miss, but I'm struggling for a while now and I can't find a solution.
Any help is very appreciated.
I had the same problem, and resolved it using the info Tim Meyer provided in the comments.
Tab order is not hierarchical - calling setTabOrder on a parent widget that doesn't accept focus will not cause the focus to be passed to the child. You will need to fetch the appropriate children from the widget and set the order on them
In my case, a dynamically constructed QTreeWidget contained editable widgets, and I needed to reset setTabOrder to account for widgets being created out-of-order.
The following code is (most) of the implementation
// Perform a depth-first gather of the child widgets of this item
void gatherTabWidgets( QObject* item, QWidgetList& tabWidgets )
{
if (item->isWidgetType())
{
QWidget* itemWidget = static_cast<QWidget*>(item);
if (itemWidget->focusPolicy() & Qt::TabFocus)
tabWidgets.push_back( itemWidget );
}
const QObjectList& children = item->children();
for (QObjectList::const_iterator itr = children.begin(); itr != children.end(); itr++)
{
gatherTabWidgets( const_cast<QObject*>(*itr), tabWidgets );
}
}
// Perform a depth-first gather of the child items widgets;
void gatherTabWidgets( QTreeWidgetItem* item, QWidgetList& tabWidgets )
{
QWidget* itemWidget = fetchWidgetForItem( item );
gatherTabWidgets( itemWidget, tabWidgets );
for (int i = 0; i < item->childCount(); i++)
{
gatherTabWidgets( item->child( i ), tabWidgets );
}
}
void VETreeWidget::sortTree()
{
// Ensure ordering is maintained.
sortItems( 0, Qt::AscendingOrder );
// Once the items have been re-ordered, re-create the tab ordering
QTreeWidgetItem* baseItem = topLevelItem( 0 );
QWidgetList tabWidgets;
gatherTabWidgets( baseItem, tabWidgets );
if (!tabWidgets.empty())
{
QWidget* lastWidget = tabWidgets.first();
// Connect this treeview to the first widget. This ensures
// if the treeview is tabbed-to, it will tab to the correct sub-widget
setTabOrder( this, lastWidget );
for (QWidgetList::iterator itr = tabWidgets.begin() + 1; itr != tabWidgets.end(); itr++)
{
setTabOrder( lastWidget, *itr );
lastWidget = *itr;
}
}
}
I have a QListView with the ViewMode set to IconMode. I would like to achieve the following DnD behavior:
If a list view item is dragged inside the view, only the items position in the view is changed. This is the same as setting DragDropMode equal to InternalMove.
If a list item is moved out of the view, it can be copied to another external view. In this case, DragDropMode is equal to DragOnly.
How do I mix the two modes in such a way that both behaviors are supported by the view?
You might be able to do this by overriding the dropEvent of your view like this:
void MyListView::dropEvent( QDropEvent* e )
{
if( e->source() != this )
{
// something comes from the outside
// what to do? return?
return;
}
else
{
// event comes from the view itself, let's do some stuff
// for example call the base class default event
QAbstractItemView::dropEvent(e);
}
}
I guess the correct flag would be QAbstractItemView::DragDrop to do this.
My custom Widget have a collection of child widgets that are constructed on AJAX response. So I need to maintain an array of child widgets and append the domNode of those widgets to its parent Widget's domNode.
I can use a dijit.WidgetSet make a collection of the child widgets but there is no dijit.WidgetSet.domNodes() method to get domNodes of all the widgets. also dijit.WidgetSet doesn't take care of placement of child nodes in its parent DomNode
Is there any other class for doing the same already ? e.g. would subclassing dijit.WidgetSet to take care of appending to parent domNode be a reinvetion of Wheel ?
If you extend your parent with dijit._Container you can get hold of your widgetset by calling parent.getChildren(). The order in which widgets are returns is afaik the order that they were added to parent (parent.addChild()) and not by DOM sibling-hood. However, once you use the parser, this ofc would be the same. Well, any widget would have a getChildren implemented but the difference for _Container class is, that the addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex) and removeChild: function(/*Widget|int*/ widget) functions are pretty nifty. If you put an insertIndex while you add children, sibling-hood is manageable.
For concern of domNodes, with dijit we go 'beyond' DOM and work in the widget layer instead - which is only a JS wrap. The chainable function '.domNodes()' does not exist anywhere in dojo, you instead need to call similar to following:
parent.getChildren().forEach(function(childWidget) {
var domNode = childWidget.domNode;
...
});
// this would get nowhere as the return child-set is merely a 'stupid' array
parent.getChildren().set("attribute", "value");
Here is a cute little wrapper for the above foreach, using dijit/map
// retreives an array of the actual domNodes of widgetset
dijit.registry.map(function(widget){return widget.domNode;}).forEach(
// uses hitch to allow passing parameters without wrapping in new function
dojo.hitch(
window, // on random scope
dojo.addClass, // calling function
"imTheChildDomNode"// arguments[0]
)
);
I am using the constructor QWidget(QWidget *parent). This parent widget contains a lot of child widgets. I need to clear all the child widgets from the parent at runtime. How can I do this?
Previous answer is wrong!! You cannot use findChildren to delete a widget's children, because Qt4's findChildren recursively lists children. Therefore, you will delete children of children, which then may be deleted twice, potentially crashing your app.
More generally, in Qt, taking a list of QObject pointers and deleting them one by one is dangerous, as destroying an object may chain-destroy other objects, due to the parent ownership mechanism, or by connecting a destroyed() signal to a deleteLater() slot. Therefore, destroying the first objects in the list may invalidate the next ones.
You need to list children widgets either by:
Passing the Qt::FindDirectChildrenOnly flag to findChild if you are using Qt5 (which did not exist when the question was asked...)
Using QLayout functions for listing items,
Using QObject::children, and for each test if it is a widget using isWidgetType() or a cast
Using findChild() in a loop and delete the result until it returns a null pointer
To take care of the recursivity problem pointed out by #galinette you can just remove the widgets in a while loop
while ( QWidget* w = findChild<QWidget*>() )
delete w;
Summarizing and supplementing:
For Qt5 in one line:
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
For Qt5 for a lot of children, using setUpdatesEnabled():
parentWidget->setUpdatesEnabled(false);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
parentWidget->setUpdatesEnabled(true);
Note that this is not exception safe! While Qt does not at this time appear to throw exceptions here, the signal destroyed() could be connected to code that does throw, or an overridden Object::childEvent(QChildEvent*) could throw.
Better would be to use a helper class:
class UpdatesEnabledHelper
{
QWidget* m_parentWidget;
public:
UpdatesEnabledHelper(QWidget* parentWidget) : m_parentWidget(parentWidget) { parentWidget->setUpdatesEnabled(false); }
~UpdatesEnabledHelper() { m_parentWidget->setUpdatesEnabled(true); }
};
...
UpdatesEnabledHelper helper(parentWidget);
qDeleteAll(parentWidget->findChildren<QWidget*>("", Qt::FindDirectChildrenOnly));
For Qt4:
QList<QWidget*> childWidgets = parentWidget->findChildren<QWidget*>();
foreach(QWidget* widget, childWidgets)
if (widget->parentWidget() == parentWidget)
delete widget;
Removing from the QLayout works in both Qt4 and Qt5:
QLayoutItem* child;
while (NULL != (child = layout->takeAt(0))) // or nullptr instead of NULL
delete child;
QObjects (and therefore QWidgets) remove themselves (automagically) from their parent in their (QObject) destructor.
From Qt docs
The following code fragment shows a safe way to remove all items from a layout:
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
...
delete child;
}
You can use the following in your parent widget class:
QList<QWidget *> widgets = findChildren<QWidget *>();
foreach(QWidget * widget, widgets)
{
delete widget;
}
I have an item renderer that checks an external source for display information. If that information changes, I want to force all item renderer instances to check it.
What's the best way for force all the item renderers in a list or grid to either commitProperties or execute some other method?
I've read that resetting the
grid.itemRenderer property will make
them all initialize.
I've also received the suggestion to
iterate recursively through all the
grid's children and call invalidateProperties
on all the UIComponents I find.
Any thoughts? Alternatives?
Remember that in Flex Lists you're dealing with virtualization and itemRenderer recycling, so generally only the currently visible itemRenderers actually exist, and are therefore the ones that actually need updating.
The following works for Spark list-based controls:
for ( var i:int=0; i< sparkList.dataGroup.numElements; i++ )
{
var element:UIComponent = sparkList.dataGroup.getElementAt( i ) as UIComponent;
if ( element )
element.invalidateProperties();
else
trace("element " + i.toString() + " wasn't there");
}
If you've got 100 items, this will update the 10 visible ones and ignore the virtual rest.
If you're working with mx DataGrid, you might want to try a variant of this- but it doesn't use DataGroup / Spark virtualization so I don't have an answer for you off the top of my head.
P.S. I'm putting the finishing touches on a completely Spark-based DataGrid, I'll post the link when I'm done.
Datagroup has getItemIndicesInView() which will give you the indicies of all item renderers that are in view. Call getElementAt with those indicies.
I also usually extend ItemRenderer and add the following which will cause the item renderer's state to refresh.
public function invalidateSkinState():void
{
super.invalidateRendererState();
}
public function updateAllRenderer():void
{
if (!list.dataGroup)
return;
if (!list.dataGroup.dataProvider)
return;
for ( var index:int=0; index< list.dataGroup.numElements; index++ )
{
var item:Object = list.dataGroup.dataProvider.getItemAt(index);
var renderer:IVisualElement = list.dataGroup.getElementAt( index ) as IVisualElement;
if ( renderer )
list.updateRenderer( renderer, index, item );
}
}
works fine for me