• Qt的children和findChildren函数详解


            我们在编写Qt应用是,经常会遇到需要获取某个窗口或控件下面的所有子控件,但是有时发现获取到的子控件总是不对,今天抽空对该函数进行分析一下:

            这是创建的一个QApplication应用,UI文件未做任何修改。

     接下来往centralwidget中放一些控件,看看是否能正确获取到子控件:

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame(ui->centralwidget);
    9. QFrame *frame_2 = new QFrame(ui->centralwidget);
    10. centerLayout->setObjectName("centerLayout");
    11. frame_1->setObjectName("frame_1");
    12. frame_2->setObjectName("frame_2");
    13. frame_1->setStyleSheet("background-color:red;");
    14. frame_2->setStyleSheet("background-color:yellow;");
    15. centerLayout->addWidget(frame_1);
    16. centerLayout->addWidget(frame_2);
    17. for (QObject *o : ui->centralwidget->children()) {
    18. qDebug()<<"ui->centralwidget->children:"<objectName();
    19. }
    20. }

            这是正常的写法,我们在创建时都为frame_1和frame_2都设置了父对象,然后再分别添加到layout中去,此时的结果:

            可以看到children能正确访问到,并且按照设置父对象的顺序。注意:该函数会返回控件的布局,因为布局的父对象也是该控件,一般情况下,我们是不需要获取到布局的。而且这里的顺序并不是添加到layout中的顺序,接下来我们加以验证:

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame();
    9. QFrame *frame_2 = new QFrame();
    10. centerLayout->setObjectName("centerLayout");
    11. frame_2->setParent(ui->centralwidget);
    12. frame_1->setParent(ui->centralwidget);
    13. frame_1->setObjectName("frame_1");
    14. frame_2->setObjectName("frame_2");
    15. frame_1->setStyleSheet("background-color:red;");
    16. frame_2->setStyleSheet("background-color:yellow;");
    17. centerLayout->addWidget(frame_1);
    18. centerLayout->addWidget(frame_2);
    19. for (QObject *o : ui->centralwidget->children()) {
    20. qDebug()<<"ui->centralwidget->children:"<objectName();
    21. }
    22. }

    结果如下:

            这样写的话从children中获取的顺序就和实际显示顺序不一致,因为我们是先给fram_2设置的父对象。

            接下来我们再变化一下,将frame_1的父对象设置为frame_2,这样centralwidget还能通过children获取到吗?

             结果如下:

            可以看到结果与之前的方案并没有差别,这是因为,尽管我们设置了frame_1的parent为frame_2,但是当我们把frame_1添加到centralwidget的布局中时,会重新设置frame_1的父对象为centralwidget。相关逻辑Qt源码中可以查看:

            进一步验证,我们在最后再重新设置frame_1的父窗口为frame_2,看看会发生什么:

             结果:

            可以看到此时frame_1是显示在frame_2中,因为它的父窗口是frame_2,并且此时centerwidget通过children访问不到frame_1。

            那如果我们想要按照子控件在布局中的位置顺序获取子控件该怎么办呢?此时可以通过layout的属性去获取:

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame();
    9. QFrame *frame_2 = new QFrame();
    10. centerLayout->setObjectName("centerLayout");
    11. frame_2->setParent(ui->centralwidget);
    12. frame_1->setParent(ui->centralwidget);
    13. frame_1->setObjectName("frame_1");
    14. frame_2->setObjectName("frame_2");
    15. frame_1->setStyleSheet("background-color:red;");
    16. frame_2->setStyleSheet("background-color:yellow;");
    17. centerLayout->addWidget(frame_1);
    18. centerLayout->addWidget(frame_2);
    19. for (QObject *o : ui->centralwidget->children()) {
    20. qDebug()<<"ui->centralwidget->children:"<objectName();
    21. }
    22. for (int i = 0; i < centerLayout->count(); ++i) {
    23. QLayoutItem *item = centerLayout->itemAt(i);
    24. QWidget *w = item->widget();
    25. qDebug()<<"centerLayout->item:"<objectName();
    26. }
    27. }

            结果如下:

     以上两种方式是否能获取到子控件中的子控件呢?我们加以验证:

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame();
    9. QFrame *frame_2 = new QFrame();
    10. centerLayout->setObjectName("centerLayout");
    11. frame_2->setParent(ui->centralwidget);
    12. frame_1->setParent(ui->centralwidget);
    13. frame_1->setObjectName("frame_1");
    14. frame_2->setObjectName("frame_2");
    15. frame_1->setStyleSheet("background-color:red;");
    16. frame_2->setStyleSheet("background-color:yellow;");
    17. centerLayout->addWidget(frame_1);
    18. centerLayout->addWidget(frame_2);
    19. QFrame *frame_1_1 = new QFrame(frame_1);
    20. QFrame *frame_1_2 = new QFrame(frame_1);
    21. QVBoxLayout *frame_1_layout = new QVBoxLayout(frame_1);
    22. frame_1_1->setObjectName("frame_1_1");
    23. frame_1_2->setObjectName("frame_1_2");
    24. frame_1_layout->setObjectName("frame_1_layout");
    25. frame_1_1->setStyleSheet("background-color:blue;");
    26. frame_1_2->setStyleSheet("background-color:white;");
    27. frame_1_layout->addWidget(frame_1_1);
    28. frame_1_layout->addWidget(frame_1_2);
    29. for (QObject *o : ui->centralwidget->children()) {
    30. qDebug()<<"ui->centralwidget->children:"<objectName();
    31. }
    32. for (int i = 0; i < centerLayout->count(); ++i) {
    33. QLayoutItem *item = centerLayout->itemAt(i);
    34. QWidget *w = item->widget();
    35. qDebug()<<"centerLayout->item:"<objectName();
    36. }
    37. }

             结果如下:

             可以看出上面的方法都不能访问到子控件中的子控件。

            如果我们需要递归获取所有的子控件,可以使用findChildren:

    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame();
    9. QFrame *frame_2 = new QFrame();
    10. centerLayout->setObjectName("centerLayout");
    11. frame_2->setParent(ui->centralwidget);
    12. frame_1->setParent(ui->centralwidget);
    13. frame_1->setObjectName("frame_1");
    14. frame_2->setObjectName("frame_2");
    15. frame_1->setStyleSheet("background-color:red;");
    16. frame_2->setStyleSheet("background-color:yellow;");
    17. centerLayout->addWidget(frame_1);
    18. centerLayout->addWidget(frame_2);
    19. QFrame *frame_1_1 = new QFrame(frame_1);
    20. QFrame *frame_1_2 = new QFrame(frame_1);
    21. QVBoxLayout *frame_1_layout = new QVBoxLayout(frame_1);
    22. frame_1_1->setObjectName("frame_1_1");
    23. frame_1_2->setObjectName("frame_1_2");
    24. frame_1_layout->setObjectName("frame_1_layout");
    25. frame_1_1->setStyleSheet("background-color:blue;");
    26. frame_1_2->setStyleSheet("background-color:white;");
    27. frame_1_layout->addWidget(frame_1_1);
    28. frame_1_layout->addWidget(frame_1_2);
    29. for (QObject *o : ui->centralwidget->children()) {
    30. qDebug()<<"ui->centralwidget->children:"<objectName();
    31. }
    32. for (int i = 0; i < centerLayout->count(); ++i) {
    33. QLayoutItem *item = centerLayout->itemAt(i);
    34. QWidget *w = item->widget();
    35. qDebug()<<"centerLayout->item:"<objectName();
    36. }
    37. for (QObject *o : ui->centralwidget->findChildren()) {
    38. qDebug()<<"ui->centralwidget->findChildren:"<objectName();
    39. }
    40. }

            结果如下:

            注意findChildren获取的顺序是设置父窗口的顺序,并不是实际布局中的显示顺序。

            如果必须要按照顺序获取,那只能我们自己实现一个递归算法

    1. void showAllChildren(QWidget *widget, bool firstIn = false)
    2. {
    3. if (!firstIn) {
    4. qDebug()<<"*********children:"<objectName();
    5. }
    6. if (widget->layout()) {
    7. for (int i = 0; i < widget->layout()->count(); ++i) {
    8. QLayoutItem *item = widget->layout()->itemAt(i);
    9. QWidget *w = item->widget();
    10. showAllChildren(w, false);
    11. }
    12. }
    13. }
    1. MainWindow::MainWindow(QWidget *parent)
    2. : QMainWindow(parent)
    3. , ui(new Ui::MainWindow)
    4. {
    5. ui->setupUi(this);
    6. ui->centralwidget->setObjectName("centralwidget");
    7. QVBoxLayout *centerLayout = new QVBoxLayout(ui->centralwidget);
    8. QFrame *frame_1 = new QFrame();
    9. QFrame *frame_2 = new QFrame();
    10. centerLayout->setObjectName("centerLayout");
    11. frame_2->setParent(ui->centralwidget);
    12. frame_1->setParent(ui->centralwidget);
    13. frame_1->setObjectName("frame_1");
    14. frame_2->setObjectName("frame_2");
    15. frame_1->setStyleSheet("background-color:red;");
    16. frame_2->setStyleSheet("background-color:yellow;");
    17. centerLayout->addWidget(frame_1);
    18. centerLayout->addWidget(frame_2);
    19. QFrame *frame_1_1 = new QFrame(frame_1);
    20. QFrame *frame_1_2 = new QFrame(frame_1);
    21. QPushButton * button_1_3 = new QPushButton(frame_1);
    22. QVBoxLayout *frame_1_layout = new QVBoxLayout(frame_1);
    23. frame_1_1->setObjectName("frame_1_1");
    24. frame_1_2->setObjectName("frame_1_2");
    25. button_1_3->setObjectName("button_1_3");
    26. frame_1_layout->setObjectName("frame_1_layout");
    27. frame_1_1->setStyleSheet("background-color:blue;");
    28. frame_1_2->setStyleSheet("background-color:white;");
    29. frame_1_layout->addWidget(frame_1_1);
    30. frame_1_layout->addWidget(frame_1_2);
    31. frame_1_layout->addWidget(button_1_3);
    32. for (QObject *o : ui->centralwidget->children()) {
    33. qDebug()<<"ui->centralwidget->children:"<objectName();
    34. }
    35. for (int i = 0; i < centerLayout->count(); ++i) {
    36. QLayoutItem *item = centerLayout->itemAt(i);
    37. QWidget *w = item->widget();
    38. qDebug()<<"centerLayout->item:"<objectName();
    39. }
    40. for (QObject *o : ui->centralwidget->findChildren()) {
    41. qDebug()<<"ui->centralwidget->findChildren:"<objectName();
    42. }
    43. showAllChildren(ui->centralwidget, true);
    44. }

            结果如下:

            如果需要过滤掉带layout的子控件(本身作为容器去加载其它控件的控件),则递归函数改为:

    1. void showAllChildren(QWidget *widget)
    2. {
    3. if (widget->layout()) {
    4. for (int i = 0; i < widget->layout()->count(); ++i) {
    5. QLayoutItem *item = widget->layout()->itemAt(i);
    6. QWidget *w = item->widget();
    7. showAllChildren(w);
    8. }
    9. } else {
    10. qDebug()<<"*********children:"<objectName();
    11. }
    12. }

            结果如下:

             frame_1作为其它控件的容器,这里过滤掉。该函数在实际项目中可根据需要添加条件进行筛选需要的控件。

  • 相关阅读:
    Mysql: 创建表 和 管理表
    Unity3D 基础——通过四元数控制对象旋转
    [全网唯一]通过修改源码使得从ZIP提取文件并在提取时进行重命名保存(博客园同步发布)
    光栅阶次分析器
    【TypeScript学习】—编译选项(三)
    Docker容器的数据卷
    跨境电商如何减少客户流失率:成功的5种保留策略
    基于C语言的操作系统(银行家算法、处理机管理、可变式分区管理、分页存储管理、进程同步模拟、生产消费者问题、哲学家就餐)
    取值函数(getter)和存值函数(setter)
    显示命令行控制台(cmd.exe)的几种方式——Qt、C++
  • 原文地址:https://blog.csdn.net/IT8343/article/details/127778948