一个项目,同事让界面上做个密码权限控制,防止客户误操作,由于设备一般不接键盘,输密码就太麻烦了,干脆模仿手机的手势解锁做了个对话框。网上找了些资源,代码都不太全,只能按照自己的理解做了一个,退回功能实在懒得做了,其它功能基本跟手机上的一样。
头文件:
#pragma once
#include <QObject>
#include <qwidget.h>
#include <qdialog.h>
#include <qpainter.h>
#include <qevent.h>
class QWHGestureLogin : public QDialog
{
Q_OBJECT
public:
QWHGestureLogin(QWidget* parent = nullptr);
~QWHGestureLogin();
void setPassword(const QString& password); //设置密码 "12369"
struct TouchCircle
{
public:
int i;
int j;
bool Touch;
};
protected:
void resizeEvent(QResizeEvent* event);
void paintEvent(QPaintEvent* event);
void drawBg(QPainter* painter);
void drawOutCircle(QPainter* painter);
void drawTipLine(QPainter* painter);
void drawInCircle(QPainter* painter);
void drawJoinLine(QPainter* painter);
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
float distance(float x0, float y0, float x1, float y1) { return sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)); }
bool getPrePointDist(float x, float y, float xm, float ym);
float pointToSegDist(float x, float y, float x1, float y1, float x2, float y2); //计算点到线段垂线距离
bool verifyPassword(); //验证密码
protected:
int m_iDesktopHeight;
int m_iDesktopWidth;
QColor m_bgColor; //背景颜色
QColor m_outCircleColor; //外圆颜色
QColor m_selLineColor; //选中颜色
QColor m_inCircleColor; //内圆颜色
int w;
int h;
int m_radius;
int x_offset;
int y_offset;
bool m_bMousePressed;
QPointF m_PressedMousePos;
int m_TryNum;
TouchCircle m_TouchCircleIndex;
std::vector<QPoint> m_TouchPoints;
std::vector<int> m_TouchOrder;
std::vector<int> m_Password;
};
由于看手机的解锁有个规则,同一行或列不能跳过中间的圆圈进行选择,即选完1号圆圈不能跳过2号圆圈直接选3号圆圈,想了下用点到线段的距离进行判断,如果连线进入中间圆圈的范围,那么中间的圆圈也会被选中。
#include "QWHGestureLogin.h"
#include "qapplication.h"
#include "qdesktopwidget.h"
#include <iostream>
QWHGestureLogin::QWHGestureLogin(QWidget *parent)
: QDialog(parent)
{
this->setWindowFlags(Qt::FramelessWindowHint);
this->setStyleSheet("background-color:rgba(1, 146, 249, 150)");
this->setAttribute(Qt::WA_DeleteOnClose);
this->setMouseTracking(true);
//m_bgColor = QColor(50,50,50,50);
m_outCircleColor = QColor(150, 246, 249);
m_selLineColor = QColor(200,20,220);
m_inCircleColor = QColor(220,20,40);
QRect desktopSize = QApplication::desktop()->screenGeometry();
m_iDesktopHeight = desktopSize.height();
m_iDesktopWidth = desktopSize.width();
this->setFixedSize(m_iDesktopHeight * 0.6 / 7.0 * 6.0, m_iDesktopHeight * 0.6);
w = m_iDesktopHeight * 0.6 / 7.0 * 6.0;
h = m_iDesktopHeight * 0.6;
m_radius = w > h ? h / 16 : w / 14;
x_offset = -w / 2;
y_offset = -h / 2;
m_TouchCircleIndex.Touch= false;
m_bMousePressed = false;
m_TryNum = 0; // Max 3
}
QWHGestureLogin::~QWHGestureLogin()
{
}
void QWHGestureLogin::setPassword(const QString& password)
{
for (int i=0; i<password.size(); i++)
{
m_Password.push_back(QString(password[i]).toInt());
}
}
bool QWHGestureLogin::verifyPassword()
{
if (m_Password.size() != m_TouchOrder.size())
return false;
for (int i=0; i<m_Password.size(); i++)
{
if (m_Password[i] != m_TouchOrder[i]+1)
return false;
}
return true;
}
void QWHGestureLogin::resizeEvent(QResizeEvent* event)
{
w = this->width();
h = this->height();
m_radius = w > h ? h / 16 : w / 14;
x_offset = -w / 2;
y_offset = -h / 2;
QDialog::resizeEvent(event);
}
void QWHGestureLogin::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(w/2, h/2);
drawBg(&painter);
drawOutCircle(&painter);
drawTipLine(&painter);
drawInCircle(&painter);
drawJoinLine(&painter);
}
void QWHGestureLogin::drawBg(QPainter* painter)
{
painter->save();
/*int width = this->width();
int height = this->height();
painter->setPen(Qt::NoPen);
painter->setBrush(m_bgColor);
painter->drawRect(-width / 2, -height/2, width, height);*/
QFont font;
font.setPixelSize(m_radius);
painter->setFont(font);
painter->drawText(x_offset + m_radius, y_offset + m_radius, QStringLiteral(" 请解锁..."));
if(m_TryNum > 0)
painter->drawText(x_offset + m_radius, y_offset + 13*m_radius, QStringLiteral(" 密码错误,还有") + QString::number(3-m_TryNum) + QStringLiteral("机会"));
painter->restore();
}
void QWHGestureLogin::drawOutCircle(QPainter* painter)
{
painter->save();
QPen pen(m_outCircleColor, 2);
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
int x = x_offset + (2+ i * 4) * m_radius;
int y = y_offset + (2 + j * 4) * m_radius;
painter->drawEllipse(x, y, m_radius*2, m_radius*2);
}
}
painter->restore();
}
void QWHGestureLogin::drawTipLine(QPainter* painter)
{
painter->save();
int offset = 3;
if (m_TouchCircleIndex.Touch)
{
if (!m_bMousePressed)
{
QPen pen(m_selLineColor, 2);
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
int x = x_offset + (2 + m_TouchCircleIndex.i * 4) * m_radius - offset;
int y = y_offset + (2 + m_TouchCircleIndex.j * 4) * m_radius - offset;
int w = (m_radius + offset) * 2;
int h = (m_radius + offset) * 2;
QRect rect(x, y, w, h);
painter->drawEllipse(rect);
}
}
painter->restore();
}
void QWHGestureLogin::drawInCircle(QPainter* painter)
{
if (m_TouchPoints.empty() || !m_bMousePressed)
return;
painter->save();
for (int i=0; i< m_TouchPoints.size(); i++)
{
QBrush brush(m_inCircleColor, Qt::SolidPattern);
painter->setBrush(brush);
int x = x_offset + (2 + m_TouchPoints[i].x() * 4) * m_radius + 0.1 * m_radius;
int y = y_offset + (2 + m_TouchPoints[i].y() * 4) * m_radius + 0.1 * m_radius;
painter->drawEllipse(x, y, m_radius * 1.8, m_radius * 1.8);
}
painter->restore();
}
void QWHGestureLogin::drawJoinLine(QPainter* painter)
{
if (m_TouchPoints.empty() || !m_bMousePressed)
return;
painter->save();
for (int i = 0; i < m_TouchPoints.size(); i++)
{
QPen pen(m_inCircleColor, 10);
painter->setBrush(Qt::NoBrush);
painter->setPen(pen);
int x = x_offset + (3 + m_TouchPoints[i].x() * 4) * m_radius;
int y = y_offset + (3 + m_TouchPoints[i].y() * 4) * m_radius;
if (i == m_TouchPoints.size() - 1)
{
painter->drawLine(x , y, m_PressedMousePos.x()+x_offset, m_PressedMousePos.y()+y_offset);
}
else
{
int x_1 = x_offset + (3 + m_TouchPoints[i+1].x() * 4) * m_radius;
int y_1 = y_offset + (3 + m_TouchPoints[i+1].y() * 4) * m_radius;
painter->drawLine(x,y,x_1, y_1);
}
}
painter->restore();
}
void QWHGestureLogin::mouseMoveEvent(QMouseEvent* event)
{
QPointF tPoint = event->pos();
if(m_bMousePressed)
m_PressedMousePos = tPoint;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
int x = (3 + i * 4) * m_radius;
int y = (3 + j * 4) * m_radius;
if (!m_bMousePressed)
{
if (distance(tPoint.x(), tPoint.y(), x, y) < m_radius)
{
m_TouchCircleIndex.i = i;
m_TouchCircleIndex.j = j;
m_TouchCircleIndex.Touch = true;
this->update();
return;
}
}
else
{
if (distance(tPoint.x(), tPoint.y(), x, y) < m_radius || getPrePointDist(x,y, tPoint.x(), tPoint.y()))
{
int order = j * 3 + i;
if (std::find(m_TouchOrder.begin(), m_TouchOrder.end(), order) != m_TouchOrder.end())
{
int i = 0;
}
else
{
m_TouchPoints.push_back(QPoint(i, j));
m_TouchOrder.push_back(order);
this->update();
return;
}
}
}
}
}
m_TouchCircleIndex.Touch = false;
this->update();
}
bool QWHGestureLogin::getPrePointDist(float x, float y, float xm, float ym)
{
if (m_TouchPoints.empty())
return false;
QPoint lastPt = m_TouchPoints[m_TouchPoints.size() - 1];
int xr = (3 + lastPt.x() * 4) * m_radius;
int yr = (3 + lastPt.y() * 4) * m_radius;
float dis = pointToSegDist(x,y,xr, yr, xm, ym);
//std::cout << x << "," << y << "," << xr << "," << yr << "," << xm << "," << ym << "," << dis << std::endl;
if (dis < m_radius)
return true;
else
return false;
}
float QWHGestureLogin::pointToSegDist(float x, float y, float x1, float y1, float x2, float y2)
{
float pqx = x2 - x1;
float pqy = y2 - y1;
float dx = x - x1;
float dy = y - y1;
float d = pqx * pqx + pqy * pqy; // qp线段长度的平方
float t = pqx * dx + pqy * dy; // p pt向量 点积 pq 向量(p相当于A点,q相当于B点,pt相当于P点)
if (d > 0)// 除数不能为0; 如果为零 t应该也为零。下面计算结果仍然成立。
t /= d;// 此时t 相当于 上述推导中的 r。
if (t < 0)
t = 0;// 当t(r)< 0时,最短距离即为 pt点 和 p点(A点和P点)之间的距离。
else if (t > 1)
t = 1;// 当t(r)> 1时,最短距离即为 pt点 和 q点(B点和P点)之间的距离。
// t = 0,计算 pt点 和 p点的距离; t = 1, 计算 pt点 和 q点 的距离; 否则计算 pt点 和 投影点 的距离。
dx = x1 + t * pqx - x;
dy = y1 + t * pqy - y;
/*std::cout << t << std::endl;*/
return sqrt(dx * dx + dy * dy);
}
void QWHGestureLogin::mousePressEvent(QMouseEvent* event)
{
m_bMousePressed = true;
m_TouchCircleIndex.Touch = false;
m_TouchPoints.clear();
m_TouchOrder.clear();
this->update();
}
void QWHGestureLogin::mouseReleaseEvent(QMouseEvent* event)
{
m_bMousePressed = false;
m_TryNum++;
if (verifyPassword())
{
accept();
return;
}
if (m_TryNum == 3)
reject();
m_TouchPoints.clear();
m_TouchOrder.clear();
this->update();
}