Qt重新绘制ComboBox


前言

Qt 重新绘制ComboBox,原因无他,没想到自己画的更丑。自定义控件看这篇,这里就直接画了
在这里插入图片描述


一、基类?

继承QComboBox,继承QWidget更好,相当于自己重新写个控件,这里主要是懒,想用现成的东西……

二、使用步骤

1.代码文件

代码如下(没啥好看的,瞎写的废话),完整项目压缩包放最后:

.h文件

#ifndef SELFCOMBOBOX_H
#define SELFCOMBOBOX_H

#include <QComboBox>

class SelfComboBox : public QComboBox
{
    
    
    Q_OBJECT

public:
    SelfComboBox(QWidget *parent = 0);
    ~SelfComboBox();

    void paintEvent(QPaintEvent *e) override;
    //覆盖
    void addItem(const QString &text, const QVariant &userData = QVariant());
    void insertItem(int index, const QString &text, const QVariant &userData = QVariant());

    //属性
    Q_PROPERTY(qreal itemHeight READ getItemHeight WRITE setItemHeight);
    Q_PROPERTY(QFont font READ getFont WRITE setFont);
    Q_PROPERTY(double viewSpace READ getViewSpace WRITE setViewSpace);

protected:
    void focusOutEvent(QFocusEvent *e) override;

public slots:
    void wheelEvent(QWheelEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void resizeEvent(QResizeEvent *e) override;
    void onTimerCallBack();
    void onViewTimerCallBack();

    double getItemHeight(){
    
    return itemHeight;}
    void setItemHeight(double iHeight);
    QFont getFont(){
    
    return font;}
    void setFont(QFont f);
    double getViewSpace(){
    
    return viewSpace;}
    void setViewSpace(double vs);

private:
    double radius = 1;
    double maxRadius = 255;
    double minRadius = 1;
    bool isExpand = false;
    double yOffect = 0;
    bool isViewOpen = false;
    int curOpenNum = 0;
    int maxNum = 4;
    int curDrawNum = 0;
    int curTopIndex = 0;
    int curClickNum = -1;
    int roundRadius = 8;
    qreal itemHeight = 0.0;
    int space = 2;
    QFont font = QFont("SimHei", 18);
    double viewSpace = 15.0;
//    QString comboStr = "Select";
    QRect *activePoints = nullptr;
    //数据存储
    QStringList allData;
    QVariantList allVData;

    QTimer *timer = nullptr;
    QTimer *viewTimer = nullptr;
    void drawRoundedView(QPainter *);
    void drawRoundedViewTitle(QPainter *);
    void onComboBtnClick();
    void onAnimChange(bool isExpand);
};

#endif // SELFCOMBOBOX_H

.cpp文件 挑重点发……

void SelfComboBox::paintEvent(QPaintEvent *e)
{
    
    
    QPainter painter(this);
    painter.setRenderHint(QPainter:: Antialiasing, true);
    this->drawRoundedViewTitle(&painter);
    //最主要的绘制地方就是这个啦
    this->drawRoundedView(&painter);
}

//重写
void SelfComboBox::addItem(const QString &text, const QVariant &userData)
{
    
    
    this->allData.push_back(text);
    this->allVData.push_back(userData);
    if(this->allData.length() == 1)
    {
    
    
        this->setCurrentIndex(0);
        this->setCurrentText(text);
    }
    //懒得维护count之类的……
    QComboBox::addItem(text, userData);
    update();
}

void SelfComboBox::focusOutEvent(QFocusEvent *e)
{
    
    
    if(this->viewTimer && this->viewTimer->isActive())
    {
    
    
        this->viewTimer->stop();
    }
    this->isViewOpen = false;
    this->viewTimer->start(10);
}
//下方区域滚动
void SelfComboBox::wheelEvent(QWheelEvent *event)
{
    
    
    this->yOffect += (event->delta() * 0.25);
    this->yOffect = this->yOffect <= 0.0 ? 0.0 : this->yOffect;
    update();
}

void SelfComboBox::mousePressEvent(QMouseEvent *event)
{
    
    
    if(this->viewTimer && this->viewTimer->isActive())
    {
    
    
        this->viewTimer->stop();
    }
    QRect detectArea(0, 0, width(), itemHeight);
    if(detectArea.contains(event->pos()))
    {
    
    
        this->isViewOpen = !this->isViewOpen;
        this->viewTimer->start(50);
    }
    //下方区域检测
    this->curClickNum = -1;
    if(event->button() == Qt::LeftButton)
    {
    
    
        for (int i = 0; i < this->curDrawNum; i++)
        {
    
    
            if(this->activePoints[i].contains(event->pos()))
            {
    
    
                this->curClickNum = i + this->curTopIndex;
                this->onAnimChange(true);
                break;
            }
        }
    }
}

void SelfComboBox::mouseReleaseEvent(QMouseEvent *event)
{
    
    
    if(this->curClickNum != -1)
    {
    
    
        this->setCurrentIndex(this->curClickNum);
        this->setCurrentText(this->allData[this->curClickNum]);

        this->focusOutEvent(nullptr);
    }
    this->onAnimChange(false);
}

void SelfComboBox::mouseMoveEvent(QMouseEvent *event)
{
    
    
    // 这里必须使用buttons()
    if(event->buttons() & Qt::LeftButton) //进行的按位与
    {
    
    
        this->curClickNum = -1;
        for (int i = 0; i < this->curDrawNum; i++)
        {
    
    
            if(this->activePoints[i].contains(event->pos()))
            {
    
    
                this->curClickNum = i + this->curTopIndex;
                update();
                break;
            }
        }
    }
}

void SelfComboBox::drawRoundedView(QPainter *painter)
{
    
    
    if(this->curOpenNum <= 0)
    {
    
    
        return;
    }
	//为啥这里逻辑那么奇怪(主要是我先在这里面写完,再把变量提出去的……)
    int maxNum = this->curOpenNum;
    int drawNum = maxNum;
    //数据条
    int dataNum = this->count();//this->count();
    //第一个起点
    //Y偏移量
    int bgOffect = 2;
    double beginPosX = 2, beginPosY = itemHeight + this->viewSpace, itemWidth = width() - bgOffect * 2;
    double bgBeginPosY = beginPosY;
    if(dataNum <= maxNum)
    {
    
    
        maxNum = dataNum;
        yOffect = 0;
    }
    curTopIndex = floor(yOffect * 1.0 / (itemHeight + space));
    int curBottomIndex = curTopIndex + maxNum;
    double topLeftNum = (yOffect * 1.0 / (itemHeight + space)) - curTopIndex;
    curTopIndex = curTopIndex + maxNum >= dataNum ? dataNum - maxNum: curTopIndex;
    topLeftNum = curTopIndex + maxNum >= dataNum ? 0.0: topLeftNum;
    curBottomIndex = curTopIndex + maxNum;
    if(topLeftNum > 0.0)
    {
    
    
        curBottomIndex += 1;
        drawNum += 1;
    }
    if(yOffect >= (dataNum - maxNum) * (itemHeight + space))
    {
    
    
        yOffect = (dataNum - maxNum) * (itemHeight + space);
    }
    this->curDrawNum = drawNum;
    beginPosY = beginPosY - topLeftNum * (itemHeight);
    //画背景
    painter->setPen(Qt::PenStyle::NoPen);
    QBrush brush(Qt::BrushStyle::SolidPattern);
    brush.setColor(QColor(15, 15, 15, 255 * 0.1));
    painter->setBrush(brush);
    painter->drawRoundedRect(beginPosX-bgOffect, bgBeginPosY+bgOffect, itemWidth+bgOffect * 2, itemHeight * maxNum + space * (maxNum - 1), this->roundRadius, this->roundRadius);
    painter->setClipRegion(QRegion(beginPosX, bgBeginPosY, itemWidth, itemHeight * maxNum + space * (maxNum - 1)));
    painter->setClipping(true);
    //画小按钮
    QPen pen(Qt::PenStyle::SolidLine);
    pen.setJoinStyle(Qt::PenJoinStyle::RoundJoin);
    pen.setColor(QColor(176, 211, 212, 255));
    painter->setFont(font);

    for(int index = 0; index < drawNum; index++)
    {
    
    
        brush.setColor(Qt::white);
        painter->setBrush(brush);
        painter->setPen(Qt::PenStyle::NoPen);

        int beginY = beginPosY + ((itemHeight + space) * index);
        QRect info_rect(beginPosX, beginY, itemWidth, itemHeight);
        QPainterPath path;
        path.setFillRule( Qt::WindingFill );
        if(index == 0 || index == drawNum - 1)
        {
    
    
            if(index == 0)
            {
    
    
                info_rect = QRect(beginPosX, bgBeginPosY + ((itemHeight + space) * index), itemWidth, (1 - topLeftNum) * itemHeight);
//                info_rect.setHeight((1 - topLeftNum) * itemHeight);
//                info_rect.setTop(bgBeginPosY + ((itemHeight + space) * index));
                path.addRoundedRect (info_rect, this->roundRadius, this->roundRadius);
                QRect temp_rect(info_rect.left(), info_rect.top() + info_rect.height() / 2.0, info_rect.width(), info_rect.height() / 2.0 + 0.5);
                path.addRect(temp_rect);
            }
            else
            {
    
    
                if(topLeftNum > 0.0) info_rect.setHeight(topLeftNum * itemHeight);
                path.addRoundedRect (info_rect, this->roundRadius, this->roundRadius);
                QRect temp_rect(info_rect.left(), info_rect.top(), info_rect.width(), info_rect.height()/2.0 + 1);
                path.addRect(temp_rect);
            }
        }
        else
        {
    
    
            path.addRect(info_rect);
        }
//        painter->drawRoundedRect(beginPosX, beginY, itemWidth, itemHeight, this->roundRadius, this->roundRadius);
        if(curTopIndex + index == curClickNum)
        {
    
    
            QRadialGradient radial(beginPosX + itemWidth * 0.5, beginY + itemHeight, this->radius, beginPosX + itemWidth * 0.5, beginY);
            if(this->radius > 1)
            {
    
    
                radial.setColorAt(0, QColor(124, 34, 235));
            }
            radial.setColorAt(1, Qt::white);
            // 设置显示模式
            radial.setSpread(QGradient::PadSpread);
            // 设置画刷填充
            painter->setBrush(radial);
//            pen.setColor(QColor(0, 0, 0, 125));
        }
        else
        {
    
    
            painter->setBrush(Qt::white);
//            pen.setColor(QColor(176, 211, 212, 255));
        }
        this->activePoints[index] = info_rect;
        painter->drawPath(path);
        painter->setPen(pen);
        painter->drawText(beginPosX, beginY, itemWidth, itemHeight, Qt::AlignCenter, this->allData[index + curTopIndex]);
    }
    //这里!两种方法,第一种是设置绘制区域,第二种是注释的代码,擦拭掉多出来的区域
    painter->setClipping(false);
//    QRect destination(beginPosX, beginPosY, itemWidth, bgBeginPosY - beginPosY);
//    painter->eraseRect(destination);
//    QRect bottomDes(beginPosX, bgBeginPosY + (maxNum * space - 1) + (maxNum * itemHeight) + 1, itemWidth, itemHeight - (bgBeginPosY - beginPosY));
//    painter->eraseRect(bottomDes);
    //画顶部底部小三角
    int triWidth = 15, triHeight = 8;
    if(curTopIndex > 0 || topLeftNum > 0.0)
    {
    
    
        double topY = bgBeginPosY - triHeight;
        QPoint first(beginPosX + itemWidth * 0.5 - triWidth * 0.5, bgBeginPosY);
        QPoint second(beginPosX + itemWidth * 0.5 + triWidth * 0.5, bgBeginPosY);
        QPoint third(beginPosX + itemWidth * 0.5, topY);
        QPoint points[3] = {
    
    first, second, third};
        painter->setPen(Qt::NoPen);
        painter->drawPolygon(points, 3);
    }
    if(curBottomIndex < dataNum)
    {
    
    
        double bottomPosY = bgBeginPosY + (maxNum * space - 1) + (maxNum * itemHeight);
        double topY = bottomPosY + triHeight;
        painter->setPen(Qt::NoPen);

        brush.setColor(QColor(15, 15, 15, 255 * 0.1));
        painter->setBrush(brush);
        QPoint bgFirst(beginPosX + itemWidth * 0.5 - triWidth * 0.5 - bgOffect, bottomPosY);
        QPoint bgSecond(beginPosX + itemWidth * 0.5 + triWidth * 0.5 + bgOffect, bottomPosY);
        QPoint bgThird(beginPosX + itemWidth * 0.5, topY + bgOffect);
        QPoint bgPoints[3] = {
    
    bgFirst, bgSecond, bgThird};
        painter->drawPolygon(bgPoints, 3);

        brush.setColor(Qt::white);
        painter->setBrush(brush);
        QPoint first(beginPosX + itemWidth * 0.5 - triWidth * 0.5, bottomPosY - 1);
        QPoint second(beginPosX + itemWidth * 0.5 + triWidth * 0.5, bottomPosY - 1);
        QPoint third(beginPosX + itemWidth * 0.5, topY - 1);
        QPoint points[3] = {
    
    first, second, third};
        painter->drawPolygon(points, 3);
        //重新设置Size
        this->resize(width(), topY + bgOffect);
    }
    else
    {
    
    
        double bottomPosY = bgBeginPosY+bgOffect+itemHeight * maxNum + space * (maxNum - 1);
        //重新设置Size
        this->resize(width(), bottomPosY);
    }
}

void SelfComboBox::drawRoundedViewTitle(QPainter *painter)
{
    
    
    double beginPosX = 2, beginPosY = 0, itemWidth = width() - 4;
    painter->setPen(Qt::PenStyle::NoPen);
    QBrush brush(Qt::BrushStyle::SolidPattern);
    brush.setColor(QColor(15, 15, 15, 255 * 0.1));
    painter->setBrush(brush);
    painter->drawRoundedRect(beginPosX-2, beginPosY+2, itemWidth + 4, itemHeight, this->roundRadius, this->roundRadius);
    painter->setBrush(Qt::white);
    painter->drawRoundedRect(beginPosX, beginPosY, itemWidth, itemHeight, this->roundRadius, this->roundRadius);

    QPen pen(Qt::PenStyle::SolidLine);
    pen.setJoinStyle(Qt::PenJoinStyle::RoundJoin);
    pen.setColor(QColor(0, 0, 0, 125));
    painter->setPen(pen);
    painter->setFont(font);
    QString firstText = this->count() > 0 ? this->allData[0] : "";
    QString curText = this->currentText() == "" ? firstText : this->currentText();
    painter->drawText(beginPosX, beginPosY, itemWidth, itemHeight, Qt::AlignCenter, curText);
    //绘制三角形角标
    int triWidth = 12, triHeight = 7, triHeightMove = 1;
    int isDown = -1;
    isDown = this->isViewOpen ? -1 : 1;
    brush.setColor(Qt::white);
    painter->setBrush(brush);
    QPoint first(beginPosX + itemWidth * 0.8, beginPosY + itemHeight * 0.5 - triHeight * 0.5 * isDown + triHeightMove);
    QPoint second(beginPosX + itemWidth * 0.8 + triWidth, beginPosY + itemHeight * 0.5 - triHeight * 0.5 * isDown + triHeightMove);
    QPoint third(beginPosX + itemWidth * 0.8 + triWidth * 0.5, beginPosY + itemHeight * 0.5 + triHeight  * 0.5 * isDown + triHeightMove);
    QPainterPath painterPath;
    painterPath.setFillRule( Qt::WindingFill );
    painterPath.moveTo(first);
    painterPath.lineTo(second);
    painterPath.lineTo(third);
    painter->fillPath(painterPath, QColor(176, 211, 212, 255));
}

小重点

主要是滚动的时候,怎么防止文字或者里面的东西在区域外显示:
1、先画出来在擦掉eraseRect,缺点就是应用起来的时候会把别人的控件显示擦掉。
2、设置裁剪区域,这样就不用担心区域外的事了

painter->setClipRegion(QRegion(x, y, width, height));
painter->setClipping(true);
xxxxxx
painter->setClipping(false);

3、用图片混合模式搞QPainter::CompositionMode,好麻烦,如果是做绘画,橡皮檫的可以用这个来实现(大概)


总结

最主要的还是在区域内绘制吧,其次为啥不用QSS,想加一点动画……(其实就是闲的),最后还是贴项目整体代码(瞎写的……)有个Bug……,count为0会报错,在drawRoundedView加个判断。代码就不重新改重新上传了,见谅

void SelfComboBox::drawRoundedView(QPainter *painter)
{
    
    
    if(this->curOpenNum <= 0)
    {
    
    
        return;
    }
    //这里!!!!!!!!!!!!!!!!!!!!!
    if(this->count() <= 0)
    {
    
    
        return;
    }
    ...
}

猜你喜欢

转载自blog.csdn.net/u014147126/article/details/117783660