一、實現效果
鼠標點擊“密碼輸入欄”,彈出虛擬鍵盤,輸入鎖屏密碼后,點擊虛擬鍵盤外部區域,則會隱藏虛擬鍵盤,再點擊登錄,成功進入主界面。
二、虛擬鍵盤-程序設計
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
上面是“虛擬鍵盤程序”的頭文件,這里使用了單例模式,保證一個程序只存在一個輸入法實例對象。
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(""));
}
上面是“虛擬鍵盤程序”的源代碼,當檢測焦點在 QlineEdit 單行輸入欄上,則顯示虛擬鍵盤,否則隱藏虛擬鍵盤。虛擬鍵盤調出的顯示位置跟 QlineEdit 對齊,嘗試過但還是無法改變顯示位置。另外可以對上面代碼進行擴展,擴展支持 QTextEdit、QTextBrowser 等窗口部件。
2.3 frmNum.ui
三、鎖屏界面-程序設計
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
上面是“鎖屏界面程序”的頭文件,這里定義了一個“虛擬鍵盤”類對象指針,鎖屏密碼設置為“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();
}
}
上面是“鎖屏界面程序”的源文件,這里使用了this->setWindowModality(Qt::WindowModal)
來成為模態對話框阻塞主界面,即鎖屏界面關閉才能進入主界面。
3.3 lockWin.ui
注:主窗口程序部分這里不再貼出,就是新建工程時系統生成的widget.h、widget.cpp、widget.ui。