Qt6开发跨平台音乐播放器(九):QListWidget两种扩展方法

1. 概述

在展示小批量数据方面,QListWidget是一个非常实用的部件,但默认的QListWidget只能展示文本,功能比较局限。在很多情况下,我们需要在列表中使用其它控件来对数据进行操作,比如复选框和菜单等,这时就需要扩展QListWidget了,这一节我们来聊聊两种扩展QListWidget的方法:

  • 使用delegate扩展

  • 使用setItemWidget扩展

作为例子,我们使用两种方法各自实现一个带下拉菜单的QListWidget

2. delegate

delegate是用于自定义模型/视图架构(list/table/tree等)中数据项的呈现和编辑方式的工具,它具有以下作用:

  • 自定义外观:可以完全控制数据项在视图中的外观,包括自定义字体、颜色、背景、图标等各种视觉元素

  • 自定义编辑功能:允许创建自定义的编辑组件,以替代默认的编辑方式

  • 优化交互

    • 可以响应各种用户交互事件,如鼠标点击、悬停、键盘输入等,并根据这些事件执行特定的操作。

    • 可以优化数据项的选择和导航行为。例如,可以实现特殊的选择效果,或者在特定情况下自动滚动视图以确保用户关注的数据项可见。

而delegate的使用有固定的步骤:

  • 继承delegate基类:QListWidget通常使用QStyledItemDelegate或QItemDelegate

  • 重写关键接口:比如paint接口用于绘制,editorEvent接口用于编辑响应

  • 设置delegate:这一步比较简单,就是将delegate设置到视图,使其生效

基于以上信息,我们创建一个SongItemDelegate类,头文件如下:

// songitemdelegate.h
#pragma once

#include <QStyledItemDelegate>

class SongItemDelegate : public QStyledItemDelegate {
    Q_OBJECT

public:
    explicit SongItemDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
    virtual ~SongItemDelegate() {}

protected:
    void paint(QPainter *painter, const QStyleOptionViewItem &option,
                       const QModelIndex &index) const override;
    QSize sizeHint(const QStyleOptionViewItem &option,
                           const QModelIndex &index) const override;
    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
                     const QModelIndex &index) override;

private:
    void showMenu(QAbstractItemModel *model, const QModelIndex &index);

private:
    int m_itemHeight = 32;
};

源文件如下:

#include "songitemdelegate.h"

#include <QAction>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>

void SongItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                             const QModelIndex &index) const {
    QRect rc = option.rect;
    
    // todo: draw other things
    // ...

    painter->drawText(rc.width() - 48, rc.top(), 48, rc.height(), Qt::AlignCenter, "...");
}

QSize SongItemDelegate::sizeHint(const QStyleOptionViewItem &option,
                                 const QModelIndex &index) const {
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    size.setHeight(m_itemHeight);
    return size;
}

bool SongItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
                                   const QStyleOptionViewItem &option, const QModelIndex &index) {
    QMouseEvent *ev = static_cast<QMouseEvent *>(event);
    if (QEvent::MouseButtonPress != ev->type()) {
        return QStyledItemDelegate::editorEvent(event, model, option, index);
    }

    QRect rc = option.rect;
    QRect menuRect(rc.width() - 48, rc.top(), 48, rc.height());
    if (menuRect.contains(ev->pos())) {
        showMenu(model, index);
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

void SongItemDelegate::showMenu(QAbstractItemModel *model, const QModelIndex &index) {
    QMenu *menu = new QMenu;

    // todo: add action
    // ...

    bool value = model->data(index, Qt::UserRole + 1).toBool();
    model->setData(index, !value, Qt::UserRole + 1);
    model->dataChanged(index, index);

    menu->popup(QCursor::pos());
}

delegate的优点是绘制速度快,缺点是难以应对复杂ui,所以简单的ui定制delegate是最佳选择。

3. setItemWidget

如果要在QListWidget中绘制复杂ui,比如有多列,每列有不同的部件,各个不同部件又有不同的响应,那使用setItemWidget就是比较好的选择了。

其使用步骤如下:

  • 继承QWidget,绘制自定义UI

  • QListWidget新增item

  • 在新增的item上调用setItemWidget

示例代码如下:

// customitem.h
#pragma once

class CustomItem : public QWidget {
    Q_OBJECT

    // custom code
    // ...
}

// use CustomItem
QListWidgetItem *bgItem = new QListWidgetItem;
ui->list->addItem(bgItem);

CustomItem *fgItem = new CustomItem;
ui->list->setItemWidget(bgItem, fgItem);

此处需要注意设置QListWidgetItem高度与CustomItem高度匹配,否则显示会错乱。

另外此种方法不适用大批量数据的展示,会有性能问题。


PS: 代码已经开源在github:linqiaqun/music-player: A cross platform music player (github.com) 欢迎star/fork/issue

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值