Qt编写自定义控件:可交互的列表控件

cpp 复制代码
#ifndef LISTWIDGET_H
#define LISTWIDGET_H

#include <QWidget>

struct ListItem
{
    QString text;
    QRect rect;
    QString icon{":/setting.png"};
};

class ListWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ListWidget(QWidget *parent = nullptr);
    void setSize(int newSize);

protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;

private:
    std::shared_ptr<struct ListWidgetPrivate> d_ptr;
};

#endif // LISTWIDGET_H
cpp 复制代码
#include "listwidget.h"
#include <QPainter>
#include <QDebug>
#include <QMouseEvent>
#include <QDateTime>

struct ListWidgetPrivate
{
    void setSize(int newSize);
    void onMousePressed(const QPoint & pos);
    void onMouseReleased();
    void onMouseMoved(const QPoint & pos);
    void updateRect();
    void drawRect(QPainter * painter);
    ~ListWidgetPrivate();

    QPoint pressPos;
    QList<ListItem*> itemList;
    bool isPressed{false};
    int startHeight{0};
    int offsetY{0};
    int pressIndex{-1};
    int itemHeight = 50;
    int tempOffsetY{0};
    int gridLineWidth{3};
    qint64 preessTime{0};
    ListWidget * q_ptr;
    QColor backgroundColor{"#ffffffff"};
    QColor itemPressedBackgroundColor{"#f0121212"};
    QColor itemBackgroundColor{"#f0555555"};
    QColor gridColor{"#f8111111"};
    QColor textColor{"#ffffffff"};
};

void ListWidgetPrivate::setSize(int newSize)
{
    auto oldSize = itemList.size();
    if(newSize != oldSize)
    {
        if(newSize > oldSize)
        {
            for (int i = 0;i < (newSize - oldSize);++i)
            {
                auto item = new ListItem;
                item->text = "项目" + QString::number(oldSize + i + 1);
                itemList << item;
            }
        }
        else
        {
            for(int i = oldSize - 1;i != (oldSize - 1);--i)
            {
                auto item = itemList.last();
                itemList.removeLast();
                delete item;
            }
        }

        updateRect();
    }
}

void ListWidgetPrivate::updateRect()
{
    auto width = q_ptr->rect().width() + gridLineWidth * 2;
    for(int i = 0,height = 0;i < itemList.size();++i, height += itemHeight)
    {
        itemList[i]->rect = QRect(-gridLineWidth, height, width, itemHeight);
    }
}

void ListWidgetPrivate::drawRect(QPainter *painter)
{
    painter->fillRect(q_ptr->rect(),backgroundColor);
    painter->setPen(QPen(gridColor, gridLineWidth));
    painter->setBrush(itemBackgroundColor);

    for (int i = 0; i < itemList.size(); ++i)
    {
        const auto & item = itemList.at(i);
        auto itemRect = item->rect;
        itemRect.moveBottom(itemRect.bottom() - offsetY);

        if (Q_UNLIKELY(isPressed && i == pressIndex))
        {
            painter->save();
            painter->setBrush(itemPressedBackgroundColor);
            painter->drawRect(itemRect);
            painter->restore();
        }
        else
        {
            painter->drawRect(itemRect);
        }

        painter->save();
        painter->setPen(QPen(textColor));
        painter->drawText(itemRect, Qt::AlignCenter, item->text);
        painter->restore();

        painter->drawPixmap(QRect(itemRect.topLeft() + QPoint(itemHeight / 4,0),QSize(itemHeight,itemHeight)).adjusted(4,4,-4,-4),
                            QPixmap(item->icon));

        painter->save();
        painter->setPen(QPen(QColor("#C0C0C0"),2,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
        QRect symbolRect(itemRect.topRight() - QPoint(itemHeight * 1.5,0),QSize(itemHeight* 1.5,itemHeight));
        auto symbolRectCenter = symbolRect.center();
        painter->drawLine(symbolRect.topLeft() + QPoint(itemHeight / 2.5,itemHeight / 3.5),symbolRectCenter);
        painter->drawLine(symbolRect.bottomLeft() + QPoint(itemHeight / 2.5,-itemHeight / 3.5),symbolRectCenter);
        painter->restore();
    }
}

ListWidgetPrivate::~ListWidgetPrivate()
{
    qDeleteAll(itemList);
}

void ListWidgetPrivate::onMousePressed(const QPoint &pos)
{
    pressPos = pos;
    tempOffsetY = offsetY;
    isPressed = true;
    pressIndex = -1;
    preessTime = QDateTime::currentMSecsSinceEpoch();
    for (int i = 0; i < itemList.size(); ++i)
    {
        auto itemRect = itemList.at(i)->rect;
        itemRect.moveBottom(itemRect.bottom() - offsetY);
        if (itemRect.contains(pressPos))
        {
            pressIndex = i;
            break;
        }
    }
    q_ptr->update();
}

void ListWidgetPrivate::onMouseReleased()
{
    if (isPressed)
    {
        isPressed = false;
        if (offsetY < 0)
            offsetY = 0;

        auto lastItemRect = itemList.last()->rect;
        lastItemRect.moveBottom(lastItemRect.bottom() - offsetY);

        if (lastItemRect.bottom() < 0 || lastItemRect.bottom() < itemHeight)
        {
            offsetY = (itemList.size() - 1) * itemHeight;
        }

        auto wRect = q_ptr->rect();
        if(lastItemRect.bottom() < (wRect.y() + wRect.height()))
        {
            int itemAllHeight = itemList.size() * this->itemHeight;
            if(itemAllHeight <= wRect.height()) //项目总高度不如部件高度大
            {
                offsetY = 0;
            }
            else
            {
                offsetY = itemAllHeight - wRect.height();
            }
        }

        if(pressIndex != -1)
        {
            qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
            if ((currentTime - preessTime) < 1000)//从按下到松开超过1秒就不认为是按下项目操作
            {
                qDebug()<<itemList.at(pressIndex)->text;
                preessTime = currentTime;
            }

            pressIndex = -1;
        }
        q_ptr->update();
    }
}

void ListWidgetPrivate::onMouseMoved(const QPoint &pos)
{
    if (isPressed)
    {
        auto diff = pressPos - pos;
        offsetY = tempOffsetY + diff.y();
        q_ptr->update();
    }
}

ListWidget::ListWidget(QWidget *parent)
    : QWidget{parent}
{
    d_ptr = std::make_shared<ListWidgetPrivate>();
    d_ptr->q_ptr = this;

    for(int i = 1;i < 10;++i)
    {
        auto item = new ListItem;
        item->text = "项目" + QString::number(i);
        d_ptr->itemList << item;
    }
}

void ListWidget::setSize(int newSize)
{
    d_ptr->setSize(newSize);
}

void ListWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    d_ptr->drawRect(&painter);
}

void ListWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        d_ptr->onMousePressed(event->pos());
    }
}

void ListWidget::mouseReleaseEvent(QMouseEvent *event)
{
    d_ptr->onMouseReleased();
}

void ListWidget::mouseMoveEvent(QMouseEvent *event)
{
    d_ptr->onMouseMoved(event->pos());
}

void ListWidget::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);
    d_ptr->updateRect();
    update();
}
相关推荐
mahuifa3 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai
冷眼看人间恩怨3 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
云空8 小时前
《QT 5.14.1 搭建 opencv 环境全攻略》
开发语言·qt·opencv
小老鼠不吃猫9 小时前
力学笃行(二)Qt 示例程序运行
开发语言·qt
晓纪同学11 小时前
QT创建一个模板槽和信号刷新UI
开发语言·qt·ui
爱码小白12 小时前
PyQt5 学习方法之悟道
开发语言·qt·学习方法
人才程序员1 天前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
学习BigData1 天前
【使用PyQt5和YOLOv11开发电脑屏幕区域的实时分类GUI】——选择检测区域
qt·yolo·分类
yerennuo1 天前
FFmpeg库之ffmpeg
qt·ffmpeg
冷眼看人间恩怨1 天前
【Qt笔记】QComboBox控件详解
c++·笔记·qt