Qt--自定义View
这是上一篇文章的续篇,关于自定义View。 多个View内部可以映射到同一个数据模型,也可以映射不同的数据结构;可以使用所有数据,也可以只使用部分数据。因为视图层与数据层的分离,操作相对比较灵活。
1. 实现一个自定义View
这里我们来实现一个自定义View,住要包含一下几个功能:
- 图案自定义:包含四个颜色不一的格子,大小小于整个窗口的1/4, 分别位于窗口的四个角落。
- 响应
鼠标
点击:鼠标点击格子区域,绘制高亮的内方格,点击非格子区域无反应。 - 响应
右箭头(→)
按键:点击选中格子后,按右箭头按键移动到下一个格子,并选中。
嗯,大概就是下面设计图的样子,并且我们把左上角、右上角、右下角和左下角的数据分别约定为QModelIndex(0,0)、QModelIndex(0,1)、QModelIndex(1,0)和QModelIndex(11)。
1.1 选择合适的视图类继承
与Model子类化一样,编写自定义视图的时候也有两种选择:继承QAbstractItemView、继承Qt提供的标准View。相对来说,继承抽象基类QAbstractItemView需要花较多的功夫,继承标准View类则比较快。当自己要使用的视图类与某个标准View相近的时候,继承这个View并且重写自己需要的功能函数是最方便的。
Qt提供了一下几个标准View(与标准Model相对应):
- QListView。
- QTableView。
- QTreeView。
- QColumnView。
- QHeaderView:用来提供表头视图给其他View使用。
其中QColumnView比较特殊,它是一个多列视图,每一列是一个QListView,点击前一列的某项会触发下一列的内容内变:
这里我们选择抽象基类,仅仅。。。。。出于随意(-_-, 嘿嘿)。
1.2 继承虚基类2步走
通过继承虚基类来实现自定义View主要有两步:实现纯虚函数、重载需要的虚函数。
1.2.1 实现QAbstractItemView的纯虚函数
自定义基类,第一步是要让你的View运行起来,我们需要实现所有的纯虚函数。因此我们需要实现以下函数:
// pure virtuals
virtual QRect visualRect(const QModelIndex &index) const;
virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);
virtual QModelIndex indexAt(const QPoint &point) const;
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
virtual int horizontalOffset() const;
virtual int verticalOffset() const;
virtual bool isIndexHidden(const QModelIndex &index) const;
virtual void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command);
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const;
visualRect()
函数返回模型下标代表的项占据的视图范围,如果该项包含多个区域,则返回主区域范围。该函数将数据模型与视图范围相关联。基本上,View的作用就是为了渲染Model数据显示,既然涉及到渲染,那渲染的范围必然是非常重要的。下面是我自定义View的现实部分(以防万一还进行了无效下标的检测,并返回无效范围):
QRect CustomeView::visualRect(const QModelIndex &index) const
{
if (!index.isValid()) return QRect();
int row = index.row();
int column = index.column();
if (row == 0) {
if (column == 0) return m_leftTopSquare;
if (column == 1) return m_rightTopSquare;
}
if (row == 1) {
if (column == 0) return m_rightBottomSquare;
if (column == 1) return m_leftBottomSquare;
}
return QRect();
}
indexAt()
,当用户鼠标点击的时候,我们需要判断它涉及那部分数据,这个函数就是用来处理这方面的逻辑的。这里我简单判断了点是否包含在四个格子里,是的话返回相应的数据下标;否则返回无效下标:
// used when users clicks at the client area
QModelIndex CustomeView::indexAt(const QPoint &point) const
{
Q_ASSERT(this->model() != nullptr);
if (m_leftTopSquare.contains(point)) return this->model()->index(0, 0);
if (m_rightTopSquare.contains(point)) return this->model()->index(0, 1);
if (m_rightBottomSquare.contains(point))return this->model()->index(1, 0);
if (m_leftBottomSquare.contains(point)) return this->model()->index(1, 1);
return QModelIndex();
}
moveCursor()
,虽然这个函数名是Cursor,但好像跟光标没有关系,只设计键盘的操作,我们的按键功能就在这里实现。简而言之,这个函数处理键盘上的移动按键(多个箭头按键、Home键、End键等),返回下一个选中项。我们只处理右箭头:
QModelIndex CustomeView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
Q_ASSERT(this->model() != nullptr);
if (cursorAction != QAbstractItemView::MoveRight ||
!m_currentModelIndex.isValid())
return QModelIndex();
else {
auto row = m_currentModelIndex.row();
auto column = m_currentModelIndex.column();
if (row == 0) {
if (column == 0) return this->model()->index(0, 1);
if (column == 1) return this->model()->index(1, 0);
}
if (row == 1) {
if (column == 0) return this->model()->index(1, 1);
if (column == 1) return this->model()->index(0, 0);
}
}
}
setSelection()
也处理选中项的问题,但是函数传入的是一个范围Rect和一个选中命令(选中、取消选中等)。用户点击时,也会调用这个函数。跟你想的不一样,这时候传入的不是一个点,也是一个范围。鼠标点击时,或多或少也会有移动,这就形成了一个小型的矩形范围(□)。 通过判断这个Rect是否包含在某个格子里面,我们做出相应的选中操作(或什么都不做),完成鼠标点击选中功能:
void CustomeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
{
if (m_leftTopSquare.contains(rect))
m_currentModelIndex = this->model()->index(0, 0);
else if (m_rightTopSquare.contains(rect))
m_currentModelIndex = this->model()->index(0, 1);
else if (m_rightBottomSquare.contains(rect))
m_currentModelIndex = this->model()->index(1, 0);
else if (m_leftBottomSquare.contains(rect))
m_currentModelIndex = this->model()->index(1, 1);
if (m_currentModelIndex.isValid())
this->selectionModel()->select(m_currentModelIndex, command);
}
其他纯虚函数我们不关心,直接返回了一个随意值。
1.2.2 重载功能需要的虚函数
到这里我们只剩自定义的图案还没有画,显而易见,我们在painrEvent()
里面实现。需要注意的是,我们实际上是在viewport
上画图,而不是在QAbstractItemView上,所以我们要把viewport指针传给QPainter:
void CustomeView::paintEvent(QPaintEvent *event)
{
QPainter painter(this->viewport());
if (this->model() != nullptr) {
updateOneRect(painter, m_leftTopSquare, 0xff22ff, this->model()->index(0, 0));
updateOneRect(painter, m_rightTopSquare, 0xff2244, this->model()->index(0, 1));
updateOneRect(painter, m_rightBottomSquare, 0x999911, this->model()->index(1, 0));
updateOneRect(painter, m_leftBottomSquare, 0x992244, this->model()->index(1, 1));
}
if (this->selectionMode()) {
auto selectedIndexes = this->selectionModel()->selectedIndexes();
for (auto index : selectedIndexes) {
if (index == this->model()->index(0, 0))
painter.fillRect(m_leftTopSquare.adjusted(20, 20, -20, -20), Qt::cyan);
if (index == this->model()->index(0, 1))
painter.fillRect(m_rightTopSquare.adjusted(20, 20, -20, -20), Qt::cyan);
if (index == this->model()->index(1, 0))
painter.fillRect(m_rightBottomSquare.adjusted(20, 20, -20, -20), Qt::cyan);
if (index == this->model()->index(1, 1))
painter.fillRect(m_leftBottomSquare.adjusted(20, 20, -20, -20), Qt::cyan);
}
}
QAbstractItemView::paintEvent(event);
}
运行结果
完整代码见此处。
Qt--自定义View的更多相关文章
- (一) Qt Model/View 的简单说明
(一) Qt Model/View 的简单说明 .预定义模型 (二)使用预定义模型 QstringListModel例子 (三)使用预定义模型QDirModel的例子 (四)Qt实现自定义模型基于QA ...
- Qt Model/View 的简单说明
目录: (一) Qt Model/View 的简单说明 .预定义模型 (二)使用预定义模型 QstringListModel例子 (三)使用预定义模型QDirModel的例子 (四)Qt实现自定义模型 ...
- 自定义view(一)
最近在学习自定义view 一遍看一别学顺便记录一下 1.View的测量-------->onMeasure() 首先,当我们要画一个图形的时候,必须知道三个数据:位置,长度,宽度 才能确定 ...
- Android 自定义View及其在布局文件中的使用示例
前言: 尽管Android已经为我们提供了一套丰富的控件,如:Button,ImageView,TextView,EditText等众多控件,但是,有时候在项目开发过程中,还是需要开发者自定义一些需要 ...
- Android自定义View之圆环交替 等待效果
学习了前面两篇的知识,对于本篇实现的效果,相信大家都不会感觉太困难,我要实现的效果是什么样呢?下面请先看效果图: 看上去是不很炫的样子,它的实现上也不是很复杂,重点在与onDraw()方法的绘制. 首 ...
- Android自定义View初步
经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...
- Android之自定义View的实现
对于学习Android开发的小童鞋对于自定义View一定不会陌生,相信大家对它是又爱又恨,爱它可以跟随我们的心意设计出漂亮的效果:恨它想要完全流畅掌握,需要一定的功夫.对于初学者来说确实很不容易,网上 ...
- [转]Android自定义控件三部曲系列完全解析(动画, 绘图, 自定义View)
来源:http://blog.csdn.net/harvic880925/article/details/50995268 一.自定义控件三部曲之动画篇 1.<自定义控件三部曲之动画篇(一)—— ...
- 通过圆形载入View了解自定义View
这是自定义View的第一篇文章,通过制作简单的自定义View来了解自定义View的流程. 自定义View是Android学习和开发中必不可少的一部分.通过自定义View我们可以制作丰富绚丽的控件,自定 ...
- 自定义view(二)
1.View 的绘制 通过继承View 并重写它的onDraw()来完成绘制. onDraw()有一个参数,就是Canvas对象.使用这个Canvas就可以绘制图像了,Canvas canvas = ...
随机推荐
- myeclipse db browser 新建数据源
Myeclipse 新建数据源 一.打开myeclipse(打开了当我没说) 二.在window选项中找到show view ,点击other,输入db,选择DB Browser 三.在DB Brow ...
- 关于springmvc接受简单参数和List集合数据的实现
首先要创建一个搭建一个springmvc的工程,至于如何搭建这里就不说了.给出比较重要的配置,项目目录结构如下,弄的比较简单,因为最近遇到一个需要传递List集合数据的问题,所以就当做实验. web. ...
- 一起来学Go --- (go的枚举以及数据类型)
枚举 枚举指一系列的相关的常量,比如下面关于一个星期的中每天的定义,通过上篇博文,我们可以用在const后跟一对圆括号的方式定义一组常量,这种定义法在go语言中通常用于定义枚举值.go语言并不支持众多 ...
- Javaweb分页功能简单实现
效果如下图 数据库中的数据 页面效果 首先,创建一个通用类Page,代码及 ...
- linux系统安装配置exim4(源码安装)
一.Exim4概述 Exim是一个MTA(Mail Transfer Agent,邮件传输代理)服务器软件,该软件基于GPL协议开发,是一款开源软件.该软件主要运行于类UNIX系统.通常该软件会与Do ...
- Effective Java通俗理解(下)
Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...
- JS判断当前使用设备是pc端还是web端(转MirageFireFox)
js判断当前设备 最近用bootstrap做自适应,发现仍然很难很好的兼容web端和PC端的现实. 仔细观察百度,淘宝,京东等大型网站,发现这些网站都有对应不同客户端的子站. 站点 PC端url we ...
- MEF IOC使用
IOC介绍 IOC:控制反转,DI:依赖注入.按我的理解应该是一个东西.作用目前我看到的主要是解除各个层之间的强耦合,实现接口分离.MEF优点: 1.net4 自带,无需安装扩展(引用System.C ...
- TX2017秋招笔试题之编码
问题描述: 假定一种编码的编码范围是a ~ y的25个字母,从1位到4位的编码,如果我们把该编码按字典序排序,形成一个数组如下: a, aa, aaa, aaaa, aaab, aaac, - -, ...
- 浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题
本文出处:http://www.cnblogs.com/wy123/p/7501261.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...