转自 https://blog.csdn.net/qq_31073871/article/details/83117430

QScrollArea属于控件容器类,可以直接在ui中拖出来。

对于QScrollArea,最难搞懂的就是:如何控制它,才能让它在我们想要出现滚动条的时候出现滚动条。

我们拖入一个QScrollArea,再向他里面拖入4个button,观察信息如下:

可以发现,4个button并不是直接位于QScrollArea中的,而是位于它的成员scorllAreaWidgetContents中的,这个成员的类型也是控件类型QWidget,也就是说,QScrollArea这个容器本身就套了两层,我们放入的按钮等控件,都处在scrllAreaWidgetContents层,下文中我把QScrollArea.widget统一称之为“内部容器”,内部容器是QScrollArea这个控件的子控件。

补充一点,内层的这个scrllAreaWidgetContents,我们可以更换掉它,方法就是QScrollArea::setWidget(QWidget *),如果scrollArea控件是用鼠标拖出来的,那么QT会自动为我们new一个内部容器,如果scrollArea控件是用代码创建的,那么不忘忘了为这个滚动区创建内部容器。

帮助文档还指出,setWidget应当在“把想加的控件都加到内部容器”之后再调用,否则会无法显示(然而,我测试发现,帮助说的不对,即使我们在setWidget之前没有给内部容器设置布局也不要紧,只要在setWidget之后,内部容器调用一下adjustSize,就可以把内部容器的控件显示出来,当然,为了保险起见,或者后续程序兼容性起见,还是按照帮助文档来做吧)。

总结一下滚动区的标准编程步骤:

(1)new QscrollArea
(2)new 内部容器
(3)new 布局,例如网格布局QGridLayout(前3步不分先后顺序)
(4)向布局中添加你想要的控件(这一步必须位于步骤3之后,这不是废话吗)
(5)关联"内部容器"和"布局"(如果在创建布局时,就把布局构造在了内部容器中,那么这一步就省了)
(6)关联"内部容器"和"滚动区",也即调用QScrollArea::setWidget(QWidget *),这一步必须位于步骤4、5之后。

 

 

一句话总结何时出现滚动条:

只要QScrollArea的尺寸 < 内部容器的尺寸,就会自动出现滚动条。下面举个具体的例子:

程序初始时我们通过setGeometry设置内部容器的尺寸为100X100,QScrollArea放置在主界面的布局中,也即QScrollArea的尺寸是随着用户鼠标拖动窗口变化而变化的,那么当用户拖大窗口时,QScrollArea也变得很大,而内部容器还是100X100,这时,滚动条就消失了;如果用户用鼠标缩小了窗口,QScrollArea也随之变的很小了,而内部容器还是100X100,QScrollArea就会自动出现滚动条。

记住一句话,除非你在程序运行时修改了内部容器的大小,否则,内部容器将一直保持它初始的大小。那么

 

 

下面讲如何控制滚动条何时出现?

有了上面的分析,我们已经知道了QScrollArea有两层,QScrollArea本身是个QWidget,它内部又套了一个小QWidget。在默认情况下,内部的这个小QWidget,也即scorllAreaWidgetContents,它的大小总是等于外部QWidget(也即QScrollArea)的大小。除非我们给scorllAreaWidgetContents设置了宽高的最小值,这时,当QScrollArea的宽或高,一旦小于scorllAreaWidgetContents的宽或高,就会出现滚动条。

这个scorllAreaWidgetContents是从哪来的?查看QT自动生成的ui代码(也即:ui->setupUi(this)函数)可见,它是在QScrollArea构造完成之后,再被new出来的,然后调用了QScrollArea::setWidget()。这一点对于我们编程非常重要,否则一不小心就中了坑,假设我们自己继承QScrollArea写了一个新的子类,在子类的构造函数中我们通过QScrollArea::setWidget()设置好了自己的小QWidget,然后在ui中把QScrollArea提升为自己写的这个子类,这时就扯淡了,我们在子类构造函数中为小QWidget添加的控件都没有显示出来,因为QT自作主张,在我们自己的构造函数执行完之后,重新new了小QWidget,并通过setWidget函数设置给了这个子类!这真是太坑了。如果你想通过ui提升的方式来使用自己写的子类,那么请你在ui拖入一个Frame容器或者widget容器等等进行提升,反正不要用拖入的QScrollArea容器进行提升。如果你非得想用QScrollArea容器进行提升,那么你想在QScrollArea的内部容器中添加控件时,请不要把添加控件的代码写在QScrollArea子类的构造函数中。

下面继续研究滚动条问题。

直接看例子,我给scorllAreaWidgetContents成员设置宽高最小值为500*1000,这么高的scorllAreaWidgetContents,显然QScrollArea在高度上是无法容纳下的。实际上,看效果发现,还没有运行程序,就已经有滚动条了

我们运行一下程序,然后把窗口缩小,看看是不是当窗口<scorllAreaWidgetContents最小值500*1000时,会自动出现水平滚动条。看下图发现,并没有出现我们期望的效果。

原因就是,水平滚动条,只有当QScrollArea<内部的QWidget时,才会出现,显然上图中,QScrollArea虽然没显示全,但是QScrollArea的宽度仍然保持原值,只是被遮住了而已。要想使QScrollArea的宽度变小,要么通过程序直接修改,要么通过设置布局,使QScrollArea的宽度随窗体的宽度减小而减小。我们这里就简单一点,直接给窗体设置网格布局,使得QScrollArea的大小受窗体大小驱动。运行起来,再看下效果:

再补充几点:

内部的小QWidget与QScrollArea的关系,就像是给QScrollArea设置了网格布局,然后把小QWidget放进了这个布局中,如果给小QWidget设置的最大宽高<QScrollArea的实时大小,那么QScrollArea会显示出空白,而空白部分是无法放置/显示我们自己拖入的控件的,如下图:

如果我们不给小QWidget设置宽高最大值,那么它的宽width将满足:

它的高也有与之相同的规律。

知道了原理,不论是用UI设计器来做界面,还是直接用代码来做界面,就都不是问题了。

 

技巧:

通过上述操作,我们知道了,我们可以通过设置内部小QWidget的宽、高最小值,来让外部QScrollArea适时的出现滚动条,那么到底把小QWidget的宽、高最小值设置为多少合适呢?

答案是显然的:把小QWidget的宽、高最小值设置为刚好能容纳内部的按钮等控件,这样看起来最舒服。难道我要先计算或者观察一下按钮等控件占用的面积之后,才能去设置小QWidget的宽、高最小值吗?

这样做太费劲了,我们肯定不会去这样做,除非是用ui设计师拖控件时,所见即所得,才无需计算小QWidget的宽、高最小值。用代码写界面时,最好的做法是:

1、向小QWidget中添加按钮等控件时,随着添加的按钮增多,小QWidget自动变大,显然用QGridLayout来做就能实现这个自动增大这个需求。自动增大也只是出现创建内部容器阶段,一旦内部容器和布局、布局内的控件都创建和添加完毕,后续即使再向布局中添加控件,内部容器也不会自动增大了,这时只有靠setGeometry手动修改内部容器的大小了。

2、添加完控件后,手动调用一下adjustSize函数,该函数会根据所有子控件的大小之和,来调整父控件的大小。

步骤如下:先在ui中拖入一个QScrollArea控件,名字为scrollArea,然后添加代码:
 

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    QGridLayout *pLayout = new QGridLayout();//网格布局
    for(int i = 0; i < 100; i++)
    {
        QPushButton *pBtn = new QPushButton();
        pBtn->setText(QString("按钮%1").arg(i));
        pBtn->setMinimumSize(QSize(60,30));   //width height
        pLayout->addWidget(pBtn);//把按钮添加到布局控件中
    }
    ui->scrollArea->widget()->setLayout(pLayout);//把布局放置到QScrollArea的内部QWidget中
}

 根据原著者的描述,转载人补充如下代码,展示在不使用ui拖拽的情况下,实现滚动条:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QScrollArea>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QScrollArea * scrollArea = new QScrollArea(this);
    QWidget * pWgt = new QWidget;

    QHBoxLayout *pLayout = new QHBoxLayout();//网格布局
        for(int i = 0; i < 100; i++)
        {
            QPushButton *pBtn = new QPushButton();
            pBtn->setText(QString("按钮%1").arg(i));
            pBtn->setMinimumSize(QSize(60,30));   //width height
            pLayout->addWidget(pBtn);//把按钮添加到布局控件中
        }
    pWgt->setLayout(pLayout);

    //这一句setWidget必须放在pWgt里面的内容都准备完毕之后,否则显示有问题
    scrollArea->setWidget(pWgt);
    setCentralWidget(scrollArea);
}

MainWindow::~MainWindow()
{
    delete ui;
}

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐