Qt+VS 自定义对话框 九宫格手势解锁

一个项目,同事让界面上做个密码权限控制,防止客户误操作,由于设备一般不接键盘,输密码就太麻烦了,干脆模仿手机的手势解锁做了个对话框。网上找了些资源,代码都不太全,只能按照自己的理解做了一个,退回功能实在懒得做了,其它功能基本跟手机上的一样。

头文件:

#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();
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值