郑重说明
此博客转载自:博主【友善啊,朋友】的 - Qt编写自定义控件:图案密码锁
一、效果展示
二、源码
patterncodelockwidget.h
#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
patterncodelockwidget.cpp
#include "patterncodelockwidget.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();
}
三、使用教程
操作:点击鼠标,然后拖动,输入图案密码,输入完毕时,双击鼠标即可退出输入;
密码:图案从左到右,从上到下依此编号0、1、2、… 、9、a、b、c、d,依据输入的先后顺序得到密码字符串;
接口
- 创建图案密码控件:传入int值到构造函数中,作为图案的行列数;
- 设置/获取图案行列数:
-
int getNumberOfEachRowAndCol() const;
-
void setNumberOfEachRowAndCol(int newNumberOfEachRowAndCol);
- 输入完毕后可以通过:
QString getPassWord();
获取输入密码; - 输入完毕后会发送信号:
void setPassword(QString);
,将密码传递出来;