Qt滚动区域原理(QAbstractScrollArea)

Qt滚动区域原理(QAbstractScrollArea)

本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)

本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址:
https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q
《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg

若对C++语法不熟悉,建议参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。

若对Qt的事件不了解,可参阅文章《Qt元对象系统、信号和槽及事件》

QAbstractScrollArea类(抽象滚动区域)

QAbstractScrollArea继承自QFrame,注意:该类不是抽象的(没有纯虚函数),只是该类的功能不够完整,所以才称为抽象的。该类主要用于被继承以实现自定义的滚动区域。

10.3.3 自定义滚动区域

1、基本思想
自定义滚动区域,分为两个步骤,即设计外观和计算滚动。下面先讲解外观的设计,只要外观设计好了,计算方法都是相同的。
2、设计外观的原理
所谓滚动区域,其实就是一个QWidget部件,比如为pw,然后在pw中添加一个子部件,比如为ps,然后当pw的大小小于ps时,显示滚动条,具体原理见图10-14。
由以上可见,可使用任何部件来实现滚动区域,只不过子类化QAbstractScrollArea类,可以使用由Qt实现的比较方便的函数,比如可以使用setHorizontalScrollBar()方便的设置水平滚动条,若不然,我们需要把水平滚动条添加到滚动区域的底部,并把其添加到一个布局中,然后对滚动条进行其他一些设置(比如大小策略等),这样就比较麻烦了。
在这里插入图片描述
3、计算
计算主要需要计算两个方面,首先,当移动滚动条时,怎样绘制子部件(可简单的移动子部件即可),其次,当不需要滚动条时,恢复子部件为最初的位置。下面介绍一种简单的方法
(1)、移动滚动条的计算(以垂直滚动条为例)
设计滚动条的范围(即最大/最小值),当需要垂直滚动时,通常子部件的大小大于视口的大小,计算原理如图10-15所示
在这里插入图片描述
(2)、恢复子部件最初位置的计算
恢复子部件时,其视口的大小大于子部件的大小,计算原理如图10-16所示
在这里插入图片描述

以下为需要重新实现的函数
9)、virtual void setupViewport(QWidget *viewport); //虚拟的
 在setViewport()被调用之后,由QAbstractScrollArea调用。在使用新的视口之前,在QAbstractScrollArea的子类中重新实现此函数以初始化新视口。
10)、virtual bool viewportEvent(QEvent *event); //虚拟的,受保护的
 视口事件,该函数是滚动区域的主事件处理程序(viewport()部件)。 它处理指定的事件,并且可以由子类调用以提供合理的默认行为。
 若返回true表明事件已处理,不需要进一步处理,若返回false,则该事件应进一步传播。
 QAbstractScrollArea使所有视口事件处理程序在此函数中都可用,在有意义的情况下,QWidget的专用处理程序会被重新映射到视口事件,重新映射的专用处理程序是:paintEvent(),resizeEvent(),mousePressEvent(),mouseReleaseEvent(),mouseDoubleClickEvent(),mouseMoveEvent(),wheelEvent(),dragEnterEvent(),dragMoveEvent(),dragLeaveEvent(),dropEvent(),contextMenuEvent() 。
 子类化QAbstractScrollArea时,建议重新实现QWidget的专用处理程序,而不是重新实现viewportEvent()函数,因为viewportEvent()需要处理的事件太多,鼠标,拖放,绘制等事件都在该函数内需要处理,一不小心就会出现错误。
11)、virtual QSize viewportSizeHint() const; //虚拟的,受保护的,qt5.2
 返回视口的建议大小。 默认实现返回viewport()-> sizeHint()。 注意,大小只是视口的大小,没有任何滚动条
12)、virtual void scrollContentsBy(int dx, int dy); //虚拟的,受保护的
 当通过dx,dy移动滚动条时,调用此函数,因此应该相应地滚动视口的内容。
 参数dx和dy是为方便而存在的,这样类就知道应该滚动多少(做像素移位时有用),当然也可以直接滚动到滚动条指示的位置。
 默认实现仅在整个viewport()上调用update(),子类可以重新实现此处理程序以进行优化,或者像QScrollArea一样移动内容部件。
 不应在此函数中以编程的方式滚动滚动条。
13)、QMargins viewportMargins() const; //返回滚动区域的边距。 受保护的,qt5.5
void setViewportMargins(int left, int top, int right, int bottom); //受保护的
void setViewportMargins(const QMargins &margins); //受保护的
 设置滚动区域周围的边距。注意,该函数通常由QTreeView和QTableView调用,所以边距必须由QAbstractScrollArea子类实现。 另外,如果要在模型/视图中使用子类,则不应调用此函数。默认情况下,所有边距都为零。

示例10.2:自定义滚动区域(子类化QAbstractScrollArea)
//m.h文件的内容
#ifndef M_H
#define M_H
#include

class B:public QAbstractScrollArea{    Q_OBJECT
public:    QScrollBar *psh,*psv;    QWidget *pw1,*psub;
B(QWidget *p=0):psub(0),QAbstractScrollArea(p ){   
			setViewportMargins(22,22,22,22);   //设置边距之后可明显的观察到滚动区域的3层结构。
    		pw1=new QWidget;    psh=new QScrollBar;    psv=new QScrollBar; 	//初始化各部件
	//设置滚动区域的外观
    		setVerticalScrollBar(psv);    setHorizontalScrollBar(psh);		//设置滚动条
    		setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);   			//水平滚动条总是可见
    		setViewport(pw1);   										//把pw1设为视口部件。
//以下语句不是必须语句,主要是用于区分各部件而为其设置的背景色。
    //把视口的背景色设置为绿色。
    		QPalette p1;    p1.setColor(QPalette::Background,QColor(1,111,1));//绿色
    		pw1->setAutoFillBackground(1);  				//填充背景,否则部件可能会是透明的。
    		pw1->setPalette(p1);
    //设置滚动区域的颜色为蓝色。
    		QPalette p2;   p2.setColor(QPalette::Background,QColor(1,1,111));	//蓝色
    		setAutoFillBackground(1);    setPalette(p2);
//设置水平滚动条的颜色为红色(需使用样式表设置)
    		psh->setStyleSheet("background-color: rgb(255,1,1);");			//红色
//联接信号和槽,主要用于当拖动滚动条时移动子部件的位置。
    		QObject::connect(psh,&QScrollBar::actionTriggered,this,&B::f1);
    		QObject::connect(psv,&QScrollBar::actionTriggered,this,&B::f1);	
} //构造函数结束

    void add(QWidget *p){			//新增一个函数,用于添加用户需要移动的子部件。
        QWidget *pp=viewport();     	//获取视口部件
        p->setParent(pp);    		//把子部件的父部件重新设置为视口部件(即pw1)
        psub=p;  }   				//把子部件保存在psub中,这样psub便指向了需要拖动的子部件。
 	
void js(){  					//此函数用于计算和设置滚动条的滚动范围
    		//设置页面步长。此步不是必须,但为了使滚动条滑块的大小随视口的改变而改变,
//需要设置此步(页面步长可影响滚动条滑块的大小,原理见QScrollBar章节)。
     	psv->setPageStep(viewport()->height()); psh->setPageStep(viewport()->width());

      	//设置滚动条的最大最小值,使用像素单位的好处是以后可直接使用滚动条的当前值
//来调整子部件的位置,以下公式的计算原理见图10-15。
      	psv->setRange(0, psub->height() - viewport()->height());
      	psh->setRange(0, psub->width() - viewport()->width());	 }

bool viewportEvent(QEvent *e){
/*处理QResizeEvent事件,此步非常重要,因为该事件在很多情况下(比如初次运行时,改变部件大小时)都会被发送。建议使用专门的QWidget::resizeEvent()处理函数来处理QResizeEvent事件,而不是在viewportEvent()函数中处理。*/
if(psub!=0&&e->type()==QEvent::Resize){ 	//判断是否是QResizeEvent事件
    			int hv = horizontalScrollBar()->value();int vv = verticalScrollBar()->value();
      		//QRect r=psub->rect(); 	//rect()不能获取子部件左上角的坐标,需使用geometry()函数。

    			//以下代码处理恢复子部件位置时的情况。具体计算原理见图10-16。
      		if(psub->height()+psub->geometry().y()<viewport()->height()){
            			psub->move(psub->geometry().x(),-vv);	}  //移动子部件位置
      		if(psub->width()+psub->geometry().x()<viewport()->width()){
      				psub->move(-hv,psub->geometry().y());	}  
 			js();   					//设置滚动条的滚动范围,此步重要,否则不会显示滚动条。
}							//if结束

//本示例不需要处理QPaintEvent事件,因为QPaintEvent事件在某些时候不会被发送,
//比如初次显示界面时就不会发送QPaintEvent事件,但会发送QResizeEvent事件,
//因此本示例处理QResizeEvent事件,而不处理QPaintEvent事件
if(psub!=0&&e->type()==QEvent::Paint){  }  
      	//viewport()->update();  					//不能调用该函数,否则会无限循环。
return QAbstractScrollArea::viewportEvent(e);
}  	//viewportEvent结束
public slots:
void f1(){psub->move(-psh->value(),-psv->value());} 	//当拖动滚动条时,移动子部件的位置。
};
#endif // M_H

//m.cpp文件的内容
#include “m.h”

int main(int argc, char *argv[]){    QApplication app(argc,argv);
QWidget *pw=new QWidget;   		//容器,这将是滚动区域的子部件
 	QLabel *pb=new QLabel;    				pb->setPixmap(QPixmap("F:/2.jpg"));
    QPushButton *pb1=new QPushButton("AAA");   QPushButton *pb2=new QPushButton("BBB");
    pb1->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
//布局容器中的内容
    QVBoxLayout *pv=new QVBoxLayout;    QHBoxLayout *ph=new QHBoxLayout;
    ph->addWidget(pb1);  ph->addWidget(pb2);  pv->addWidget(pb);  pv->addLayout(ph);
    pw->setLayout(pv);
//设置容器pw的背景色为红色
    QPalette p1;    p1.setColor(QPalette::Background,QColor(255,1,1));	//红色
    pw->setAutoFillBackground(1);    pw->setPalette(p1);    pw->resize(333,222);
    B ps;  					//使用自定义的滚动区域
    ps.add(pw);  				//添加子部件
    ps.show();    return app.exec(); 
}

运行结果及说明见图10-17、10-18、10-19
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
作者:黄邦勇帅(原名:黄勇)

在这里插入图片描述

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值