Qt中派生QAbatractProxyModel

Qt是非常强大的,功能很全,它有个特点:如果你顺着它的特性做需求,那就会非常舒服顺畅。如果你要越过它的特性做一些特别的事:

VERY PAINFULL

以下记录本人的一个非常痛苦的经历,希望可以让后来人少走弯路。

首先遇到的需求是,做一个可以根据外部筛选规则来自定义显示的tableview,外界可以选择显示数据集合中的某一段,此时tableview表现就好像整个数据集合只有这些数据一样。另外,原数据是一维数据,显示出来将是一个有行列的二维数据。这个规则不能用Qt的QSortFilterProxyModel来做,因为Filter只能针对DisplayRole做一些filter操作,说白了,是为查找功能而生的。当然绝大多数软件都只会有常规查找功能,不会有我这样奇葩的需求。

当然,也可以通过Role来进行筛选,不过作为一个程序员,不能满足于使用这种类似“HACK”的方法实现。既然我们的需求和QSortFilterProxyModel的设计初衷不一样了,自然,我们应该写一个新的QAbstractProxyModel。

一般来说,从QAbstractProxyModel直接继承需要写很多代码,其中包括connect大量原数据集合的各种数据更改signal。这是非常痛苦的。所以,我们不应该直接从QAbstractProxyModel,这也是Qt所推荐的:Qt文档里指出,应该选择最接近的行为的ProxyModel进行继承。于是,我选择了存在感最低的QIdentityProxyModel,事实上也没什么更好选择,总共就两个,QIdentityProxyModel和QSortFilterProxyModel,后者所做的特化太多了。

继承了它之后,重写几个必要函数:

QModelIndex mapToSource(const QModelIndex &proxy_index) const Q_DECL_OVERRIDE;
QModelIndex mapFromSource(const QModelIndex &source_index) const Q_DECL_OVERRIDE;
QModelIndex sibling(int row, int column, const QModelIndex &idx) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex & proxy_child) const Q_DECL_OVERRIDE;
QModelIndex index(int row,int column,const QModelIndex &proxy_index) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &proxy_parent) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &proxy_parent) const Q_DECL_OVERRIDE;

片刻的编译之后,程序如愿地进行了filter。

然而这并不是完美结局,很快发现一个奇怪的问题:除了以原数据0为开始的filter形式,其他形式的选择功能全部表现得非常奇怪,具体地说,不管怎样进行矩形范围选择,selection永远都是从原数据第0个,到所选择的第一个项。然而如果直接使用原数据集合,那当然是没有问题。

这就非常奇怪了,因为我正确地实现了mapToSource和mapFromSource。难道proxy不应该是透明了吗?为什么会产生这样的变化?由于Qt在mac上不太好调试,我无法跟踪到Qt代码中,进行了各种log输出,最奇怪的在于:当调用selectionModel->selectedIndex()时,结果是错误的,但在selectionChanged的信号中,selected和unselected是正确的。这就更奇怪了,我查看源代码,这两处应该是一样的处理方式。

最后,我把目光放在QIdentityProxyModel上,检查发现,它重写了sibling函数。我重写这个函数,直接调用QAbstractProxyModel的函数,问题解决。

原因在于,selection在导出index的时候,使用了sibling,从top到bottom,一路用sibling来创建index。而QIdentityProxyModel中,sibling是直接转发了原始数据集合的,而原始数据集合只有一列,导致sibling全部是-1,-1,导致了最后selectedIndex返回的错误。

结论1:Qt做东西,最好顺着它做。

结论2:继承一个并不了解的类做事情,如果出现了问题,首先考虑这个类本身的行为是不是影响了预期目标

时间: 2024-11-03 14:02:32

Qt中派生QAbatractProxyModel的相关文章

[转]Qt中ui文件的使用

用designer设计的*.ui文件可以通过uic工具转换为*.h文件(在编译时也会自动生成这样一个ui_*.h文件),有了这个.h文件就可以直接按照纯C++的方式对其中的类进行调用.ui文件的使用就是利用默认工具uic自动产生一个类,然后用该类的setui函数加载界面到相应的对象上.       .ui文件的使用有三种形式:第一种是直接使用,第二种是定义一个新类,声明一个ui子对象,利用该对象来加载界面,第三种是将ui作为基类派生新的类. 借用一个例程分析如下: 工程及界面          

(转)QT中QWidget、QDialog及QMainWindow的区别

QWidget类是所有用户界面对象的基类. 窗口部件是用户界面的一个基本单元:它从窗口系统接收鼠标.键盘和其它事件,并且在屏幕上绘制自己.每一个窗口部件都是矩形的,并且它们按Z轴顺序排列.一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分. QMainWindow 类提供一个有菜单条.锚接窗口(例如工具条)和一个状态条的主应用程序窗口.主窗口通常用在提供一个大的中央窗口部件(例如文本编辑或者绘制画布)以及周围 菜单.工具条和一个状态条.QMainWindow常常被继承,因为这使得封装

Qt中如何 编写插件 加载插件 卸载插件

Qt中如何 编写插件 加载插件 卸载插件是本文要介绍的内容.Qt提供了一个类QPluginLoader来加载静态库和动态库,在Qt中,Qt把动态库和静态库都看成是一个插件,使用QPluginLoader来加载和卸载这些库.由于在开发项目的过程中,要开发一套插件系统,就使用了Qt的这套类库. 一 编写插件 编写一个Qt的插件需要以下步骤 1.声明一个插件类, 2.定义一个类,实现这个插件类定义的接口,定义的这个类必须从QObject集成下来. 3.使用Q_INTERFACESQ_INTERFACE

解析Qt中QThread使用方法

本文讲述的是在Qt中QThread使用方法,QThread似乎是很难的一个东西,特别是信号和槽,有非常多的人(尽管使用者本人往往不知道)在用不恰当(甚至错误)的方式在使用QThread,随便用google一搜,就能搜出大量结果出来.无怪乎Qt的开发人员 Bradley T. Hughes 声嘶力竭地喊you are-doing-it-wrong 和众多用户一样,初次看到这个时,感到 Bradley T. Hughes有 些莫名奇妙,小题大作.尽管不舒服,当时还是整理过一篇博客QThread 的使

QT中使用Glut库

用Qt中的QGLWidget窗体类中是不包括glut工具库的,难怪在myGLWidget(在我的程序中是QGLWidget的派生类)中绘制实心球体是说“glutSolidSphere”: 找不到标识符,就是说没有这个函数的声明.接下来就来安装glut库: 1.先下载glut库 http://www.opengl.org/resources/libraries/glut/glutdlls37beta.zip. 2.将下载下来的文件解压,将glut32.lib和glut.lib两个lib文件移到qt

Qt中,当QDockWidget的父窗口是一个不可以拖动的QTabWidget的时候实现拖动的方法

之前在做有关QDockWidget的内容时候遇到了瓶颈,那就是窗口弹出来之后拖动不了,也不可以放大和缩小,若是弹出来之后设置成了window的flags,也不可以拖动,而且也不是需要的效果. 1.弹出来之后的dockwidget的titlebar右边需要有3个按钮分别来控制放大与恢复.弹出来与收进去和关闭按钮.考虑到Qt自带的dockwidget弹出来后实现不了这个,所以参考了网上的方法,需要自己从QWidget中派生一个类来实现自己的titlebar 2.因为dockwidget是嵌套在QTa

Qt中的属性设置(搜集整理)

一.Qt中的属性 属性是指窗口或控件的属性,比如opacity属性表示"透明度",geometry指的是"位置和大小",pos属性代表"位置".qt中的控件有自带的属性,我们也可以自己定义属性. QObject这个类有一个函数setProperty,我们可以通过这个函数定义自己的属性,使用方法很简单,setProperty(const char * name, const QVariant & value),第一个参数是属性的名称,第二个

qt 关于Qt中MVC的介绍与使用

Qt包含一组使用模型/视图结构的类,可以用来管理数据并呈现给用户.这种体系结构引入的分离使开发人员更灵活地定制项目,并且提供了一个标准模型的接口,以允许广泛范围的数据源被使用到到现有的视图中. 模型 - 视图 - 控制器(MVC)是一种设计模式,由三类对象组成:模型:应用程序对象.视图:屏幕演示.控制器:定义了用户界面响应用户输入的方式. 在引入MVC之前,用户界面的设计往往是将这些对象组合在一起.MVC的解耦带来了灵活性和重用性. 如果视图和控制器对象相结合,其结果是模型/视图结构,仍然分离了

Qt中的parent形参

在 派生类的构造函数初始化列表中 调用 父类的带有参数的构造函数,是为了初始化从父类继承来的成员变量.因为这些变量无法直接初始化,只能采用这种方式初始化. 而在qt中,MainWindow中的某成员变量(指向父组件的指针,假定为p)无法直接初始化,只能在初始化列表中调用QMainWindow(parent),把形参parent的值间接的传给p,使p完成初始化.