Qt - 鎖屏界面加虛擬小鍵盤

1、實現效果

img

<span style="color:orange">鼠標點擊「密碼輸入欄」,彈出虛擬鍵盤,輸入鎖屏密碼後,點擊虛擬鍵盤外部區域,則會隱藏虛擬鍵盤,再點擊登陸,成功進入主界面。</span>linux

<br />c++

2、虛擬鍵盤-程序設計

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

<span style="color:orange">上面是「虛擬鍵盤程序」的頭文件,這裏使用了單例模式,保證一個程序只存在一個輸入法實例對象。</span>正則表達式

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

<span style="color:orange">上面是「虛擬鍵盤程序」的源代碼,當檢測焦點在 QlineEdit 單行輸入欄上,則顯示虛擬鍵盤,不然隱藏虛擬鍵盤。虛擬鍵盤調出的顯示位置跟 QlineEdit 對齊,嘗試過但仍是沒法改變顯示位置。另外能夠對上面代碼進行擴展,擴展支持 QTextEdit、QTextBrowser 等窗口部件。</span>app

2.3 frmNum.ui

img

<br />less

3、鎖屏界面-程序設計

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

<span style="color:orange">上面是「鎖屏界面程序」的頭文件,這裏定義了一個「虛擬鍵盤」類對象指針,鎖屏密碼設置爲「123456」。</span>函數

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

<span style="color:orange">上面是「鎖屏界面程序」的源文件,這裏使用了this->setWindowModality(Qt::WindowModal)來成爲模態對話框阻塞主界面,即鎖屏界面關閉才能進入主界面。</span>字體

3.3 lockWin.ui

img

注:主窗口程序部分這裏再也不貼出,就是新建工程時系統生成的widget.h、widget.cpp、widget.ui。ui

<br />this

相關文章
相關標籤/搜索