鼠标在QTreeView、QTableView、QTableWidget项上移动,背景色改变

目录

[1. 前言](#1. 前言)

[2. 需求](#2. 需求)

[3. 功能实现](#3. 功能实现)

[3.1. 代码实现](#3.1. 代码实现)

[3.2. 功能讲解](#3.2. 功能讲解)

[4. 附录](#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:

cpp 复制代码
#ifndef LOG_TABLE_VIEW_H
#define LOG_TABLE_VIEW_H

#include <QTableView>


class CLogTableView : public QTableView
{
    Q_OBJECT

public:
    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:

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:
cpp 复制代码
#ifndef LOGTABLEMODEL_H
#define LOGTABLEMODEL_H

#include <QAbstractTableModel>

class CLogTableModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    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:
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 role

    return 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 2
        case 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:
cpp 复制代码
#ifndef CLOGITEMDELEGATE_H
#define CLOGITEMDELEGATE_H

#include <QStyledItemDelegate>

class CLogItemDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    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:

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;
}

调用方代码如下:

cpp 复制代码
#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);

}

void CLogWnd::addLog(const QString&qsLog, const QString&qsLogSrcModule, int nLogEvel)
{
    m_pLogTableModel->addLog(qsLog, qsLogSrcModule, nLogEvel);
}

调用:

cpp 复制代码
auto pLogWnd = new CLogWnd(this);
pLogWnd->addLog("a", "a1", 0);
pLogWnd->addLog("b", "b1", 2);
pLogWnd->addLog("c", "c1", 2;

3.2. 功能讲解

功能实现说明如下:

  • CLogTableView类中将鼠标跟踪属性设置为true,否则不会捕捉QAbstractItemView类的entered信号。
  • 在槽函数CLogItemDelegate类的enterItem记录鼠标光标所在项目的模型索引。
  • 在委托类CLogItemDelegate中的paint函数中检测发现如果是鼠标光标所在项目的模型索引,则改变该项的背景色,否则就采用默认背景色。
  • 不能重载mouseMoveEvent函数,否则QAbstractItemView::entered信号不会触发,如果非要重载mouseMoveEvent函数,则在该函数中发送自定义鼠标进入视图项的信号,如本类中的mouseEnterItemSigal信号。

4. 附录

现实中,经常发现QAbstractItemView类的entered信号不会激发,解决方法,参见:

QAbstractItemView类如:QTreeView、QTableView、QTableWidget不发送entered信号的问题解决