QTableView的单元格中嵌入多个可点击的超链接的例子

关键代码:

#ifndef STUDENT_H
#define STUDENT_H

#include <QString>

class Student
{
public:
    Student();
    void setId(int id);
    int id();
    void setName(const QString &name);
    QString name();

private:
    int m_id;
    QString m_name;
};

#endif // STUDENT_H
#include "student.h"

Student::Student()
{
    m_id=0;
    m_name="";
}

void Student::setId(int id)
{
    this->m_id=id;
}

int Student::id()
{
    return this->m_id;
}

void Student::setName(const QString &name)
{
    this->m_name=name;
}

QString Student::name()
{
    return this->m_name;
}
#ifndef STUDENTTABLEVIEWMODEL_H
#define STUDENTTABLEVIEWMODEL_H

#include <QAbstractTableModel>
#include <QList>
#include <QMap>

class Student;

class StudentTableViewModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    StudentTableViewModel(QObject *parent=0);

    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;

    void add();
    void remove(int id);
    Student *get(int id);

private:
    QList<Student*> m_studentList;
    QMap<int, Student*> m_studentMap;
    int m_testNumber;

    void updateModel();

};

#endif // STUDENTTABLEVIEWMODEL_H
#include "studenttableviewmodel.h"
#include "student.h"

StudentTableViewModel::StudentTableViewModel(QObject *parent)
    : QAbstractTableModel(parent)
{
    m_testNumber=10000;
}

int StudentTableViewModel::rowCount(const QModelIndex &parent) const
{
     return m_studentList.size();
}

int StudentTableViewModel::columnCount(const QModelIndex &parent) const
{
    return 3;
}

QVariant StudentTableViewModel::data(const QModelIndex &index, int role) const
{
    Student* student=m_studentList.at(index.row());
    switch (role) {
    case Qt::DisplayRole:
        if(index.column()==0)
        {
            return student->id();
        }
        else if(index.column()==1)
        {
            return student->name();
        }
        else if(index.column()==2)
        {
            return QString("<a href=\"about://add\">Add</a>&nbsp;<a href=\"about://remove?%1\">Remove</a>&nbsp;<a href=\"about://alert?%1\">Alert</a>").arg(student->id());
        }
        break;
    case Qt::UserRole:
        return student->id();
        break;
    default:
        break;
    }
    return QVariant();
}

QVariant StudentTableViewModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role==Qt::DisplayRole && orientation==Qt::Horizontal)
    {
        if(section==0)
        {
            return "ID";
        }
        else if(section==1)
        {
            return "Name";
        }
        else if(section==2)
        {
            return "Operation";
        }
    }
    return QAbstractTableModel::headerData(section, orientation, role);
}

Qt::ItemFlags StudentTableViewModel::flags(const QModelIndex &index) const
{
    return QAbstractTableModel::flags(index);
}

void StudentTableViewModel::add()
{
    Student *student=new Student;
    student->setId(m_testNumber);
    student->setName(QString("Name%1").arg(m_testNumber));
    m_studentMap.insert(m_testNumber, student);
    updateModel();
    m_testNumber++;
}

void StudentTableViewModel::remove(int id)
{
    Student *student=m_studentMap.value(id);
    if(student)
    {
        delete student;
    }
    m_studentMap.remove(id);
    updateModel();
}

Student *StudentTableViewModel::get(int id)
{
    return m_studentMap.value(id);
}

void StudentTableViewModel::updateModel()
{
    m_studentList.clear();
    QList<int> keys=m_studentMap.keys();
    qSort(keys.begin(), keys.end());
    for(int i=0; i<keys.size(); i++)
    {
        m_studentList.push_back(m_studentMap.value(keys.at(i)));
    }
    emit layoutChanged();
}
#ifndef LINKABLEITEMDELEGATE_H
#define LINKABLEITEMDELEGATE_H

#include <QStyledItemDelegate>

class LinkableItemDelegate : public QStyledItemDelegate {
    Q_OBJECT

public:
    explicit LinkableItemDelegate(QObject *parent = 0);

    QString anchorAt(QString html, const QPoint &point) const;

protected:
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
};

#endif // LINKABLEITEMDELEGATE_H
#include "linkableitemdelegate.h"

#include <QPainter>
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include <QIcon>
#include <QSize>

LinkableItemDelegate::LinkableItemDelegate(QObject *parent) :
    QStyledItemDelegate(parent)
{

}

QString LinkableItemDelegate::anchorAt(QString html, const QPoint &point) const
{
    QTextDocument doc;
    doc.setHtml(html);

    auto textLayout = doc.documentLayout();
    Q_ASSERT(textLayout != 0);
    return textLayout->anchorAt(point);
}

void LinkableItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    /********* 一种简单的实现 ***********
    auto options = option;
    initStyleOption(&options, index);

    painter->save();

    QTextDocument doc;
    doc.setHtml(options.text);

    options.text = "";
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &option, painter);

    painter->translate(options.rect.left(), options.rect.top());
    QRect clip(0, 0, options.rect.width(), options.rect.height());
    doc.drawContents(painter, clip);

    painter->restore();
    ***********************************/
    QStyleOptionViewItemV4 options = option;
    initStyleOption(&options, index);

    painter->save();

    QTextDocument doc;
    doc.setHtml(options.text);

    options.text = "";
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);

    QSize iconSize = options.icon.actualSize(options.rect.size());
    // right shit the icon
    painter->translate(options.rect.left() + iconSize.width(), options.rect.top());
    QRect clip(0, 0, options.rect.width() + iconSize.width(), options.rect.height());

    painter->setClipRect(clip);
    QAbstractTextDocumentLayout::PaintContext ctx;

    // Adjust color palette if the cell is selected
    if (option.state & QStyle::State_Selected)
        ctx.palette.setColor(QPalette::Text, option.palette.color(QPalette::Active, QPalette::HighlightedText));
    ctx.clip = clip;

    // Vertical Center alignment instead of the default top alignment
    painter->translate(0, 0.5*(options.rect.height() - doc.size().height()));

    doc.documentLayout()->draw(painter, ctx);
    painter->restore();
}

QSize LinkableItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItemV4 options = option;
    initStyleOption(&options, index);

    QTextDocument doc;
    doc.setHtml(options.text);
    doc.setTextWidth(options.rect.width());
    return QSize(doc.idealWidth(), doc.size().height());
}

#ifndef LINKABLEVIEW_H
#define LINKABLEVIEW_H

#include <QTableView>

class LinkableTableView : public QTableView
{
    Q_OBJECT

public:
    explicit LinkableTableView(QWidget *parent = 0);

signals:
    void linkActivated(QString link);
    void linkHovered(QString link);
    void linkUnhovered();

protected:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

private:
    QString anchorAt(const QPoint &pos) const;

private:
    QString _mousePressAnchor;
    QString _lastHoveredAnchor;
};

#endif // LINKABLEVIEW_H
#include "linkabletableview.h"

#include <QApplication>
#include <QCursor>
#include <QMouseEvent>
#include "linkableitemdelegate.h"

LinkableTableView::LinkableTableView(QWidget *parent) :
    QTableView(parent)
{
    // needed for the hover functionality
    setMouseTracking(true);
}

void LinkableTableView::mousePressEvent(QMouseEvent *event) {
    QTableView::mousePressEvent(event);

    auto anchor = anchorAt(event->pos());
    _mousePressAnchor = anchor;
}

void LinkableTableView::mouseMoveEvent(QMouseEvent *event) {
    auto anchor = anchorAt(event->pos());

    if (_mousePressAnchor != anchor) {
        _mousePressAnchor.clear();
    }

    if (_lastHoveredAnchor != anchor) {
        _lastHoveredAnchor = anchor;
        if (!_lastHoveredAnchor.isEmpty()) {
            QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
            emit linkHovered(_lastHoveredAnchor);
        } else {
            QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
            emit linkUnhovered();
        }
    }
}

void LinkableTableView::mouseReleaseEvent(QMouseEvent *event) {
    if (!_mousePressAnchor.isEmpty()) {
        auto anchor = anchorAt(event->pos());

        if (anchor == _mousePressAnchor) {
            emit linkActivated(_mousePressAnchor);
        }

        _mousePressAnchor.clear();
    }

    QTableView::mouseReleaseEvent(event);
}

QString LinkableTableView::anchorAt(const QPoint &pos) const {
    auto index = indexAt(pos);
    if (index.isValid()) {
        auto delegate = itemDelegate(index);
        auto linkableDelegate = qobject_cast<LinkableItemDelegate *>(delegate);
        if (linkableDelegate != 0) {
            auto itemRect = visualRect(index);
            auto relativeClickPosition = pos - itemRect.topLeft();

            auto html = model()->data(index, Qt::DisplayRole).toString();

            return linkableDelegate->anchorAt(html, relativeClickPosition);
        }
    }

    return QString();
}
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>

class StudentTableViewModel;
class LinkableItemDelegate;
class LinkableTableView;

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = 0);
    ~MainWidget();

private slots:
    void doAdd();
    void doLinkActivated(QString link);

private:
    StudentTableViewModel *m_studentTableViewModel;
    LinkableItemDelegate *m_linkableItemDelegate;
    LinkableTableView *m_linkableTableView;

};

#endif // MAINWIDGET_H
#include "mainwidget.h"

#include <QPushButton>
#include <QVBoxLayout>

#include "linkableitemdelegate.h"
#include "linkabletableview.h"
#include "studenttableviewmodel.h"
#include <QMessageBox>
#include <QHeaderView>
#include "student.h"

MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent)
{
    m_linkableTableView=new LinkableTableView;
    m_linkableTableView->setSelectionMode(QTableView::NoSelection);
    m_linkableTableView->horizontalHeader()->setStretchLastSection(true);
    QVBoxLayout *vLayout=new QVBoxLayout;
    this->setLayout(vLayout);
    vLayout->addWidget(m_linkableTableView);

    QPushButton *buttonAdd=new QPushButton;
    buttonAdd->setText("Add");
    connect(buttonAdd, SIGNAL(clicked(bool)), this, SLOT(doAdd()));
    vLayout->addWidget(buttonAdd);

    m_studentTableViewModel=new StudentTableViewModel(this);
    m_linkableTableView->setModel(m_studentTableViewModel);

    m_linkableItemDelegate=new LinkableItemDelegate(this);
    m_linkableTableView->setItemDelegate(m_linkableItemDelegate);
    connect(m_linkableTableView, SIGNAL(linkActivated(QString)), this, SLOT(doLinkActivated(QString)));
}

MainWidget::~MainWidget()
{

}

void MainWidget::doAdd()
{
    m_studentTableViewModel->add();
}

void MainWidget::doLinkActivated(QString link)
{
    if(link.contains("add"))
    {
        m_studentTableViewModel->add();
    }
    else if(link.contains("remove"))
    {
        int id=link.mid(link.indexOf("?")+1).toInt();
        m_studentTableViewModel->remove(id);
    }
    else if(link.contains("alert"))
    {
        int id=link.mid(link.indexOf("?")+1).toInt();
        Student *student=m_studentTableViewModel->get(id);
        if(student)
        {
            QMessageBox::about(this, "Alert", QString("id:%1, name:%2").arg(student->id()).arg(student->name()));
        }
    }
}

运行效果:

参考资料:

https://stackoverflow.com/questions/35397943/how-to-make-a-fast-qtableview-with-html-formatted-and-clickable-cells

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值