Qt滚动区域原理(QAbstractScrollArea)

若对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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
运行结果及说明见图10-17、10-18、10-19
————————————————
版权声明:本文为CSDN博主「hyongilfmmm」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hyongilfmmm/article/details/83014405

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值