Qt - lock screen interface plus virtual keypad

1. Realize the effect

 

Click the "password input field" with the mouse to pop up a virtual keyboard. After entering the lock screen password, click the outer area of ​​the virtual keyboard to hide the virtual keyboard. Then click Login to enter the main interface successfully.

2. Virtual keyboard - program design

2.1 frmNum.h

#ifndef FRMNUM_H
#define FRMNUM_H

#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QTimer>

namespace Ui
{
    class frmNum;
}

class frmNum : public QWidget
{
    Q_OBJECT

public:
    explicit frmNum(QWidget *parent =nullptr);
    ~frmNum();

    //单例模式,保证一个程序只存在一个输入法实例对象
    static frmNum *Instance()
    {
        if (!_instance)
        {
            _instance = new frmNum;
        }
        return _instance;
    }

    void Init(QString style, int fontSize);  //初始化窗口,包括字体大小

protected:
    //事件过滤器:处理鼠标按下弹出小键盘
    bool eventFilter(QObject *obj, QEvent *event);

private slots:
    //焦点改变事件槽函数处理
    void focusChanged(QWidget *oldWidget, QWidget *nowWidget);
    //小键盘按键处理槽函数
    void btn_clicked();
    //改变小键盘样式
    void changeStyle(QString topColor, QString bottomColor,
                     QString borderColor, QString textColor);
    //定时器处理退格键
    void reClicked();

private:
    Ui::frmNum *ui;

    static frmNum *_instance;       //实例对象

    bool isPressBackBtn;            //是否长按退格键
    bool isFirst;                   //是否首次加载

    QPushButton *btnPress;          //长按按钮
    QTimer *backBtnTimert;          //退格键定时器
    QWidget *currentWidget;         //当前焦点的对象
    QLineEdit *currentLineEdit;     //当前焦点的单行文本框

    QString currentEditType;        //当前焦点控件的类型
    QString currentStyle;           //当前小键盘样式
    int currentFontSize;            //当前输入法面板字体大小

    bool checkPress();              //校验当前长按的按钮//初始化属性
    void ChangeStyle(QString currentStyle);             //改变样式

    void insertValue(QString value);//插入值到当前焦点控件
    void deleteValue();             //删除当前焦点控件的一个字符
    void clearValue();             //clear当前焦点控件的一个字符
};

#endif // FRMNUM_H

The above is the header file of the "virtual keyboard program". The singleton mode is used here to ensure that there is only one input method instance object in a program.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

2.2 frmNum.cpp

#include "frmnum.h"
#include "ui_frmnum.h"
#include <QShortcut>
#include <QDebug>

frmNum *frmNum::_instance = nullptr;

frmNum::frmNum(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::frmNum)
{
    ui->setupUi(this);

    //初始化窗口
    Init("black",20); //黑色,字体大小为20px

    ui->btnClear->setFocus();
    ui->btnClear->setShortcut(QKeySequence::InsertParagraphSeparator);
    ui->btnClear->setShortcut(Qt::Key_Enter);
    ui->btnClear->setShortcut(Qt::Key_Return);
}

frmNum::~frmNum()
{
    delete ui;
}

//初始化窗口,包括字体大小
void frmNum::Init(QString style, int fontSize)
{
    //设置窗口无边框且窗口显示在最顶层
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);

    isFirst = true; //是否首次加载
    isPressBackBtn = false; //是否长按退格键
    //退格键定时器
    backBtnTimert = new QTimer(this);
    connect(backBtnTimert, SIGNAL(timeout()), this, SLOT(reClicked()));
    currentWidget = nullptr;//当前焦点的对象

    //输入法面板字体大小,如果需要更改面板字体大小,该这里即可
    this->currentFontSize = fontSize;

    //如果需要更改输入法面板的样式,改变style这个形式参数即可
    //blue--淡蓝色  dev--dev风格  black--黑色  brown--灰黑色  lightgray--浅灰色  darkgray--深灰色  gray--灰色  silvery--银色
    this->ChangeStyle(style);

    //初始化小键盘上各按键属性
    ui->btn0->setProperty("btnNum", true);
    ui->btn1->setProperty("btnNum", true);
    ui->btn2->setProperty("btnNum", true);
    ui->btn3->setProperty("btnNum", true);
    ui->btn4->setProperty("btnNum", true);
    ui->btn5->setProperty("btnNum", true);
    ui->btn6->setProperty("btnNum", true);
    ui->btn7->setProperty("btnNum", true);
    ui->btn8->setProperty("btnNum", true);
    ui->btn9->setProperty("btnNum", true);
    ui->btnDelete->setProperty("btnOther", true);

    //链接小键盘上各数字键与功能键的点击信号到点击槽函数上
    QList<QPushButton *> btn = this->findChildren<QPushButton *>();
    foreach (QPushButton * b, btn)
    {
        connect(b, SIGNAL(clicked()), this, SLOT(btn_clicked()));
    }

    //绑定全局改变焦点信号槽
    connect(qApp, SIGNAL(focusChanged(QWidget *, QWidget *)),
            this, SLOT(focusChanged(QWidget *, QWidget *)));

    //绑定按键事件过滤器
    qApp->installEventFilter(this);
}

void frmNum::focusChanged(QWidget *oldWidget, QWidget *nowWidget)
{
    //qDebug() << "oldWidget:" << oldWidget << " nowWidget:" << nowWidget;
    if (nowWidget != nullptr && !this->isAncestorOf(nowWidget))
    {
        /*在Qt5和linux系统中(嵌入式linux除外),当输入法面板关闭时,焦点会变成无,然后焦点会再次移到焦点控件处
        这样导致输入法面板的关闭按钮不起作用,关闭后马上有控件获取焦点又显示.
        为此,增加判断,当焦点是从有对象转为无对象再转为有对象时不要显示.
        这里又要多一个判断,万一首个窗体的第一个焦点就是落在可输入的对象中,则要过滤掉*/

        #ifndef __arm__
                if (oldWidget == nullptr && !isFirst)
                {
                    return;
                }
        #endif

        isFirst = false;
        if (nowWidget->inherits("QLineEdit"))
        {
            currentLineEdit = static_cast<QLineEdit *>(nowWidget);
            currentEditType = "QLineEdit";
            this->setVisible(true);
        }
        else
        {
            currentWidget = nullptr;
            currentLineEdit = nullptr;
            currentEditType = "";
            this->setVisible(false);
        }

        QRect rect = nowWidget->rect();
        QPoint pos = QPoint(rect.left(), rect.bottom() + 2);
        pos = nowWidget->mapToGlobal(pos);
        this->setGeometry(pos.x(), pos.y(), this->width(), this->height());
    }

    Q_UNUSED(oldWidget);//未使用参数
}

//事件过滤器:处理鼠标按下弹出小键盘
bool frmNum::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonPress)
    {
        //确保每次点击输入栏都弹出虚拟键盘
        if (currentEditType == "QLineEdit")
        {
            if (obj != ui->btnClear)
            {
                this->setVisible(true);
            }
            btnPress = static_cast<QPushButton *>(obj);

            if (checkPress())
            {
                isPressBackBtn = true;
                backBtnTimert->start(500);
            }
        }

        return false;
    }
    else if (event->type() == QEvent::MouseButtonRelease)
    {
        btnPress = static_cast<QPushButton *>(obj);

        if (checkPress())
        {
            isPressBackBtn = false;
            backBtnTimert->stop();
        }

        return false;
    }

    return QWidget::eventFilter(obj, event);
}

//校验当前长按的按钮
bool frmNum::checkPress()
{
    //只有属于数字键盘的合法按钮才继续处理
    bool num_ok = btnPress->property("btnNum").toBool();
    bool other_ok = btnPress->property("btnOther").toBool();
    if (num_ok || other_ok)
    {
        return true;
    }

    return false;
}

//定时器处理退格键
void frmNum::reClicked()
{
    if (isPressBackBtn)
    {
        backBtnTimert->setInterval(30);
        btnPress->click();
    }
}

//小键盘按键处理槽函数
void frmNum::btn_clicked()
{
    //如果当前焦点控件类型为空,则返回不需要继续处理
    if (currentEditType == "")
    {
        return;
    }

    QPushButton *btn = static_cast<QPushButton *>(sender());
    QString objectName = btn->objectName();
    if (objectName == "btnDelete")
    {
        this->deleteValue();
    }
    else if (objectName == "btnClear")
    {
        this->clearValue();
    }
    else
    {
        QString value = btn->text();
        this->insertValue(value);
    }
}

//插入值到当前焦点控件
void frmNum::insertValue(QString value)
{
    if (currentEditType == "QLineEdit")
    {
        currentLineEdit->insert(value);
    }
}

//删除当前焦点控件的一个字符
void frmNum::deleteValue()
{
    if (currentEditType == "QLineEdit")
    {
        currentLineEdit->backspace();
    }
}

//清空当前焦点控件的所有字符
void frmNum::clearValue()
{
    if (currentEditType == "QLineEdit")
    {
        currentLineEdit->clear();
    }
}

//改变样式
void frmNum::ChangeStyle(QString currentStyle)
{
    if (currentStyle == "blue")
    {
        changeStyle("#DEF0FE", "#C0DEF6", "#C0DCF2", "#386487");
    }
    else if (currentStyle == "dev")
    {
        changeStyle("#C0D3EB", "#BCCFE7", "#B4C2D7", "#324C6C");
    }
    else if (currentStyle == "gray")
    {
        changeStyle("#E4E4E4", "#A2A2A2", "#A9A9A9", "#000000");
    }
    else if (currentStyle == "lightgray")
    {
        changeStyle("#EEEEEE", "#E5E5E5", "#D4D0C8", "#6F6F6F");
    }
    else if (currentStyle == "darkgray")
    {
        changeStyle("#D8D9DE", "#C8C8D0", "#A9ACB5", "#5D5C6C");
    }
    else if (currentStyle == "black")
    {
        changeStyle("#4D4D4D", "#292929", "#D9D9D9", "#CACAD0");
    }
    else if (currentStyle == "brown")
    {
        changeStyle("#667481", "#566373", "#C2CCD8", "#E7ECF0");
    }
    else if (currentStyle == "silvery")
    {
        changeStyle("#E1E4E6", "#CCD3D9", "#B2B6B9", "#000000");
    }
}

//改变小键盘样式
void frmNum::changeStyle(QString topColor, QString bottomColor, QString borderColor, QString textColor)
{
    QStringList qss;
    qss.append(QString("QWidget#frmNum{background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 %1,stop:1 %2);}")
               .arg(topColor).arg(bottomColor));
    qss.append("QPushButton{padding:5px;border-radius:3px;}");
    qss.append(QString("QPushButton:hover{background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 %1,stop:1 %2);}")
               .arg(topColor).arg(bottomColor));
    qss.append(QString("QLabel,QPushButton{font-size:%1pt;color:%2;}")
               .arg(currentFontSize).arg(textColor));
    qss.append(QString("QPushButton#btnPre,QPushButton#btnNext,QPushButton#btnClose{padding:5px;}"));
    qss.append(QString("QPushButton{border:1px solid %1;}")
               .arg(borderColor));
    qss.append(QString("QLineEdit{border:1px solid %1;border-radius:5px;padding:2px;background:none;selection-background-color:%2;selection-color:%3;}")
               .arg(borderColor).arg(bottomColor).arg(topColor));
    this->setStyleSheet(qss.join(""));
}

The above is the source code of the "virtual keyboard program". When the detection focus is on the single-line input field of QlineEdit, the virtual keyboard will be displayed, otherwise the virtual keyboard will be hidden. The display position invoked by the virtual keyboard is aligned with QlineEdit. I have tried but still cannot change the display position. In addition, the above code can be extended to support widgets such as QTextEdit and QTextBrowser.

2.3 frmNum.ui

 

3. Lock screen interface - program design

3.1 lockWin.h

/*
注:注意.ui文件中的dailog的focusPolicy要设置为clickFocus
*/
#ifndef LOCKWIN_H
#define LOCKWIN_H

#include <QDialog>
#include <QDebug>
#include "frmnum.h"

#define PASSWD "123456" //锁屏密码

namespace Ui {
class LoginWin;
}

class LockWin : public QDialog
{
    Q_OBJECT

public:
    explicit LockWin(QWidget *parent = nullptr);
    ~LockWin();

private slots:
    void on_cancelButton_clicked(); //取消按钮-点击槽函数:清空密码栏
    void on_loginButton_clicked(); //登录按钮-点击槽函数

private:
    Ui::LoginWin *ui;

    bool eventFilter(QObject *watched, QEvent *event); //事件过滤器

    frmNum *myFrmnum;
};

#endif // LOCKWIN_H

The above is the header file of the "lock screen interface program", where a "virtual keyboard" class object pointer is defined, and the lock screen password is set to "123456".

3.2 lockWin.cpp

#include "lockWin.h"
#include "ui_loginwin.h"

LockWin::LockWin(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginWin)
{
    ui->setupUi(this);

    //设置窗口无边框且窗口显示在最顶层
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);  
        
    //阻塞其父子窗口
    this->setWindowModality(Qt::WindowModal);

    ui->lineEdit->installEventFilter(this);

    //通过正则表达式设置"密码输入栏",只能输入数字0-9,不超过6位
    QValidator *accountValidator = new QRegExpValidator(QRegExp("[0-9]{6}"));
    ui->lineEdit->setValidator(accountValidator);

    myFrmnum = new frmNum(this);
}

LockWin::~LockWin()
{
    delete ui;
}

//事件过滤器
bool  LockWin::eventFilter(QObject *watched, QEvent *event)
{
    if(watched ==ui->lineEdit)
    {
        if(QEvent::FocusIn == event->type())
        {
            if(ui->lineEdit->echoMode()==QLineEdit::Normal)
            {
                ui->lineEdit->clear();
            }

            ui->lineEdit->setEchoMode(QLineEdit::Password);
        }
    }

    // 最后将事件交给上层对话框
    return QWidget::eventFilter(watched,event);
}

//---------------------------slots-----------------------------------------------

//取消按钮-点击槽函数:清空密码栏
void LockWin::on_cancelButton_clicked()
{
    ui->lineEdit->clear();
}

//登录按钮-点击槽函数
void LockWin::on_loginButton_clicked()
{
    if(ui->lineEdit->text()==PASSWD) //密码正确则关闭锁屏窗口
    {
        this->close();
    }
    else if(ui->lineEdit->text().isEmpty())
    {
        ui->infoLabel->setText("输入密码不能为空!");
    }
    else if(ui->lineEdit->text().length()<6)
    {
        ui->infoLabel->setText("输入密码不足6位!");
        ui->lineEdit->clear();
    }
    else
    {
        ui->infoLabel->setText("密码错误,请重新输入");
        ui->lineEdit->clear();
    }
}

The above is the source file of the "lock screen interface program". Here, this->setWindowModality(Qt::WindowModal) is used to become a modal dialog box to block the main interface, that is, the lock screen interface can be closed to enter the main interface.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (C++ language foundation, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/QtCompany/article/details/131710413