qt 实现随机验证码功能-kb88凯时官网登录

时间:2024-10-18
阅读:
免费资源网,https://freexyz.cn/

1.界面实现效果

以下是具体的项目需要用到的效果展示,用于验证字母。

qt 实现随机验证码功能

2.简介

自定义captchamovablelabel,继承自qlabel类:
中间的4个字母,就是captchamovablelabel类来实例化的对象。
主要功能如下:
1.显示字母;
2.实现了鼠标移动事件,使字母可拖动;
3.存在定时器,不断改变字母颜色;
4.绘制字母时,可旋转一定角度;

#ifndef captchamovablelabel_h
#define captchamovablelabel_h
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define captcha_refresh_duration 300 // 刷新的动画时长
#define captcha_char_angle_max 20 // 最大旋转角:20°
#define captcha_shadow_blur_max 80 // 最大的阴影模糊半径
class captchamovablelabel : public qlabel
{
    q_object
    q_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.h"
captchamovablelabel::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/4 h*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 
#define captchar_count 4 // 验证码字符数量
class captchalabel : public qwidget
{
    q_object
    q_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控件
    qlist noisepoints; // 噪音点
    qlist pointcolors; // 点的颜色
    qlist linestarts; // 噪音线起始点
    qlist lineends; // 噪音先结束点
    qlist startsv; // 起始点的移动速度(带方向)
    qlist endsv; // 结束点的速度(带方向)
    qlist linecolor1s; // 线的渐变色1
    qlist linecolor2s; // 线的渐变色2
    qlist linewidths;
    qtimer movingtimer;
    int refreshprogress = 100;
    qlist noisepoints2; // 新的位置
    int autorefreshmax = 2; // match错误几次后就自动刷新
    int matchfailcount = 0; // match错误次数
    int matchfailandrefreshcount = 0; // 失败且导致刷新的次数,强行刷新
};
#endif // captchalabel_h
#include "captchalabel.h"
captchalabel::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 * (i 1) / 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, charlabels captchar_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即可。

qt 实现随机验证码功能

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图