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



作者:黄邦勇帅(原名:黄勇)

Qt滚动区域原理(QAbstractScrollArea)相关推荐

  1. qt通过代码创建滚动区域,添加滚动区域到窗口

    引言 继承于QWidget创建的项目,要想在QWidget中添加很多子控件,考虑到若是子控件的数目不确定,太多的时候就需要添加滚动条来实现滚动查看,于是可以自定义一个类,在该类中实现滚动区域和滚动窗口 ...

  2. Qt事例: QScrollArea滚动区域

    Qt事例: QScrollArea滚动区域 /******* ChatList.h ******/ #ifndef CHATLIST_H #define CHATLIST_H #include < ...

  3. qt设置滚动区域的滚动条的样式

    引言 当窗口的控件太多,不能一下完全显示,就可以采用滚动区域来添加控件,这样添加的控件,可以通过滑动滚动区域的滚动条来浏览所有的控件.下面就大致记录一下滚动区域的滚动条的样式怎么设置. 实现 直接上设 ...

  4. 【学习笔记】C++ GUI Qt4 第六章 6.4 滚动区域 和 6.5 停靠窗口和工具栏

    文章目录 6.4 滚动区域 6.5 停靠窗口和工具栏 6.4 滚动区域 QScrollArea类提供了一个可以滚动的视口和两个滚动条.如果想给一个窗口部件添加一个滚动条,则可以使用一一个QScroll ...

  5. RPG游戏滚动地图原理

    首先应该明确,RPG地图通常由很多小块拼凑而成,所以在此我们讨论的是由很多小块组成的地图的滚动实现原理. 主要涉及到的对象有地图(由地图数组及图块图片拼组成),我们先定义一些属性来描述它:图块图片(以 ...

  6. [css] 你知道全屏滚动的原理是什么吗?它用到了CSS的哪些属性?

    [css] 你知道全屏滚动的原理是什么吗?它用到了CSS的哪些属性? 全屏滚动和轮播图类似,都是通过改变元素位置或者显示与隐藏来实现,配合JS的一些交互距离判断,实现类似原生滚动捕获的效果.这里全屏的 ...

  7. 《微信小程序案例5》仿小米Lite小程序分类板块-两个纵向滚动区域独立互不影响

    一.小米Lite分类板块图 二.技术分解 1.布局是分为左右两个纵向滚动区域,使用   <scroll-view> 2.每个小菜单的如图片 标题等内容使用js里面的data来保存 3.如此 ...

  8. 原生微信小程序,scroll-view的使用,一屏两个滚动区域,scroll-y(纵向滚动)

    原生微信小程序,scroll-view的使用,一屏两个滚动区域,scroll-y(纵向滚动) 这个会简单很多 结构层 <view class="container">& ...

  9. 可控制的页面内滚动区域

    效果预览 下面我们就来详细讲解一下这种效果的制作方法: 首先,我们在样式表里加入".opacity {FILTER: alpha(opacity=100)",看下面! <st ...

最新文章

  1. 决策树算法详解(1)
  2. 下列数据类型中python不支持的是_ 下列选项中 ,Python 不支持的数据类型有 ( ) 。_学小易找答案...
  3. 如果你不喜欢让大人说你祝酒祝福
  4. poj3253 优先队列
  5. OAuth(开放授权):(第三方)通过(授权)令牌(Access Token)访问用户数据
  6. SAP Hybris Enterprise Commerce的一些有用链接 - 保证持续更新
  7. 阿里云、百度云被约谈 督促落实防范治理电信网络诈骗
  8. mysql表空间不足_MySQL Innodb表空间不足的处理方法 风好大
  9. 2013年微软编程之美大赛初赛第二题(博客园居然可以插入代码!!)
  10. Java再学习——关于ConcurrentHashMap
  11. h5侠客行服务器维护有更新什么,侠客行h5转生条件大全及转生激励说明
  12. 《算法》第四版官网库及数据文件
  13. 项目管理办公室(PMO) 的不同定位
  14. 种草功能在电商app源码中发挥的作用和价值
  15. cad尺寸标注快捷键_CAD快速在尺寸标注后加上公差的方法有哪些?【AutoCAD教程】...
  16. CTWAP和CTNET的区别
  17. 安装Java Visualvm监控堆内存和参数说明
  18. 【游戏开发阅读列表2】动画(Anima2D、粒子、物理等)
  19. Unity3D游戏美术全攻略:从入门到精通
  20. 如何对变量进行对位置1与对位清0

热门文章

  1. LoadRunner 12.02 进行录制脚本时提示无Internet访问
  2. 无内鬼来点干货,银行java开发面试题(含答案)
  3. GET POST 区别!别听他们扯犊子,越看越气人
  4. 冰火魔界服务器维修,冰火魔界手游在电脑上怎么玩 冰火魔界电脑版安装方法...
  5. VMware Workstation 无法连接到虚拟机。请确保您有权限运行该程序、访问改程序使用的所有目录以及访问所有临时文件目录。未能将管道连接到虚拟机: 所有的管道范例都在使用中。
  6. 【帧同步】关于状态同步的经验分享
  7. wolfram 在线_在Google Chrome中访问Wolfram Alpha搜索
  8. CKEditor 3.4 配置方法
  9. 串口转以太网模块:WIZ108SR(用户手册)
  10. 基于Halcon学习的二维码识别【七】2d_data_codes_rectify_symbol.hdev