Qt编写自定义控件:图案密码锁

代码:

#ifndef PATTERNCODELOCK_H
#define PATTERNCODELOCK_H

#include <QWidget>
#include <QTimer>

class PatternCodeLockWidget : public QWidget
{
    Q_OBJECT

public:
    PatternCodeLockWidget(int numberOfEachRowAndCol,QWidget *parent = nullptr);
    ~PatternCodeLockWidget()override;
    void reset();

    int getNumberOfEachRowAndCol() const;
    void setNumberOfEachRowAndCol(int newNumberOfEachRowAndCol);

signals:
    void setPassword(QString);

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

private:
    void onHoverTimer();
    QString getPassWord();

    enum class CircularState
    {
        normal,//正常
        hoverOnInnerSamrtCircular//鼠标悬浮在中间的小圆上面
    };

    enum class PatternCodeLockState
    {
        notSet,
        setting,
        setted_valid,//有效
        setted_invalid//无效
    };

    int numberOfEachRowAndCol;
    PatternCodeLockState PatternCodeLockSetUpState{PatternCodeLockState::notSet};
    QList<CircularState> circularStateList;
    QList<QRect> smartCircularRectList;
    bool isUpdateSmartCircularRect{true};
    QTimer hoverTimer;
    int radiu;
    int lastHoverIndex{-1};
    int currentchangeLength;
    bool mouseInSomeSmartCircular{false};
    QList<int> setList;
    QPoint settingMousPos;
};
#endif // PATTERNCODELOCK_H
#include "patterncodelock.h"
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>

PatternCodeLockWidget::PatternCodeLockWidget(int numberOfEachRowAndCol,QWidget *parent)
    : QWidget(parent),numberOfEachRowAndCol(numberOfEachRowAndCol)
{
    setMouseTracking(true);
    setMinimumSize(400,400);

    for (int i = 0;i < numberOfEachRowAndCol;++i)
    {
        for (int j = 0;j < numberOfEachRowAndCol;++j)
        {
            circularStateList << CircularState::normal;
            smartCircularRectList << QRect();
        }
    }

    connect(&hoverTimer,&QTimer::timeout,this,&PatternCodeLockWidget::onHoverTimer);
    hoverTimer.setInterval(40);
}

PatternCodeLockWidget::~PatternCodeLockWidget()
{
}

void PatternCodeLockWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    const auto rect = event->rect();
    painter.fillRect(rect,QColor("#1D1D1D"));

    auto width = rect.width();
    auto height = rect.height();
    auto sideLength = std::min(width,height) - 20;
    auto halfSideLength = sideLength/2;

    QRect drawZoneRect = QRect(-halfSideLength,-halfSideLength,sideLength,sideLength);
    drawZoneRect.translate(rect.center());

    painter.save();
    painter.setPen(QPen(QBrush("#141414"),5));
    painter.drawRoundedRect(drawZoneRect,12,12);
    painter.restore();

    radiu = sideLength / (1 + 1 + numberOfEachRowAndCol * 2 + (numberOfEachRowAndCol - 1) * 3);

    bool mustUpdateCircularRect = isUpdateSmartCircularRect;
    for (int i = 0,listIndex = 0;i < numberOfEachRowAndCol;++i)
    {
        for (int j = 0;j < numberOfEachRowAndCol;++j,++listIndex)
        {
            QPoint circularCenter = drawZoneRect.topLeft() + QPoint(2*radiu + i*5*radiu,2*radiu + j*5*radiu);
            if(setList.contains(listIndex))
            {
                if(PatternCodeLockSetUpState == PatternCodeLockState::setting ||
                   PatternCodeLockSetUpState == PatternCodeLockState::setted_valid)
                {
                    painter.setPen(QPen(QBrush(QColor("#00FF80")),3));
                }
                else
                {
                    painter.setPen(QPen(QBrush(QColor("#FE4C40")),3));
                }
            }
            else
            {
                painter.setPen(QPen(QBrush(Qt::white),3));
            }

            if(listIndex == lastHoverIndex &&
               hoverTimer.isActive() &&
               PatternCodeLockSetUpState == PatternCodeLockState::setting)
            {
                painter.drawEllipse(circularCenter,radiu + currentchangeLength,radiu + currentchangeLength);
            }
            else
            {
                painter.drawEllipse(circularCenter,radiu,radiu);
            }

            if(mustUpdateCircularRect)
            {
                QRect newCircularRect = QRect(-radiu/2,-radiu/2,radiu,radiu);
                newCircularRect.translate(circularCenter);
                smartCircularRectList[listIndex] = newCircularRect;
            }

            painter.save();
            switch (circularStateList.at(listIndex))
            {
                case CircularState::normal:
                {
                    if(listIndex == lastHoverIndex &&
                       hoverTimer.isActive() &&
                       PatternCodeLockSetUpState == PatternCodeLockState::notSet)
                    {
                        painter.setBrush(Qt::white);
                        painter.setPen(Qt::transparent);
                        painter.drawEllipse(circularCenter,
                                            static_cast<int>(radiu * 0.5 + currentchangeLength),
                                            static_cast<int>(radiu * 0.5 + currentchangeLength));
                    }
                    else
                    {
                        painter.setBrush(QColor("#888888"));
                        painter.setPen(Qt::transparent);
                        painter.drawEllipse(circularCenter,radiu/2,radiu/2);
                    }
                }break;
                case CircularState::hoverOnInnerSamrtCircular:
                {
                    painter.setPen(Qt::transparent);

                    if(PatternCodeLockSetUpState == PatternCodeLockState::notSet)
                    {
                        painter.setBrush(Qt::white);
                        if(hoverTimer.isActive())
                        {
                            painter.drawEllipse(circularCenter,
                                                static_cast<int>(radiu * 0.5 + currentchangeLength),
                                                static_cast<int>(radiu * 0.5 + currentchangeLength));
                        }
                        else
                        {
                            painter.drawEllipse(circularCenter,
                                                static_cast<int>(radiu * 0.7),
                                                static_cast<int>(radiu * 0.7));
                        }
                    }
                    else
                    {
                        if(PatternCodeLockSetUpState == PatternCodeLockState::setting ||
                           PatternCodeLockSetUpState == PatternCodeLockState::setted_valid)
                        {
                            painter.setBrush(QColor("#00FF80"));
                        }
                        else
                        {
                            painter.setBrush(QColor("#FE4C40"));
                        }
                        painter.drawEllipse(circularCenter,
                                            static_cast<int>(radiu * 0.7),
                                            static_cast<int>(radiu * 0.7));
                    }
                }break;
            }
            painter.restore();
        }
    }

    if(PatternCodeLockSetUpState != PatternCodeLockState::notSet)
    {
        painter.setBrush(Qt::transparent);
        if(PatternCodeLockSetUpState == PatternCodeLockState::setted_invalid)
        {
            painter.setPen(QPen(QBrush(QColor("#FE4C40")),7,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
        }
        else
        {
            painter.setPen(QPen(QBrush(QColor("#00FF80")),7,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
        }
        int setListSize = setList.size();
        for(int i = 0;i < setListSize;++i)
        {
            if(i < (setListSize - 1))
            {
                painter.drawLine(smartCircularRectList.at(setList.at(i)).center(),
                                 smartCircularRectList.at(setList.at(i + 1)).center());
            }
        }

        if(PatternCodeLockSetUpState == PatternCodeLockState::setting)
        {
            painter.drawLine(smartCircularRectList.at(setList.last()).center(),
                             settingMousPos);
        }
    }

    if(mustUpdateCircularRect)
        isUpdateSmartCircularRect = false;
}

void PatternCodeLockWidget::resizeEvent(QResizeEvent *event)
{
    isUpdateSmartCircularRect = true;
    QWidget::resizeEvent(event);
}

void PatternCodeLockWidget::mouseMoveEvent(QMouseEvent *event)
{
    auto pos = event->pos();
    int currentHoverIndex = -1;
    if(PatternCodeLockSetUpState == PatternCodeLockState::notSet)
    {
        bool mouseInSomeSmartCircular{false};
        for(int i = 0;i < smartCircularRectList.size();++i)
        {
            if(smartCircularRectList.at(i).contains(pos))
            {
                circularStateList[i] = CircularState::hoverOnInnerSamrtCircular;
                currentHoverIndex = i;
                mouseInSomeSmartCircular = true;
            }
            else
            {
                circularStateList[i] = CircularState::normal;
            }
        }

        if(mouseInSomeSmartCircular)
        {
            lastHoverIndex = currentHoverIndex;
            setCursor(Qt::PointingHandCursor);
        }
        else
        {
            setCursor(Qt::ArrowCursor);
        }

        if(!hoverTimer.isActive())
        {
            if(this->mouseInSomeSmartCircular != mouseInSomeSmartCircular)//鼠标进入了某个小圆或从小圆出来
            {
                this->mouseInSomeSmartCircular = mouseInSomeSmartCircular;
                if(this->mouseInSomeSmartCircular)
                {
                    currentchangeLength = 0;
                }
                else
                {
                    currentchangeLength = radiu * 0.2;
                }
                hoverTimer.start();
            }
        }
    }
    else if(PatternCodeLockSetUpState == PatternCodeLockState::setting)
    {
        bool mouseInSomeSmartCircular{false};
        for(int i = 0;i < smartCircularRectList.size();++i)
        {
            if(smartCircularRectList.at(i).contains(pos))
            {
                if(!setList.contains(i))
                {
                    setList << i;
                    circularStateList[i] = CircularState::hoverOnInnerSamrtCircular;
                    currentHoverIndex = i;
                }
                mouseInSomeSmartCircular = true;
            }
        }

        if(this->mouseInSomeSmartCircular != mouseInSomeSmartCircular)
        {
            this->mouseInSomeSmartCircular = mouseInSomeSmartCircular;
            if(mouseInSomeSmartCircular)
            {
                lastHoverIndex = currentHoverIndex;
                setCursor(Qt::PointingHandCursor);

                if(!hoverTimer.isActive())
                {
                    currentchangeLength = 0;
                    hoverTimer.start();
                }
            }
            else
            {
                setCursor(Qt::ArrowCursor);
            }
        }

        settingMousPos = pos;
    }
    update();
    QWidget::mouseMoveEvent(event);
}

void PatternCodeLockWidget::onHoverTimer()
{
    if(PatternCodeLockSetUpState == PatternCodeLockState::notSet)
    {
        if(mouseInSomeSmartCircular)
        {
            if(currentchangeLength >= (radiu * 0.2))
            {
                hoverTimer.stop();
            }
            currentchangeLength += 2;
        }
        else
        {
            if(currentchangeLength <= -(radiu * 0.1))
            {
                hoverTimer.stop();
            }
            currentchangeLength -= 2;
        }
    }
    else if(PatternCodeLockSetUpState == PatternCodeLockState::setting)
    {
        if(currentchangeLength >= (radiu * 0.1))
        {
            hoverTimer.stop();
        }
        currentchangeLength += 2;
    }
    update();
}

void PatternCodeLockWidget::mousePressEvent(QMouseEvent *event)
{
    if(lastHoverIndex != -1)
    {
        if(PatternCodeLockSetUpState == PatternCodeLockState::notSet)//开始设置
        {
            PatternCodeLockSetUpState = PatternCodeLockState::setting;
            setList << lastHoverIndex;
            circularStateList[lastHoverIndex] = CircularState::hoverOnInnerSamrtCircular;
            settingMousPos = event->pos();
            currentchangeLength = 0;
            hoverTimer.start();
            update();
        }
    }

    QWidget::mousePressEvent(event);
}

void PatternCodeLockWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(PatternCodeLockSetUpState == PatternCodeLockState::setting && !mouseInSomeSmartCircular)
    {
        if(setList.size() < 4)
        {
            PatternCodeLockSetUpState = PatternCodeLockState::setted_invalid;
            emit setPassword("4位以下的无效密码");
        }
        else
        {
            PatternCodeLockSetUpState = PatternCodeLockState::setted_valid;
            emit setPassword(getPassWord());
        }
        update();
    }

    QWidget::mouseDoubleClickEvent(event);
}

QString PatternCodeLockWidget::getPassWord()
{
    QString psw;
    for(const int & value : setList)
    {
        psw.append(QString::number(value,16));
    }

    return psw;
}

int PatternCodeLockWidget::getNumberOfEachRowAndCol() const
{
    return numberOfEachRowAndCol;
}

void PatternCodeLockWidget::setNumberOfEachRowAndCol(int newNumberOfEachRowAndCol)
{
    if(newNumberOfEachRowAndCol > 1 && newNumberOfEachRowAndCol < 10)
    {
        reset();
        circularStateList.clear();
        smartCircularRectList.clear();
        numberOfEachRowAndCol = newNumberOfEachRowAndCol;
        for (int i = 0;i < numberOfEachRowAndCol;++i)
        {
            for (int j = 0;j < numberOfEachRowAndCol;++j)
            {
                circularStateList << CircularState::normal;
                smartCircularRectList << QRect();
            }
        }
        update();
    }
}

void PatternCodeLockWidget::reset()
{
    PatternCodeLockSetUpState = PatternCodeLockState::notSet;
    std::fill(circularStateList.begin(),circularStateList.end(),CircularState::normal);
    lastHoverIndex = -1;
    hoverTimer.stop();
    currentchangeLength = 0;
    isUpdateSmartCircularRect = true;
    mouseInSomeSmartCircular = false;
    setList.clear();
    settingMousPos = QPoint(0,0);
    update();
}

使用:

#include "patterncodelock.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QSpinBox>
#include <QLineEdit>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    PatternCodeLockWidget * w = new PatternCodeLockWidget(4);

    QWidget widget;
    auto hb = new QHBoxLayout;
    hb->addWidget(new QLabel("设置的密码:"));
    auto lineEdit = new QLineEdit;
    hb->addWidget(lineEdit);
    hb->addWidget(new QLabel("设置行列数:"));
    auto spinbox = new QSpinBox;
    spinbox->setRange(2,9);
    spinbox->setMinimumWidth(100);
    spinbox->setValue(w->getNumberOfEachRowAndCol());
    hb->addWidget(spinbox);
    auto btn = new QPushButton("还原");
    hb->addWidget(btn);
    auto vb = new QVBoxLayout;
    vb->addWidget(w,5);
    vb->addLayout(hb,1);
    widget.setLayout(vb);
    QObject::connect(w,&PatternCodeLockWidget::setPassword,lineEdit,&QLineEdit::setText);
    QObject::connect(spinbox,&QSpinBox::valueChanged,w,&PatternCodeLockWidget::setNumberOfEachRowAndCol);
    QObject::connect(btn,&QPushButton::clicked,w,&PatternCodeLockWidget::reset);

    widget.show();
    return a.exec();
}

效果:

UI参考:jQuery手机图案密码锁设置代码

猜你喜欢

转载自blog.csdn.net/kenfan1647/article/details/121716467