QRowTable表格控件-支持hover整行、checked整行、指定列排序等
目录
原文链接:qrowtable表格控件-支持hover整行、checked整行、局部列排序等
一、开心一刻
老公和老婆晚上回家,路旁突然跳出三个持刀蒙面大汉:“绑架!你俩可以走一个,回家等消息。”
老公一把将老婆推开:“老婆快走!”待老婆走远后,三个蒙面人摘下面具:“尼玛现在找你打个麻将这么费劲?”
五分钟后老公致电老婆:"往卡里打五千块,别报警,他们说关我一夜明早放人。"十分钟后老公从卡里取走五千块战斗到天明。
次日老公回家,老婆扑上来含泪说:“关键时候才能看出老公对我的好,老公以后我啥都听你的!
二、嘴一嘴
看完笑话,我们进入正题。
本篇文章我们带来的是一个表格的简单使用,主要是把表格的hover、checked进行了重新定制,支持用户自己去设置相关颜色,并且可以对指定列进行放大缩小等。
为什么会写这篇文章呢?博主自己使用qt也好几年了,对于qt的使用算是比较有心得了吧。最近在做表格的一些相关东西,发现网上的很多文章讲的不是特别好,因此将自己做的一个简单事例做一分享,希望能帮到有需要的同学。
本篇文章我们主要对表格的以下内容进行了封装,内容比较有限,有深度定制需求的同学可以加我qq详谈。
- hover行背景色
- checked行背景色
- 列宽是否可排序
- 列宽允许拖动范围
- 准确定位hover状态
三、效果展示
如下图所示,一个简单的gif效果展示。
配色时博主自己随便搞的,配色只能说很一般,大家主要看效果和实现思路。
四、浅谈实现
使用过qtableview或者了解控件的同学应该都很清楚,表格是一个很强大的控件,支持我们做各种各样的功能,博主之前也想过几篇相关的文章,都是讲述表格控件的。
当然了表格控件的使用远远不止于此,后续有时间和精力我会陆续推出更多有用好玩儿的功能。
本篇文章的功能实现也比较简单,写这个demo我大概用了大半天的时间,代码量其实不多,大部分的时间主要用来设计接口和重构逻辑功能了。
如下图所示,是整个工程目录结构,这里主要重写了数据源model和view
下面我们就分别讲述model和view都干了些什么
五、自定义数据源
所谓数据源,其实主要就是给view提供数据的地方,这里我们取了一个巧,数据源直接继承自qstandaritemmodel,这样的话数据的存储和获取大部分的工作都不需要我们去关心,因为qstandaritemmodel这个类已经做的很完善。
这里我们主要从写了两个接口
virtual qvariant data(const qmodelindex &index, int role = qt::displayrole) const override; virtual qt::itemflags flags(const qmodelindex & index) const override;
1、data函数
视图获取数据的接口就是data,因此这个接口我们必须重写,实现代码也很简单,主要是需要大家对qt的mvc有一个基本的认识。
qvariant qrowmodel::data(const qmodelindex & index, int role /*= qt::displayrole*/) const { if (qt::backgroundrole == role) { if (index.row() == m_ihoverrow) { return m_hovercolor; } } else if (role == qt::foregroundrole) { if (index.row() == m_ihoverrow) { return m_hovercolor.lighter(255); } } return qstandarditemmodel::data(index, role); }
看上述代码,当绘制视图的时候,hover一行时,我们需要返回自定义的颜色值,其他情况走默认处理即可,是不是很简单。
关于checked的实现不能放在这里,因为当item被选中的时候,item的背景色不是取自qt::backgroundrole,同时前景色也不是取自qt::foregroundrole,因此这里处理不了。
如果非要让checked状态在这里实现也是有办法的,我们可以重写flags函数,让item不能被选中,这个办法博主是试过的,没有问题。 但是就存在一个隐,如果后期我们的item想被选中,就比较麻烦了。
基于以上设想,我们只需要这样重写flags函数,然后在data函数中返回checked行的背景色和前景色即可。
qt::itemflags qrowmodel::flags(const qmodelindex & index) const { return qt::itemisenabled; }
2、flags函数
上一小节flags函数都已经展示出来了,item处于可用状态,这里还需要在添加上可选中状态,以免有其他坑
qt::itemflags qrowmodel::flags(const qmodelindex & index) const { return qt::itemisenabled | qt::itemisselectable; }
以上就是model函数的重写了,比较简单,下面放出model的头文件
/** * 简介: 主要提供接口 提供hover行 */ class qrowmodel : public qstandarditemmodel { q_object public: explicit qrowmodel(qobject * parent = 0); ~qrowmodel(); private: //hover时背景色 不建议外部直接调用 void sethovercolor(const qcolor & color); qcolor gethovercolor() const { return m_hovercolor; } //设置当前hover行 不建议外部直接调用 void sethoverrow(int row); int gethoverrow() const { return m_ihoverrow; } protected: virtual qvariant data(const qmodelindex &index, int role = qt::displayrole) const override; virtual qt::itemflags flags(const qmodelindex & index) const override; private: int m_ihoverrow = -1;//没有hover qcolor m_hovercolor = qcolor(20, 22, 23); friend class qrowtable; };
六、自定义视图
下面讲述今天的重头戏view。
首先需要搞清楚,重写视图需要完成哪些工作,才能更好的准备定位每个函数的意思。
1、目的
- hover时,把hover行通知到数据源
- 准确的hover和取消hover状态,这个问题确实搞了好久
- 点击item时,设置行选中色
- 指定列可排序
有了以上目标之后,我们逐个问题解决。
2、问题分析
针对以上4个目的,我们逐个分析解决办法
1、鼠标hover到这个比较简单,而且可以通过多种方式进行获取。
想要拿到这个状态,有一个很重要的属性需要开启:setmousetracking(true);
a、如果我们重写的是qtablewidget这个类,那么可以去接收cellentered这个信号
b、如果重写跟本篇文章一样,重写的qtableview这个类,那么我们需要重写mousemoveevent这个函数,然后通过indexat函数获取当前行。
void qrowtable::mousemoveevent(qmouseevent * event) { qtableview::mousemoveevent(event); const qmodelindex & index = indexat(event->pos()); int row = -1; if (index.isvalid()) { row = index.row(); } if (m_pmodel->gethoverrow() != row) { m_pmodel->sethoverrow(index.row()); viewport()->update(); } }
2、取消hover装态
这个问题处理确实化了好长时间,主要还是想处理的办法更优雅,效率高一些。
这里为了实现不在item上时立刻取消hover状态,主要做了2件事。
第一件事
重写leaveevent函数,鼠标离开时,恢复hover行为-1
void qrowtable::leaveevent(qevent * e) { if (m_pmodel->gethoverrow() != -1) { m_pmodel->sethoverrow(-1); viewport()->update(); } qtableview::leaveevent(e); }
第二件事
重写了qheaderview,当鼠标在表头移动时,发送mousemove信号,重置当前hover行为-1
void qrowheader::mousemoveevent(qmouseevent * event) { emit mousemove(); qheaderview::mousemoveevent(event); }
auto callback = [this]{ if (m_pmodel->gethoverrow() != -1) { m_pmodel->sethoverrow(-1); viewport()->update(); } }; connect(vheader, &qrowheader::mousemove, this, callback); connect(hheader, &qrowheader::mousemove, this, callback);
以上这个办法也是不得已为之,如果大家有更好的办法,欢迎评论区留言
3、点击item时设置当前行高亮背景色
第一小节的最后,我们也提到了,checked正行可以放到data函数中完成,但是由于我们的item有了可选择属性后,当前选中的item,或者选中行的背景色和前景色都是取自以下两个属性,因此这里我们只需要把这两个属性颜色值进行设置即可。
qpalette::highlight qpalette::highlightedtext
设置checked行颜色实现方式如下。
void qrowtable::setcheckedcolor(const qcolor & color) { m_checkedcolor = color; qpalette palette = this->palette(); palette.setbrush(qpalette::inactive, qpalette::highlight, m_checkedcolor); palette.setbrush(qpalette::inactive, qpalette::highlightedtext, m_checkedcolor.lighter(255)); setpalette(palette); }
如果仔细想一想,可能就会觉得有些问题,这里我们怎么去控制是一个单元格背景色还是整行背景色呢?
莫慌,加上以下两个属性即可,接口真的很简单,啥意思我就不说了。如果实现不知道的同学,欢迎评论区留言
setselectionbehavior(qabstractitemview::selectrows); setselectionmode(qtableview::singleselection);//不能多选
4、 指定列排序
首先需要搞清楚qt自带的排序处理逻辑,然后才可以对症下药,这里我也是跟了qt自己代码,然后各种分析后,得出的处理方式。
qt的代码分析这里不做讨论,有需要的同学私信吧。
各种代码跟踪后发现,当我们设置了setsortingenabled为true时,qt就支持排序了,并且在排序完成后会发给我们一个sortindicatorchanged信号,这个信号主要是用来显示排序三角形的。
如果我们想不显示排序三角形,但是只是排序也是可以的,可以在接收这个信号后,调用setsortindicatorshown接口隐藏三角形
为什么会这样麻烦呢?因为楼主跟踪qt的代码了,发现如果排序了,qt的三角形就是必须画出来的
void qheaderview::paintsection(qpainter *painter, const qrect &rect, int logicalindex) const { ... if (issortindicatorshown() && sortindicatorsection() == logicalindex) opt.sortindicator = (sortindicatororder() == qt::ascendingorder) ? qstyleoptionheader::sortdown : qstyleoptionheader::sortup; ... }
是不是很刺激,qt太强大了,啥事情都给我们搞好了。
本篇文章我们主要是实现指定列不能进行排序,因此处理函数是这么干的,当接收到非排序列排序信号时,调用setsortingenabled接口设置禁止排序。
void qrowtable::sortcolumnchanged(int logicalindex, qt::sortorder order) { if (m_indicator.contains(logicalindex) && m_indicator[logicalindex] == false) { if (issortingenabled()) { setsortingenabled(false); } } else { if (issortingenabled() == false) { setsortingenabled(true); } } }
七、测试
1、view配置
qrowtable w; //前两列不可排序 w.setindicatorvisible(0, false); w.setindicatorvisible(1, false); //设置列最大宽度 w.setcolumnminwidth(50); //设置列最小宽度 w.setcolumnmaxwidth(150); w.sethovercolor(qt::red);
2、model配置
qrowmodel * model = w.getmodel(); qstringlist headlist; headlist << qstringliteral("代码") << qstringliteral("名称") << qstringliteral("行业") << qstringliteral("价格") << qstringliteral("涨跌幅") << qstringliteral("换手率"); model->sethorizontalheaderlabels(headlist);
数据添加就跟我们平时往qstandarditemmodel中放数据类似,代码太长就不放出来了。
八、相关文章
以上就是本篇文章的所有内容了,一个简易的整行hover、整行checked的表格
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!
很重要--转载声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者: or twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。