Qt关于TableView和TreeView的一些代理绘制心得

代理delegate,我的理解就是在table或tree中,通过代码的方式,绘制上自定义的控件,本篇文章主要就讲了如何代理、具体怎么绘制等操作(以下仅为个人心得,若有错误请指正)

代理的开始

首先,我们要建立一个tableView或者treeView,以下统称为表格table,二者相差不多。

如果对表头没有要求,我们可以在代理绘制前,先将表格的内容填好,如果有要求,可翻到文章最后一部分,表头的绘制。

以下是一个示例;

void BugReportTest::initTable() {

    auto tableviewModel = new QStandardItemModel;    //view类都需要model进行操作
    ui.tableView->setModel(tableviewModel);
   
    QStringList headerList;                        //设置表头
    headerList << "1" << "2" << "3" << "4" << "5";
    tableviewModel->setColumnCount(5);            //列数
    tableviewModel->setHorizontalHeaderLabels(headerList);

//设置列宽和行高
    ui.tableView->horizontalHeader()->setMinimumHeight(53);
    ui.tableView->verticalHeader()->setDefaultSectionSize(53);

    //设置每个列的宽度
    ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
    ui.tableView->horizontalHeader()->setSectionResizeMode(3 ,QHeaderView::Stretch);//Stretch是随内容扩展
    ui.tableView->horizontalHeader()->setSectionResizeMode(1 ,QHeaderView::Stretch);
    ui.tableView->setColumnWidth(0 , 110);
    ui.tableView->setColumnWidth(1 , 300);
    ui.tableView->setColumnWidth(2 , 183);
    ui.tableView->setColumnWidth(3 , 256);
    ui.tableView->setColumnWidth(4 , 305);

//以下是插入数据
    auto time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    for (int i = 0; i < 4; i++) {
        tableviewModel->insertRow(i);
        tableviewModel->setData(tableviewModel->index(i,0),i+1);
        tableviewModel->setData(tableviewModel->index(i,1),QString("EXCEPTION_ACCESS_VIOLATION_WRITE"));
        tableviewModel->setData(tableviewModel->index(i,2),QString("V%1.0").arg(i));
        tableviewModel->setData(tableviewModel->index(i,3),time);
        tableviewModel->setData(tableviewModel->index(i,4),time);
    }
}

这样一个朴实无华的表格就建立好了,而想要代理,我们就需要创建一个类,代理类(头文件缺啥加啥):

class ProjectDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    explicit ProjectDelegate(int type , QObject *parent = nullptr);
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;                 //绘制单元格内容如文本
    bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;   //响应按钮事件

};

一般网上说的是可以重写五个函数,我这只重写了用的到的,其余可以看情况加,自行搜索。

创建完代理类,怎么调用,这就需要下列语句:

ui.tableView->setItemDelegate(delegate = new ProjectDelegate(1 , ui.tableView));  //添加代理

语句还可以根据第几列或者第几行去加,不过除特殊情况外,我推荐在代理类里面增加判断语句就行,这边甩个截图:

至此,我们已经完成最主要的部分了,接下里就是如何绘制。

代理的绘制

如果你有仔细看上面的内容,你肯定会知道绘制函数在哪,没错,就是我们重写的paint函数,所以我们接下来的操作就在paint函数内进行。

首先是最简单的文本绘制:

文本绘制
void ProjectDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

    int rowIndex = index.row();   //行号
    int colIndex = index.column();//列号
    if (colIndex == 0 || colIndex == 4 || colIndex == 5 || colIndex == 6) {    //筛选条件
        painter->setPen(0x565757);//画笔颜色
        QTextOption op;        //文本格式在这设置
        if (colIndex == 0) {
            op.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
        } else {
            op.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
        }
        QFont font;            //字体属性在这
        font.setPixelSize(14);
        painter->setFont(font);
        painter->drawText(option.rect, index.data(Qt::DisplayRole).toString(), op);    //这就是绘制的函数了,中间的代表绘制表格中原本的文字
    }

}

下来是qLabel,注意,因为我是节选的我的代码,筛选方式会有冲突,切勿直接cv。

QLabel绘制
QLabel label;
label.setText(index.data(Qt::DisplayRole).toString());
label.setStyleSheet("border-radius: 2px 2px 2px 2px; background: #52c41a; color: #ffffff; padding: 1px 8px; font-size: 14px;");
int labelWidth = 46;
int labelHeight = 24;
int x = option.rect.x() + (option.rect.width() - labelWidth) / 2; // 计算水平居中位置
int y = option.rect.y() + (option.rect.height() - labelHeight) / 2; // 计算垂直居中位置
label.setGeometry(x, y, labelWidth, labelHeight);
painter->save();
painter->translate(x, y);    //绘制起始位置
label.render(painter);       //通过调用render方法,可以将label对象的内容绘制到painter所关联的绘图设备上,从而实现在单元格中显示QLabel的文本内容

我在网上没有看到具体的QLabel绘制,可能用的比较少,因此我写的也比较片面,欢迎补充。

QPixmap绘制
const QPixmap star = QPixmap(":/res/delete.png");    //图标路径
auto width = star.width();
auto height = star.height();
auto rect = option.rect;    //表格的当前区域
int x = rect.x() + rect.width() / 2 - width / 2;    //起始绘制坐标,也就是左上角
int y = rect.y() + rect.height() / 2 - height / 2;
painter->drawPixmap(x, y, star);

貌似QImage绘制方式跟这个差不多?我下面就不赘述了,大家可以自己尝试。

QButton、QCheckBox等绘制

我具体没有使用,但是可以参考这位大佬的:

http://t.csdnimg.cn/TsBUa

虽然他的没有注释,但我相信如果您看了上面的内容,理解大佬的代码不是问题。

表头的绘制

话不多说,表头也需要建立一个表头类

class MyView : public QHeaderView
{
    Q_OBJECT
public:
    // 第一个参数设定表头方向
    MyView(Qt::Orientation orientation,
                 QWidget * parent = nullptr) : QHeaderView(orientation, parent)
    {
        setSectionsClickable(true);
    }
protected:
    void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override;

};

很明显,又是对函数的重写,那我们直接跳转过去:

void MyHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const {

    painter->save();
    QHeaderView::paintSection(painter, rect ,logicalIndex); //paintSection,是对每一段进行一个绘制,logicalIndex == 0是代表是表头的第一列
    painter->restore();

    if(logicalIndex == 0){
        QStyleOptionHeader op;
        initStyleOption(&op);
        op.textAlignment = Qt::AlignCenter;
        op.rect = rect;
        op.text = "#";
        painter->setPen(0x252525);
        QFont font;
        font.setPixelSize(14);
        painter->setFont(font);
        style()->drawControl(QStyle::CE_HeaderLabel,&op,painter);
    }
}

可以看到跟tableView并无太大区别,甚至参数都十分相似,那么同理,其它方式也如tableView一般绘制。

哦对了,差点完了,在这使用表头

auto tableviewModel = new QStandardItemModel;
ui.tableView->setModel(tableviewModel);
//表头引用
auto *headerView = new MyView(Qt::Horizontal , ui.tableView);
ui.tableView->setHorizontalHeader(headerView);

我只是个编程新手,如果有错,请务必指导我一下,求饶求饶!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值