检查一个小部件是否在Qt布局中

8

如何检查一个小部件是否在布局中?我有一个可能包含该小部件的布局。

  • 小部件名称:infoClient
  • 图层名称:verticalLayout_3

需要检查小部件是否存在于布局中,如果存在则不添加新的小部件。如果不存在,则添加一个新的小部件。如何实现呢?

void MainWindow::slotPush1()
{
    if <there is no infoClient> ui->verticalLayout_3->addWidget(new infoClient(this));
}
5个回答

3

几点说明:

  • 布局中小部件的父级是包含顶层布局的小部件
  • 布局可以嵌套。
  • 布局包含项目(QLayoutItem),这些项目可以是布局(layout() 不为空)或者小部件(widget() 不为空)。最终你会得到一个布局项树。

因此,您需要从父小部件开始搜索(深度优先搜索,广度优先搜索)。

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

2

Qt框架内部没有提供您所需的检查机制。您需要自行实现:

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

尽管我上面提到了这种方法,但我并不真正推荐它。更明智的做法是在程序中将是否已将infoClient添加到布局中作为独立的布尔值进行存储。以这种方式查询布局的内容有点不寻常,并且比使用bool更混乱。


这个问题比较微妙。你的解决方案适用于它的本地化情况...而不是“检查一个小部件是否在Qt布局中”的情况... - UmNyobe
@UmNyobe 没错,但问题似乎只是在询问本地化的情况。在许多其他答案中似乎也被忽略的是,“infoClient”实际上不是小部件名称;它是小部件类型(尽管问题中声明了相反)。 - RA.
是的,您可以使用“infoClient”作为类名,因为他想要在布局中实现“单例行为”。我理解。 - UmNyobe

2

使用QObject::findChild通过名称查找子项。例如:

void MainWindow::slotPush1()
{
    if (ui->verticalLayout_3->findChild<QWidget*>("infoClient")) // your code to add it here
}

注意:findChild是一个模板函数。如果您不熟悉模板函数,只需要知道您要查找的对象类型(在您的示例中,看起来您可以使用 ui->verticalLayout_3->findChild<infoClient*>("infoClient"))。如果您想查找QWidget或任何从QWidget继承的内容,只需使用 findChild<QWidget*>(),这样就可以安全地实现。


3
正如其他答案所指出的那样,小部件(widget)不是布局(layout)的子元素,而是父级小部件的子元素,无论它是否在布局中。 - hyde
谢谢你的想法!但是你的方法不起作用。子控件必须在父控件中查找。我正在这样做:if (!(ui->centralWidget->findChild<QWidget*>("infoClient"))) ui->verticalLayout_3->addWidget(new infoClient(this)); - SerJS
@SerJS “它运行正常!!”并不意味着它是正确的。一旦您嵌套布局或使用小部件进行更复杂的操作,就会遇到麻烦。 - UmNyobe
我意识到这并不好,但这是解决我的问题最简单的方法。如果出现问题,我会使用其他解决方案。 - SerJS

1
我同意Tom Panning的解决方案,使用QObject::findChild()方法来查找您的子项。 但是,将Widget添加到QLayout中会将其重新分配给布局的父级。因此,您需要通过使用MainWindow对象调用它来查找它:
void MainWindow::slotPush1()
{
    if (this->findChild<QWidget*>("infoClient")) {
        // ...
    }
}

如果您的infoClient小部件是在QtDesigner中添加的,那么您不会遇到此解决方案的问题。设计师默认设置对象名称。 如果在代码中将infoClient添加到布局中,则必须显式设置对象名称,否则您将无法找到它,因为其名称为空: (假设m_client是MainWindow的成员变量)
void MainWindow::createWidgets()
{
    if (infoClientShouldBeAdded) {
        m_client = new infoClient(this);
        m_client->setObjectName("infoClient");
        ui->verticalLayout_3->addWidget(m_infoClient);
    }
}

0
另一种可能性是跟踪所创建的小部件的指针(例如,通过将它们存储在std :: vector或Qt等中)。这允许使用 indexof(QWidget*)

搜索此布局中的小部件widget(不包括子布局)。 返回widget的索引,如果未找到widget,则返回-1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接