目录
1. 前言
2. 需求
3. 功能实现
3.1. 代码实现
3.2. 功能讲解
4. 附录
1. 前言
本博文用到了Qt的model/view framework框架,如果对Qt的“模型/视图/委托”框架不懂,本博文很难读懂。如果不懂这方面的知识,请在Qt Assistant 中输入Model/View Programming 学习了解。读者本机Qt安装目录下的Examples\Qt-XX.XX.XX\widgets\itemviews目录下有很多model/view framework的例子,可以进行自学了解,其中XX.XX.XX为Qt的版本号,如:5.14.1。
因为QColumnView、QHeaderView、QListView、QTableView、QTreeView、QListWidget 、QUndoView、QTableWidget、QTreeWidget都是从QAbstractItemView继承,故本博文所说的技术点也适用于这些类,本博文以QTableView类来讲解。
2. 需求
业务需求是:鼠标在QTableView的项上方移动(不是按下)时,项背景色改变。即像下面那样的效果:
3. 功能实现
3.1. 代码实现
logTableView.h:
#ifndef LOG_TABLE_VIEW_H
#define LOG_TABLE_VIEW_H#include <QTableView>class CLogTableView : public QTableView
{Q_OBJECTpublic:explicit CLogTableView(QWidget *parent = nullptr);signals:// void mouseEnterItemSigal(const QModelIndex &);
private:/* 注意:不能重载mouseMoveEvent函数,否则QAbstractItemView::entered信号不会触发,如果非要重载mouseMoveEvent函数,则在该函数中发送自定义鼠标进入视图项的信号,如本 类中的mouseEnterItemSigal信号*///virtual void mouseMoveEvent(QMouseEvent *event) override;
};#endif // LOG_TABLE_VIEW_H
logTableView.cpp:
#include "logTableView.h"
#include<QMouseEvent>
CLogTableView::CLogTableView(QWidget *parent): QTableView(parent)
{setMouseTracking(true);
}/* 注意:不能重载mouseMoveEvent函数,否则QAbstractItemView::entered信号不会触发,如果非要重载mouseMoveEvent函数,则在该函数中发送自定义鼠标进入视图项的信号,如本类中的mouseEnterItemSigal信号
*/
//void CLogTableView::mouseMoveEvent(QMouseEvent *event)
//{
// auto pos = event->pos();
// auto modelIndex = this->indexAt(pos);
// // emit mouseEnterItemSigal(modelIndex);
// //update();
//}
logTableModel.h:
#ifndef LOGTABLEMODEL_H
#define LOGTABLEMODEL_H#include <QAbstractTableModel>class CLogTableModel : public QAbstractTableModel
{Q_OBJECTpublic:explicit CLogTableModel(QObject *parent = nullptr);public:/*功能:向外层发送日志信息*参数1:日志具体内容*参数2:日志来源的模块,如:设备管理模块*参数3:日志级别*/void addLog(const QString&qsLog, const QString&qsLogSrcModule, int nLogEvel);private:// Basic functionality:int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
private:QVariant doDecorationRole(int nLogEvel) const;QVariant doForegroundRole(int nLogEvel) const;private:QStringList m_lstLogInfo;};#endif // LOGTABLEMODEL_H
logTableModel.cpp:
#include "logTableModel.h"
#include<QIcon>
#include<QBrush>CLogTableModel::CLogTableModel(QObject *parent): QAbstractTableModel(parent)
{}int CLogTableModel::rowCount(const QModelIndex &parent) const
{if (parent.isValid())return 0;return static_cast<int>(m_lstLogInfo.size() / 3);
}int CLogTableModel::columnCount(const QModelIndex &parent) const
{if (parent.isValid())return 0;return 1;
}QVariant CLogTableModel::data(const QModelIndex &index, int role) const
{if (!index.isValid())return QVariant();auto rowIndex = index.row();auto rowCount = m_lstLogInfo.size() / 3;if((rowIndex < 0) || ( rowCount <= rowIndex)){Q_ASSERT(0);return QVariant();}// 以下这么做的目的是为了让最近产生的日志排在最顶部,最久产生的日志排在最底部auto logIndex = 3 * rowIndex;auto qsLog = m_lstLogInfo[logIndex];auto qsLogSrcModule = m_lstLogInfo[logIndex + 1];auto nLogEvel = m_lstLogInfo[logIndex + 2].toInt();auto columnIndex = index.column();switch (role){case Qt::DisplayRole:{switch (columnIndex){case 0:{return qsLog;}}}break;case Qt::DecorationRole:{return doDecorationRole(nLogEvel);}break;case Qt::ForegroundRole:{return doForegroundRole(nLogEvel);}break;} // end switch rolereturn QVariant();
}Qt::ItemFlags CLogTableModel::flags(const QModelIndex &index) const
{Q_UNUSED(index);return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}QVariant CLogTableModel::doForegroundRole(int nLogEvel) const
{switch (nLogEvel){case 1:{return QBrush(Qt::yellow);}break;case 2case 3:{return QBrush(Qt::red);}break;default:{}break;}return QVariant();
}QVariant CLogTableModel::doDecorationRole(int nLogEvel) const
{QIcon icon;switch (nLogEvel){case 0:{icon.addFile(QLatin1String(":/logWnd/image/log/normal.png"));return icon;}break;case 1:{icon.addFile(QLatin1String(":/logWnd/image/log/warn.png"));return icon;}break;case 2:case 3:{icon.addFile(QLatin1String(":/logWnd/image/log/error.png"));return icon;}break;default:{return icon;}break;}return QVariant();
}void CLogTableModel::addLog(const QString&qsLog, const QString&qsLogSrcModule, int nLogEvel)
{beginResetModel();if(m_lstLogInfo.size() >= 50){m_lstLogInfo.clear();}auto qsLogEvel = QString::number(nLogEvel);m_lstLogInfo.insert(0, qsLog);m_lstLogInfo.insert(1, qsLogSrcModule);m_lstLogInfo.insert(2, qsLogEvel);endResetModel();
}
logItemDelegate.h:
#ifndef CLOGITEMDELEGATE_H
#define CLOGITEMDELEGATE_H#include <QStyledItemDelegate>class CLogItemDelegate : public QStyledItemDelegate
{Q_OBJECTpublic:explicit CLogItemDelegate(QObject *parent = nullptr);public:void enterItem(const QModelIndex &index);
private:void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const override;private:QModelIndex m_curIndex;
};#endif // CLOGITEMDELEGATE_H
logItemDelegate.cpp:
#include "logItemDelegate.h"
#include<QPainter>CLogItemDelegate::CLogItemDelegate(QObject *parent): QStyledItemDelegate(parent)
{
}void CLogItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{if (m_curIndex.row() == index.row()){// 可以把第2个参数换成你想要的任何颜色painter->fillRect(option.rect, option.palette.highlight());}QStyledItemDelegate::paint(painter, option, index);
}void CLogItemDelegate::enterItem(const QModelIndex &index)
{if(!index.isValid()){return;}m_curIndex = index;
}
调用方代码如下:
#include "logWnd.h"
#include "ui_logWnd.h"
#include<QPainter>
#include "logTableModel.h"
#include"logItemDelegate.h"CLogWnd::CLogWnd(QWidget *parent) : QWidget(parent),ui(new Ui::CLogWnd)
{ ui->setupUi(this);this->resize(650, 1200);this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Window);// 创建模型类m_pLogTableModel = new CLogTableModel(this);ui->logTv->setModel(m_pLogTableModel);// 创建委托类auto pLogItemDelegate = new CLogItemDelegate(this);ui->logTv->setItemDelegate(pLogItemDelegate);ui->logTv->horizontalHeader()->hide();ui->logTv->verticalHeader()->hide();ui->logTv->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);// 连接entered信号connect(ui->logTv, &CLogTableView::entered, pLogItemDelegate, &CLogItemDelegate::enterItem);// connect(ui->logTv, &QAbstractItemView::mouseEnterItemSigal, pLogItemDelegate, &CLogItemDelegate::enterItem);}
3.2. 功能讲解
功能实现说明如下:
- CLogTableView类中将鼠标跟踪属性设置为true,否则不会捕捉QAbstractItemView类的entered信号。
- 在槽函数CLogItemDelegate类的enterItem记录鼠标光标所在项目的模型索引。
- 在委托类CLogItemDelegate中的paint函数中检测发现如果是鼠标光标所在项目的模型索引,则改变该项的背景色,否则就采用默认背景色。
- 不能重载mouseMoveEvent函数,否则QAbstractItemView::entered信号不会触发,如果非要重载mouseMoveEvent函数,则在该函数中发送自定义鼠标进入视图项的信号,如本类中的mouseEnterItemSigal信号。
4. 附录
现实中,经常发现QAbstractItemView类的entered信号不会激发,解决方法,参见:
QAbstractItemView类如:QTreeView、QTableView、QTableWidget不发送entered信号的问题解决