Check whether a widget is in the layout Qt - qt

How to check whether a widget is in a layout? I have a layout that may contain the widget or not.
Widget name: infoClient
Layer name: verticalLayout_3
Need to check if the widget exists in the layout, then do not add a new one.
And if does not exist, add a new one.
How to do it?
void MainWindow::slotPush1()
{
if <there is no infoClient> ui->verticalLayout_3->addWidget(new infoClient(this));
}

Few points:
The parent of a widget in a layout is the widget containing the top-level layout
Layouts can be nested.
A layout contains items (QLayoutItem), which are either layouts (layout() is not null) or widgets (widget() is not null). In the end you have a tree of layout items.
So You need to do a search from the parent widget (dfs, bfs).
bool checkWidgetInsideLayout(const QWidget* _someWidget){
return _someWidget != NULL &&
_someWidget->parent() != NULL &&
_someWidget->parent()->layout() != NULL &&
foundItem(_someWidget->parent()->layout(), _someWidget );
}
//clumsy dfs
bool foundItem(const QLayout* layout, const QWidget* _someWidget ){
for(int i = 0; i < layout->count(); i++){
QLayoutItem* item = layout->itemAt(i);
if(item->widget() == _someWidget )
return true;
if(item->layout() && foundItem(item->layout(), _someWidget) )
return true;
}
return false;
}

Use QObject::findChild to find a child by its name. For instance:
void MainWindow::slotPush1()
{
if (ui->verticalLayout_3->findChild<QWidget*>("infoClient")) // your code to add it here
}
Note: findChild is a template function. If you're not familiar with template functions, just know that you pass the type of object you want to find (in your example, it looks like you could use ui->verticalLayout_3->findChild<infoClient*>("infoClient")). If you want to find a QWidget, or anything that inherits from QWidget, you can just use findChild<QWidget*>() and you'll be safe.

There is no mechanism within Qt that will perform the check that you are looking for. You will have to implement it yourself:
void MainWindow::slotPush1()
{
if (doesLayoutContainInfoClient(ui->verticalLayout_3))
{
ui->verticalLayout_3->addWidget(new infoClient(this));
}
}
bool MainWindow::doesLayoutContainInfoClient(QLayout* layout)
{
const QString infoClientName("infoClient");
for (int i=0; i<layout->count(); i++)
{
QWidget* layoutWidget = layout->itemAt(i)->widget();
if (layoutWidget)
{
if (infoClientName == layoutWidget->metaObject()->className())
{
return true;
}
}
}
return false;
}
Despite what I've suggested above, I don't really recommend it. It makes more sense to store whether or not you've added infoClient to your layout as an independent boolean within your program somewhere. Querying the contents of layouts in this manner is somewhat unusual, and is messier than just using a bool.

I agree with Tom Panning's solution to find your child with the QObject::findChild() method.
But adding a Widget to QLayout will reparent it to the layout's parent. So you'll have to find it by calling it with the MainWindow object like that:
void MainWindow::slotPush1()
{
if (this->findChild<QWidget*>("infoClient")) {
// ...
}
}
If your infoClient widget was added in the QtDesigner, you won't have problems with this solution. The designer sets per default the object name.
If the infoClient was added to the layout in your code, you have to set the object name explicitly, otherwise you won't be able to find it because its name is empty:
(Assuming, m_client is a member variable of MainWindow)
void MainWindow::createWidgets()
{
if (infoClientShouldBeAdded) {
m_client = new infoClient(this);
m_client->setObjectName("infoClient");
ui->verticalLayout_3->addWidget(m_infoClient);
}
}

Another possibility would be to keep track of the pointers of your created widgets (e.g., by storing them in a std::vector or Qt equivalent). This allows to use indexof(QWidget*):
Searches for widget widget in this layout (not including child layouts).
Returns the index of widget, or -1 if widget is not found.

Related

How to simple overwrite showContextMenu on all columns of a grid?

If I overwrite showContextMenu on a grid it only add additional menu to the pop up menu when I click on a grid but do not click on any column. One way is to overwrite showContextMenu on all columns in grid but this solution does not looks right. Is there some better way how to insertItem to showContextMenu on all columns of a grid?
I am using AX 2012.
Standard code works only if grid is clicked but not a single column of a grid.
int ret,ii;
int myMenu = 2;
PopupMenu popupMenu = PopupMenu::create(_menuHandle);
FormListItem item;
;
deleteAttachment = popupMenu.insertItem('My menu');
ret = super(_menuHandle);
if(ret == myMenu)
{
//My code
}
return ret;
You need to use method registerOverrideMethod.
You can test how it works as follows. E.g. you have a form with a grid (property Name = 'Grid', AutoDeclaration = 'Yes'), and there are a few StringEdit controls in that grid.
1) Create following method in your form:
public void formControlContext(FormStringControl _formStringControl)
{
_formStringControl.context();
info(strFmt(#"Overridden context of control '%1'", _formStringControl.name()));
}
2) Override method init in your form:
public void init()
{
FormStringControl fsc;
int controlNum;
super();
for (controlNum = 1; controlNum <= Grid.controlCount(); controlNum++)
{
fsc = Grid.controlNum(controlNum);
fsc.registerOverrideMethod(methodStr(FormStringControl, context), identifierStr(formControlContext));
}
}
You only need to modify method 'formControlContext' according to your needs.
P.S. I just noticed you need to override method showContextMenu anot not context. The idea is the same - use registerOverrideMethod. You can override any method this way.
I've never played with overwriting the ShowContextMenu, but I did just make a blog post about how to recursively loop over every form control that sounds like it might help you.
http://alexondax.blogspot.com/2014/05/how-to-use-recursion-to-loop-over-form.html
I'd imagine you could create some sort of handler and key/value thing if you're creative.

How can add action with QWidget.addAction()?

In Qt, I want to add some actions in a widget using QWidget.addAction().
I can easily do it with QToolBar.addAction(), but when I use QWidget.addAction(), it doesn't work.
How can I use QWidget.addAction()?
Here is my function:
void Reb::addActionToBar(QString *tabName, QAction *action)
{
//if tab exist, just add the action, else:
tab_widget->addTab(new QWidget(), *tabName);
for(int i = 0 ; i <= tab_widget->count() ; i++) {
if(tab_widget->tabText(i) == tabName) {
action.setParent(tab_widget->widget(i));
tab_widget->widget(i)->addAction(action);
}
}
}
And as you know tab_widget is a QTabWidget...
I have no error but i can't see my action in tab.
QWidget::addAction() does not do add the action to the UI - the only place where the widget's actions are shown is in the widget's context menu, given the right context menu policy.
QTabWidget has no means to display actions in its UI. Actions are usually displayed in toolbars or menubars, so you would need to add the action there.
As a side note, there is no need to pass QStrings by pointer, simply pass the QString by const reference:
void Reb::addActionToBar(const QString &tabName, QAction *action)
Also, your code has an off-by-one error, use i < tab_widget->count() instead of i <= tab_widget->count() to fix that.

QT: changing the tab traversal order on runtime?

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;
}
}
}

How to delete an already existing layout on a widget?

You must first delete the existing layout manager (returned by
layout()) before you can call setLayout() with the new layout.
from http://doc.qt.io/qt-5.9/qwidget.html#setLayout
Which function is used for deleting the previous layout?
Chris Wilson's answer is correct, but I've found the layout does not delete sublayouts and qwidgets beneath it. It's best to do it manually if you have complicated layouts or you might have a memory leak.
QLayout * layout = new QWhateverLayout();
// ... create complicated layout ...
// completely delete layout and sublayouts
QLayoutItem * item;
QLayout * sublayout;
QWidget * widget;
while ((item = layout->takeAt(0))) {
if ((sublayout = item->layout()) != 0) {/* do the same for sublayout*/}
else if ((widget = item->widget()) != 0) {widget->hide(); delete widget;}
else {delete item;}
}
// then finally
delete layout;
You just use
delete layout;
like you would with any other pointer you created using new.
This code deletes the layout, all its children and everything inside the layout 'disappears'.
qDeleteAll(yourWidget->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly));
delete layout();
This deletes all direct widgets of the widget yourWidget. Using Qt::FindDirectChildrenOnly is essential as it prevents the deletion of widgets that are children of widgets that are also in the list and probably already deleted by the loop inside qDeleteAll.
Here is the description of qDeleteAll:
void qDeleteAll(ForwardIterator begin, ForwardIterator end)
Deletes all the items in the range [begin, end] using the C++ delete > operator. The item type must be a pointer type (for example, QWidget *).
Note that qDeleteAll needs to be called with a container from that widget (not the layout). And note that qDeleteAll does NOT delete yourWidget - just its children.
Now a new layout can be set.
I want to remove the current layout, replace it with a new layout but keep all widgets managed by the layout. I found that in this case, Chris Wilson's solution does not work well. The layout is not always changed.
This worked for me:
void RemoveLayout (QWidget* widget)
{
QLayout* layout = widget->layout ();
if (layout != 0)
{
QLayoutItem *item;
while ((item = layout->takeAt(0)) != 0)
layout->removeItem (item);
delete layout;
}
}
From Qt6's docs:
The following code fragment shows a safe way to remove all items from a layout:
QLayoutItem *child;
while ((child = layout->takeAt(0)) != nullptr) {
...
delete child->widget(); // delete the widget
delete child; // delete the layout item
}
This assumes takeAt() has been correctly implemented in the QLayout subclass. Follow link for details.

Is there a way to get a list of all skin classes in the current application?

Is there a way to get a list of all the skin classes that are in the current application? I'm using Flex 4.5.1.
Here is the loop that I'm using now to get all the skins,
for each (var item:Object in styleManager.typeHierarchyCache) {
for (label in item) {
if (label=="spark.components.supportClasses.Skin" ||
label=="spark.skins.mobile.supportClasses.MobileSkin") {
for (label in item) {
name = label.substr(label.lastIndexOf(".")+1);
vo = new SkinObject();
vo.name = name;
vo.qualifiedName = label;
dictionary[label] = vo;
}
break;
}
}
}
for each (item in dictionary) {
array.push(item);
}
The reason why is because I want to list all the skins in the application and then be able to apply them in real time so I can see what they look like. * I have this working but I was hoping for a better way.
You definitely could iterate through all objects on the screen and see if they're of type SparkSkin. Something like this:
private function findSkins():void
{
recurseComponent(FlexGlobals.topLevelApplication);
}
private function recurseComponent(parent:UIComponent):void
{
var child:UIComponent;
for(var i:uint = 0, len:uint = parent.numElements; i<len; i++)
{
child = parent.getElementAt(i) as UIComponent;
if(child && child is SparkSkin)
{
trace("Skin Found!"); // trace whatever you need here
}
recurseComponent(child);
}
}
But be warned that this solution is very expensive since it needs to iterate through all objects on the screen, which can go into several thousands. However, I really don't see what's the purpose of this and would definitely not recommend it other than for debugging/testing purposes.
Edit: Also, this will only work for skins on the display list. Skins mentioned in CSS will not be recognized and I'm fairly sure there's no way of figuring that out short of going through all the css and see if there's a skinClass property. But then it won't catch any default skins or skins set in actionscript or inline mxml.

Resources