网页打不开404,一个网站做seo,网站做建筑三维图,厦门营销型网站建设1.界面实现效果
以下是具体的项目需要用到的效果展示#xff0c;用于验证字母。
2.简介
自定义CaptchaMovableLabel#xff0c;继承自QLabel类#xff1a; 中间的4个字母#xff0c;就是CaptchaMovableLabel类来实例化的对象。 主要功能如下#xff1a; 1.显示字母用于验证字母。
2.简介
自定义CaptchaMovableLabel继承自QLabel类 中间的4个字母就是CaptchaMovableLabel类来实例化的对象。 主要功能如下 1.显示字母 2.实现了鼠标移动事件使字母可拖动 3.存在定时器不断改变字母颜色 4.绘制字母时可旋转一定角度
#ifndef CAPTCHAMOVABLELABEL_H
#define CAPTCHAMOVABLELABEL_H#include QLabel
#include QTime
#include QPropertyAnimation
#include QDebug
#include QMouseEvent
#include QPainter
#include QPainterPath
#include QApplication
#include QGraphicsDropShadowEffect
#include cmath
#include QTimer#define CAPTCHA_REFRESH_DURATION 300 // 刷新的动画时长
#define CAPTCHA_CHAR_ANGLE_MAX 20 // 最大旋转角20°
#define CAPTCHA_SHADOW_BLUR_MAX 80 // 最大的阴影模糊半径class CaptchaMovableLabel : public QLabel
{Q_OBJECTQ_PROPERTY(int refreshProgress READ getRefreshProgress WRITE setRefreshProgress)Q_PROPERTY(int pressProgress READ getPressProgress WRITE setPressProgress)
public:CaptchaMovableLabel(QWidget* parent);void setAngle(int angle);void setColor(QColor color);void setText(QString ch);void startRefreshAnimation();void setMoveBorder(QRect rect);QString text();protected:void paintEvent(QPaintEvent *) override;void mousePressEvent(QMouseEvent *ev) override;void mouseMoveEvent(QMouseEvent *ev) override;void mouseReleaseEvent(QMouseEvent *ev) override;private:void startPressAnimation(int end);void setRefreshProgress(int g);int getRefreshProgress();inline bool isNoAni();void setPressProgress(int g);int getPressProgress();private slots://设置rgb颜色void slotMovePos();private:QPoint press_pos;bool dragging false;bool moved false;QGraphicsDropShadowEffect effect;QString ch;QColor color;int angle 0;int refreshProgress 100;QString prevCh;QColor prevColor;int prevAngle 0;QString prevChar;int pressProgress 0;bool inited false;QTimer movingTimer;int moveR, moveG, moveB;
};#endif // CAPTCHAMOVABLELABEL_H#include captchamovablelabel.hCaptchaMovableLabel::CaptchaMovableLabel(QWidget *parent) : QLabel(parent)
{effect.setOffset(0, 0);
// effect.setBlurRadius(8);setGraphicsEffect(effect);movingTimer.setInterval(30);movingTimer.setSingleShot(false);connect(movingTimer, SIGNAL(timeout()), this, SLOT(slotMovePos()));
}void CaptchaMovableLabel::setAngle(int angle)
{this-prevAngle this-angle;this-angle angle;
}void CaptchaMovableLabel::setColor(QColor color)
{this-prevColor this-color;this-color color;moveR qrand() % 5;moveG qrand() % 5;moveB qrand() % 5;movingTimer.start();
}void CaptchaMovableLabel::setText(QString text)
{this-prevCh this-ch;this-ch text;// 计算合适的高度QFontMetrics fm(this-font());double w fm.horizontalAdvance(text)2;double h fm.height();const double PI 3.141592;int xieHalf sqrt(w*w/4h*h/4); // 斜边的一半double a atan(w/h) CAPTCHA_CHAR_ANGLE_MAX * PI / 180; // 最大的倾斜角度int w2 xieHalf * sin(a) * 2;a atan(w/h) - CAPTCHA_CHAR_ANGLE_MAX * PI / 180;int h2 xieHalf * cos(a) * 2;resize(w2, h2);
}void CaptchaMovableLabel::startRefreshAnimation()
{if (!inited) // 第一次直接显示取消动画{inited true;return ;}QPropertyAnimation* ani new QPropertyAnimation(this, refreshProgress);ani-setStartValue(0);ani-setEndValue(100);ani-setDuration(qrand() % (CAPTCHA_REFRESH_DURATION / 3) CAPTCHA_REFRESH_DURATION / 3);ani-start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));connect(ani, SIGNAL(finished()), movingTimer, SLOT(start()));
}QString CaptchaMovableLabel::text()
{return ch;
}void CaptchaMovableLabel::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setFont(this-font());painter.setRenderHint(QPainter::SmoothPixmapTransform);int w2 width()/2, h2 height()/2;painter.translate(w2, h2); // 平移到中心绕中心点旋转if (isNoAni()) // 不在动画中直接绘制{painter.setPen(color);painter.rotate(angle);painter.drawText(QRect(-w2, -h2, width(), height()), Qt::AlignCenter, ch);return ;}// 动画里面前后渐变替换double newProp refreshProgress / 100.0;double oldProp 1.0 - newProp;double a prevAngle * oldProp angle * newProp 0.5;painter.save();painter.rotate(a);QColor c prevColor;c.setAlpha(c.alpha() * oldProp); // 旧文字渐渐消失painter.setPen(c);painter.drawText(QRect(-w2,-h2,width(),height()), Qt::AlignCenter, prevCh);c this-color;c.setAlpha(c.alpha() * newProp); // 新文字渐渐显示painter.setPen(c);painter.drawText(QRect(-w2, -h2, width(), height()), Qt::AlignCenter, ch);painter.restore();
}void CaptchaMovableLabel::mousePressEvent(QMouseEvent *ev)
{if (ev-button() Qt::LeftButton){// 开始拖拽press_pos ev-pos();dragging true;moved false;this-raise();movingTimer.stop();startPressAnimation(200);return ev-accept();}QLabel::mousePressEvent(ev);
}void CaptchaMovableLabel::mouseMoveEvent(QMouseEvent *ev)
{if (dragging ev-buttons() Qt::LeftButton){if (!moved (ev-pos() - press_pos).manhattanLength() QApplication::startDragDistance()){return QLabel::mouseMoveEvent(ev); // 还没到这时候}moved true;move(this-pos() ev-pos() - press_pos);ev-accept();return ;}QLabel::mouseMoveEvent(ev);
}void CaptchaMovableLabel::mouseReleaseEvent(QMouseEvent *ev)
{if (dragging){// 结束拖拽dragging false;movingTimer.start();startPressAnimation(0);}if (moved)return ev-accept();QLabel::mouseReleaseEvent(ev);
}void CaptchaMovableLabel::startPressAnimation(int end)
{QPropertyAnimation* ani new QPropertyAnimation(this, pressProgress);ani-setStartValue(pressProgress);ani-setEndValue(end);ani-setDuration(CAPTCHA_REFRESH_DURATION 1);ani-start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
}void CaptchaMovableLabel::setRefreshProgress(int g)
{this-refreshProgress g;update();
}int CaptchaMovableLabel::getRefreshProgress()
{return refreshProgress;
}bool CaptchaMovableLabel::isNoAni()
{return refreshProgress 100;
}void CaptchaMovableLabel::setPressProgress(int g)
{this-pressProgress g;double off g / 100;effect.setBlurRadius(g / 20.0);effect.setOffset(-off, off);
}int CaptchaMovableLabel::getPressProgress()
{return pressProgress;
}void CaptchaMovableLabel::slotMovePos()
{if (refreshProgress 100)return ;int val color.red() moveR;if ( val 255){val 255;moveR - qrand() % 5;}else if (val 0){val 0;moveR - qrand() % 5;}color.setRed(val);val color.green() moveG;if ( val 255){val 255;moveG - qrand() % 5;}else if (val 0){val 0;moveG - qrand() % 5;}color.setGreen(val);val color.blue() moveB;if ( val 255){val 255;moveB - qrand() % 5;}else if (val 0){val 0;moveB - qrand() % 5;}color.setBlue(val);
}
自定义CaptchaLabel类此类继承QWidget用于存在上面的4个字母。 主要功能如下 1.画噪音点背景上绘制无数个随机颜色的点 2.画噪音线这个线条是动态的随时间更改起渐变颜色线条位置 3.鼠标点击生成随机字母。
#ifndef CAPTCHALABEL_H
#define CAPTCHALABEL_H#include captchamovablelabel.h
#include QTimer#define CAPTCHAR_COUNT 4 // 验证码字符数量class CaptchaLabel : public QWidget
{Q_OBJECTQ_PROPERTY(int refreshProgress READ getRefreshProgress WRITE setRefreshProgress)
public:CaptchaLabel(QWidget* parent nullptr);void refresh();bool match(QString input);private:void initView();void initData();void setRefreshProgress(int g);int getRefreshProgress();bool isNoAni();private slots:void moveNoiseLines();protected:void paintEvent(QPaintEvent* ) override;void mouseReleaseEvent(QMouseEvent *event) override;private:CaptchaMovableLabel* charLabels[CAPTCHAR_COUNT]; // Label控件QListQPoint noisePoints; // 噪音点QListQColor pointColors; // 点的颜色QListQPointF lineStarts; // 噪音线起始点QListQPointF lineEnds; // 噪音先结束点QListQPointF startsV; // 起始点的移动速度带方向QListQPointF endsV; // 结束点的速度带方向QListQColor lineColor1s; // 线的渐变色1QListQColor lineColor2s; // 线的渐变色2QListint lineWidths;QTimer movingTimer;int refreshProgress 100;QListQPoint noisePoints2; // 新的位置int autoRefreshMax 2; // match错误几次后就自动刷新int matchFailCount 0; // match错误次数int matchFailAndRefreshCount 0; // 失败且导致刷新的次数强行刷新
};#endif // CAPTCHALABEL_H#include captchalabel.hCaptchaLabel::CaptchaLabel(QWidget *parent) : QWidget(parent)
{initView();// 这里延迟等待布局结束QTimer::singleShot(0, []{initData();refresh();});
}void CaptchaLabel::initView()
{// 初始化控件for (int i 0; i CAPTCHAR_COUNT; i){charLabels[i] new CaptchaMovableLabel(this);charLabels[i]-move(0, 0);}// 初始化时钟movingTimer.setInterval(30);movingTimer.setSingleShot(false);movingTimer.start();connect(movingTimer, SIGNAL(timeout()), this, SLOT(moveNoiseLines()));
}void CaptchaLabel::initData()
{// 初始化噪音线auto getRandomColor []{return QColor(qrand() % 255, qrand() % 255, qrand() % 255);};int w width(), h height();int count 20/*w * h / 400*/;int penW qMin(w, h) / 15;for (int i 0; i count; i){lineStarts.append(QPointF(qrand() % w, qrand() % h));lineEnds.append(QPointF(qrand() % w, qrand() % h));startsV.append(QPointF((qrand() % 30 - 15) / 10.0, (qrand() % 30 - 15) / 10.0));endsV.append(QPointF((qrand() % 30 - 15) / 10.0, (qrand() % 30 - 15) / 10.0));lineWidths.append(qrand() % penW 1);lineColor1s.append(getRandomColor());lineColor2s.append(getRandomColor());}
}void CaptchaLabel::setRefreshProgress(int g)
{this-refreshProgress g;update();
}int CaptchaLabel::getRefreshProgress()
{return refreshProgress;
}bool CaptchaLabel::isNoAni()
{return refreshProgress 100;
}void CaptchaLabel::moveNoiseLines()
{int w width(), h height();double vBase 100.0; // 大概最快要3秒钟走完for (int i 0; i lineStarts.size(); i){QPointF pos lineStarts[i];pos startsV.at(i);if (pos.x() 0)startsV[i].setX(qrand() % w / vBase);else if (pos.x() w)startsV[i].setX(- qrand() % w / vBase);if (pos.y() 0)startsV[i].setY(qrand() % h / vBase);else if (pos.y() h)startsV[i].setY(- qrand() % h / vBase);}for (int i 0; i lineEnds.size(); i){QPointF pos lineEnds[i];pos endsV.at(i);if (pos.x() 0)endsV[i].setX(qrand() % w / vBase);else if (pos.x() w)endsV[i].setX(- qrand() % w / vBase);if (pos.y() 0)endsV[i].setY(qrand() % h / vBase);else if (pos.y() h)endsV[i].setY(- qrand() % h / vBase);}update();
}
void CaptchaLabel::refresh()
{int width this-width();int height this-height();// 清空全部内容for (int i 0; i CAPTCHAR_COUNT; i)charLabels[i]-hide();refreshProgress -1;update();// 获取背景底色QPixmap rend(this-size());render(rend);QColor bgColor rend.toImage().pixelColor(width/2, height/2);int br bgColor.red(), bg bgColor.green(), bb bgColor.blue();// 开始随机生成const int border 10;int leftest width / border;int topest height / border;int wid width - leftest * 2;int hei height - topest * 2;for (int i 0; i CAPTCHAR_COUNT; i){auto label charLabels[i];// 随机大小QFont font;font.setPointSize( qrand() % 8 22 );label-setFont(font);// 随机旋转label-setAngle( qrand() % (CAPTCHA_CHAR_ANGLE_MAX*2) - CAPTCHA_CHAR_ANGLE_MAX);// 生成随机字符const QString pool QWERTYUIOPASDFGHJKLZXCVBNM;QChar rc pool.at(qrand() % pool.size());// 此时会调整大小setText必须在setFont之后label-setText(rc);// 生成随机位置排除边缘int left leftest wid * i / CAPTCHAR_COUNT;int right leftest wid * (i1) / CAPTCHAR_COUNT - label-width();int x qrand() % qMax(right-left, 1) left;int y qrand() % qMax(hei - label-height(), 1) topest;label-show(); // 之前是hide状态QPropertyAnimation * ani new QPropertyAnimation(label, pos);ani-setStartValue(label-pos());ani-setEndValue(QPoint(x, y));ani-setDuration(qrand() % (CAPTCHA_REFRESH_DURATION/2) CAPTCHA_REFRESH_DURATION/2);ani-setEasingCurve(QEasingCurve::OutQuart);ani-start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));// 生成随机颜色且必须和背景颜色有区分度QColor color;while (true){int r qrand() % 255;int g qrand() % 255;int b qrand() % 255;if (abs(r-br) abs(g-bg) abs(b-bb) 383){color QColor(r, g, b);break;}}label-setColor(color);label-startRefreshAnimation();}// 生成噪音点int count wid * hei / border; // 点的数量if (noisePoints.size() 0) // 第一次{for (int i 0; i count; i){int x qrand() % width;int y qrand() % height;noisePoints.append(QPoint(x, y / 2));noisePoints2.append(QPoint(x, y));pointColors.append(QColor(qrand() % 255, qrand() % 255, qrand() % 255));}}else{noisePoints noisePoints2;count noisePoints.size();noisePoints2.clear();for (int i 0; i count; i){noisePoints2.append(QPoint(qrand() % width, qrand() % height));}}// 生成噪音线QPropertyAnimation* ani new QPropertyAnimation(this, refreshProgress);ani-setStartValue(0);ani-setEndValue(100);ani-setDuration(qrand() % (CAPTCHA_REFRESH_DURATION) CAPTCHA_REFRESH_DURATION);ani-start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
}/*** 判断能否匹配*/
bool CaptchaLabel::match(QString input)
{// 根据label的位置排序std::sort(charLabels, charLabelsCAPTCHAR_COUNT, [](QLabel* a, QLabel* b){if (a-pos().x() b-pos().x())return a-pos().y() b-pos().y();return a-pos().x() b-pos().x();});// 按顺序组合成新的字符串QString captcha;for (int i 0; i CAPTCHAR_COUNT; i)captcha charLabels[i]-text();// 进行比较if (input.toUpper() captcha)return true;// 记录失败matchFailCount;if (matchFailCount autoRefreshMax // 达到刷新的次数|| matchFailAndRefreshCount 2) // 多次错误导致刷新{refresh();matchFailAndRefreshCount;matchFailCount 0;}return false;
}void CaptchaLabel::paintEvent(QPaintEvent *)
{QPainter painter(this);if (refreshProgress -1) // 不画可能需要获取背景颜色return ;// 画噪音点if (isNoAni()){// 显示随机的点for (int i 0; i noisePoints2.size(); i){painter.setPen(pointColors.at(i));painter.drawPoint(noisePoints2.at(i));}}else{// 动画过程中的点的移动double newProp refreshProgress / 100.0;double oldProp 1.0 - newProp;int count qMin(noisePoints.size(), noisePoints2.size());for (int i 0; i count; i){QPoint pt1 noisePoints.at(i);QPoint pt2 noisePoints2.at(i);QPoint pt( pt1.x() * oldProp pt2.x() * newProp,pt1.y() * oldProp pt2.y() * newProp );painter.setPen(pointColors.at(i));painter.drawPoint(pt);}}// 画噪音线painter.setRenderHint(QPainter::Antialiasing);for (int i 0; i lineStarts.size(); i){QLinearGradient grad(lineStarts.at(i), lineEnds.at(i));grad.setColorAt(0, lineColor1s.at(i));grad.setColorAt(1, lineColor2s.at(i));painter.setPen(QPen(grad, lineWidths.at(i)));painter.drawLine(lineStarts.at(i), lineEnds.at(i));}
}void CaptchaLabel::mouseReleaseEvent(QMouseEvent *event)
{if (QRect(0,0,width(),height()).contains(event-pos()))refresh();QWidget::mouseReleaseEvent(event);
}
3.使用
新建MainWindow拖动一个QWidget提升为CaptchaLabel即可。