QTreeView自绘实现酷炫样式
本篇文章结合笔者的经历,介绍一种通过重写QTreeView绘制事件,使用QPainter来实现好看的列表的方式。
导语
Hi,各位读者朋友,大家好。相信大家在日常的工作中,经常会接触到QTreeView这个控件吧!
QTreeView,顾名思义,就是一种树形的控件,在我们需要做类似于文件列表的视图时,是一个不错的选择。然而,仅通过设置样式表,往往无法完全满足我们的需求。迫不得已,我们只能选择自实现QTreeView的绘制事件,通过画笔,逐个绘制我们想要的效果。
关于QTreeView的样式表部分,Qt官方给出了一些示例:Customizing QTreeView , 本文不作具体介绍,感兴趣的读者可以自行观看。
话不多说,咱们先上效果图:
接下来,咱们通过一下几个步骤,逐个分析,怎么达到上图的效果。
- 整个控件的背景色
- 单元格的效果
- 单击和双击选择的效果
整个控件的背景色
话不多说,码来!
void MyTreeView::paintEvent(QPaintEvent* event)
{
int count = m_pModel->rowCount(this->rootIndex());
QPainter painter(this->viewport());
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(40, 44, 52));
painter.drawRect(this->viewport()->rect());
// 列表为空时,绘制空状态
if (count == 0) {
QPoint startPos(this->width() / 2 - 72, this->height() / 2);
QFont font("Microsoft YaHei UI");
font.setPixelSize(12);
painter.setFont(font);
painter.setPen(QColor(153, 154, 161));
painter.setBrush(Qt::NoBrush);
painter.drawText(startPos.x(), startPos.y() + 64 + 8, 148, 32, Qt::AlignCenter | Qt::TextWordWrap, "文件夹为空");
return;
}
QTreeView::paintEvent(event);
}
在paintEvent中,首先在整个QTreeView的区域,绘制了一个背景色。其次判断model中是否没有数据,如果没有数据,则居中绘制一个空状态。
单元格的效果
绘制单元格,我们通过drawRow去为每一个单元格绘制我们想要的效果。
void MyTreeView::drawRow(QPainter* painter, const QStyleOptionViewItem& options, const QModelIndex& index) const
{
if (!index.isValid())
return;
painter->save();
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
QString path = m_pModel->filePath(index);
QFileInfo file(path);
QRect rect = options.rect;
painter->setPen(Qt::NoPen);
//判断状态
bool bNormal = false, bDClick = false, bClick = false;
if (path == m_selectPath)
{
bDClick = true;
} else if (index == currentIndex()) {
bClick = true;
} else {
bNormal = true;
}
// 计算当前Index的目录相对root目录的层级
int rootPathSize = m_pModel->rootPath().size();
int indexPathSize = m_pModel->filePath(index).size();
QString relativePath = m_pModel->filePath(index).mid(rootPathSize, indexPathSize - rootPathSize);
int indexTier = relativePath.split('/').size() - 1;
// 当前index的缩进
int indexIndentation = (indexTier - 1) * TierIndentation + 26;
//QString theme = theme_manager::Instance()->GetThemeName();
#pragma region 绘制底色
if (bNormal)
{
QBrush brush = QBrush(QColor(40, 44, 52));
painter->setBrush(brush);
painter->drawRect(rect);
}
#pragma endregion
#pragma region 绘制选中样式
if (!file.isDir() && bDClick)
{
QBrush brush = QBrush(QColor(50, 56, 66));
painter->setBrush(brush);
painter->drawRect(rect);
}
#pragma endregion
#pragma region 绘制单击样式
if (bClick)
{
QBrush brush = QBrush(QColor(44, 49, 58));
painter->setBrush(brush);
painter->drawRect(rect);
}
#pragma endregion
#pragma region 绘制图标
// 画图标
int iconWidth = file.isDir() ? FOLDER_WIDTH : ARROW_WIDTH;
int iconHeight = file.isDir() ? FOLDER_HEIGHT : ARROW_HEIGHT;
// (iconWidth - ARROW_HEIGHT)是绘制普通文件时,应该多往右边移动一点
int xIcon = rect.x() + indexIndentation - (iconWidth - FOLDER_WIDTH);
int yIcon = rect.y() + (SectionHeight - iconHeight) / 2;
QRect iconRec(QPoint(xIcon, yIcon), QSize(iconWidth, iconHeight));
if (file.isDir())
{
// 绘制文件夹展开与收起样式
isExpanded(index) ? painter->drawImage(iconRec, m_ImageTreeFolderExpand)
: painter->drawImage(iconRec, m_ImageTreeFolder);
}
else
{
QRect iconRec(QPoint(xIcon, yIcon), QSize(14, 14));
painter->drawImage(iconRec, m_ImageCppFile);
}
#pragma endregion
#pragma region 绘制文字
QString text = index.data(Qt::DisplayRole).toString();
// x坐标为当前层次缩进+图标宽度(20)+文字与图标间距(5)
QRect leverRect(QPoint(rect.x() + indexIndentation + FOLDER_WIDTH + 4, rect.y()),
QSize(rect.width(), rect.height()));
// 绘制文字
QColor themeTextColor(204, 204, 204);
painter->setFont(m_textFont);
painter->setBrush(Qt::NoBrush);
painter->setPen(QPen(themeTextColor));
QFontMetrics fm(m_textFont);
text = fm.elidedText(text, Qt::ElideRight, m_width);
QRect boundingRect;
painter->drawText(leverRect, Qt::AlignLeft | Qt::AlignVCenter, text, &boundingRect);
#pragma endregion
#pragma region 绘制箭头
//绘制箭头
bool bExpand = this->isExpanded(index);
int xArrow = rect.x() + MARGIN_WIDTH;
int yArrow = rect.topLeft().y() + (SectionHeight - 8) / 2;
QRect border(xArrow, yArrow, 8, 8);
bool bDir = file.isDir(), bEmpty = true;
if (bDir)
{
QDir dir(path);
bEmpty = dir.isEmpty();
}
if (bDir && !bExpand && !bEmpty)
{
painter->drawImage(border, m_ImageExpand);
} else if (bDir && bExpand && !bEmpty)
{
painter->drawImage(border, m_ImageClose);
}
#pragma endregion
painter->restore();
}
️绘制的顺序,需要根据各个部分实际所在的图层顺序去绘制,比如说先要绘制底色,再去绘制上面的内容。
单击选择和双击选择
这一个部分,我们通过重载 mouseDoubleClickEvent 和 mousePressEvent 来监控鼠标的事件。
void MyTreeView::mousePressEvent(QMouseEvent* event)
{
QTreeView::mousePressEvent(event);
QModelIndex index = indexAt(event->pos());
if (!index.isValid())
return;
//是否为文件夹
const auto& path = m_pModel->filePath(index);
QFileInfo info(path);
if (!info.isDir())
return;
//是否在箭头区域
QPoint point = event->pos();
int width = this->viewport()->width();
QRect rect(width - MARGIN_WIDTH - ARROW_WIDTH, SectionHeight - MARGIN_HEIGHT - ARROW_HEIGHT, ARROW_WIDTH, ARROW_HEIGHT);
bool bContain = rect.contains(QPoint(event->pos().x(), event->pos().y() % SectionHeight));
if (!bContain)
return;
// 如果目录为空,不触发箭头交互效果
QDir tmpDir(path);
if (tmpDir.isEmpty())
return;
//展开收缩节点
this->isExpanded(index) ? this->collapse(index)
: this->expand(index);
}
void MyTreeView::mouseDoubleClickEvent(QMouseEvent* event)
{
QModelIndex index = this->indexAt(event->pos());
if (index != this->currentIndex())
return;
// 处理普通文件
QString path = m_pModel->filePath(index);
if (!m_pModel->isDir(index)) {
m_selectPath = path;
}
update();
QTreeView::mouseDoubleClickEvent(event);
}
通过上面的几个函数,我们就可以实现自定义绘制QTreeView的需求,如果还需要绘制更多的东西,则只要自己往上面添加绘制的区域即可。
一些碎碎念
本来觉得绘制一个QTreeView比较麻烦,包括在这篇博客的时候仍然是这种感觉,但是通过输出这篇博客,发现其实也很简单(当然,不考虑绘制效率的情况下)。就像我刚开始接触Qt时,总觉得自绘是一件很麻烦、很困难的事,想着全部都要自己去画。
等到后面真正接触到的时候,更慌了。但没办法,任务来了,怎么办?硬着头皮上呗,一顿捯饬之后,我发现,其实也没那么难。
所以说,刚接触Qt的同学,如果样式表达不到自己需求,就勇敢的去尝试自绘吧!️
创作不易,如果对您有帮助,点赞、关注、收藏支持一下!不甚感激!
QTreeView自绘实现酷炫样式的更多相关文章
- 纯CSS3实现的一些酷炫效果
之前在网上看到一些用纯CSS3实现的酷炫效果,以为实现起来比较困难,于是想看看具体是怎么实现的. 一.笑脸猫动画 实现效果如下: 这个实现起来确实比较麻烦,很多地方需要花时间,有耐心地调整. 1.先看 ...
- 三分钟学会用 js + css3 打造酷炫3D相册
之前发过该文,后来不知怎么回事不见了,现在重新发一下. 中秋主题的3D旋转相册 如图,这是通过Javascript和css3来实现的.整个案例只有不到80行代码,我希望通过这个案例,让正处于迷茫期的j ...
- html5+Canvas实现酷炫的小游戏
最近除了做业务,也在尝试学习h5和移动端,在这个过程中,学到了很多,利用h5和canvas做了一个爱心鱼的小游戏.点这里去玩一下 PS: 貌似有点闪屏,亲测多刷新两下就好了==.代码在本地跑都不会闪, ...
- 纯CSS3写的10个不同的酷炫图片遮罩层效果【转】
这个是纯CSS3实现的的10个不同的酷炫图片遮罩层效果,可以欣赏一下 在线预览 下载地址 实例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...
- css3实现酷炫的3D盒子翻转效果
简介 运用css3先在平面空间组成立方体盒子,再让整个盒子翻转起来,先来张效果图: 步骤 1.先用css将6张图片摆成下图的样子: 下面就是通过css3的3D变换将每个面进行翻转,使之成为一个立体的盒 ...
- 纯CSS3写的10个不同的酷炫图片遮罩层效果
这个是纯CSS3实现的的10个不同的酷炫图片遮罩层效果,可以欣赏一下 在线预览 下载地址 实例代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...
- Android酷炫实用的开源框架(UI框架)
Android酷炫实用的开源框架(UI框架) 前言 忙碌的工作终于可以停息一段时间了,最近突然有一个想法,就是自己写一个app,所以找了一些合适开源控件,这样更加省时,再此分享给大家,希望能对大家有帮 ...
- Android酷炫实用的开源框架——UI框架(转)
转载别人整理好的文章,列出了很多炫酷的UI开源设计 原文地址:http://www.androidchina.net/1992.html 1.Side-Menu.Android分类侧滑菜单,Yalan ...
- Android酷炫实用的开源框架(UI框架) 转
Android酷炫实用的开源框架(UI框架) 前言 忙碌的工作终于可以停息一段时间了,最近突然有一个想法,就是自己写一个app,所以找了一些合适开源控件,这样更加省时,再此分享给大家,希望能对大家有帮 ...
- WPF换肤之六:酷炫的时区浏览小精灵
原文:WPF换肤之六:酷炫的时区浏览小精灵 由于工作需要,经常要查看到不同地区的 当前时间,以前总是对照着时区表来进行加减运算,现在有了这个小工具以后,感觉省心了不少.下面是软件的截图: 效果图赏析 ...
随机推荐
- 2021-04-21:手写代码:Dijkstra算法。
2021-04-21:手写代码:Dijkstra算法. 福大大 答案2021-04-21: Dijkstra算法是一种基于贪心策略的算法.每次新扩展一个路程最短的点,更新与其相邻的点的路程.时间紧,未 ...
- vue请求后端数据和跨域问题
最近遇到的一个问题 后端写好的接口,前端怎么获取数据 这是我后端的接口:GET 接口 这是我前端运行的项目地址: 简单使用: 咱门前端使用 颇受好评的 axios 来发起请求 这是它的官网:https ...
- 软硬件--智能穿戴常见BUG及原因分析
软硬件--智能穿戴常见BUG及原因分析 1.手表有常亮功能(类似熄屏表盘),开启常亮暗屏状态下 按侧键,设备时间出现倒退现象:频率切换相关问题: 2.手表有常亮功能(类似熄屏表盘),开启常亮暗屏状态下 ...
- bootstrap treeview基本运用
虽然现在有了很多新的前端框架,但是有的时候我们做一个不需要任何其他js编译环境就可以运行的项目,那还是的使用一些老式技术,接下来就来回顾一些bootstrap treeview + jquery的使用 ...
- Pycharm激活码,Pycharm稳定专属激活码(持续更新)
分享一下 PyCharm 2023.1.2 最新激活注册码,破解教程如下,可免费永久激活,亲测有效,下面是详细文档哦~ 申明:本教程 PyCharm 激活码收集于网络,请勿商用,仅供个人学习使用,如有 ...
- Python批量填补遥感影像的无效值NoData
本文介绍基于Python中ArcPy模块,对大量栅格遥感影像文件批量进行无效值(NoData值)填充的方法. 在处理栅格图像文件时,我们经常会遇到图像中存在有无效值(即NoData值)的情况. ...
- UpSetR 关联的 venneuler 包安装笔记
本文章已经设置了最低额度的付费阅读,如果您觉得文章对您有用,且手头宽裕,欢迎请作者喝杯热茶.本文章付费部分内容并不影响您对文章的阅读和理解,只是作者对付费阅读的一次尝试,感谢. 背景 R 语言中的 v ...
- 18.详解AQS家族的成员:Semaphore
关注:王有志,一个分享硬核Java技术的互金摸鱼侠. 欢迎你加入Java人的提桶跑路群:共同富裕的Java人 今天我们来聊一聊AQS家族中另一个重要成员Semaphore,我只收集到了一道关于Sema ...
- 【技术积累】Python中的NumPy库【一】
NumPy库是什么 NumPy是Python科学计算的核心库之一,用来进行科学计算,数值分析等矩阵运算.主要提供了以下几种功能: 1.多维数组(ndarray)对象,可以进行快速的数值计算和数组操作: ...
- 【后端面经-Java】Java创建线程的方法简介
目录 1. 线程的基本概念 1.1 线程 1.2 线程状态和生命周期 2. 创建线程的四种方法 2.1 继承Thread类 2.2 实现Runnable接口 2.3 实现Callable接口 2.4 ...