auther: abinng date: 2026-03-26 10:01
createDate:2026-03-26 10:01
在银行等私密输入密码的地方,其键盘的排列并不是顺序的123456789,而是九个位置顺序打乱
功能描述
矩阵键盘分为数字键(0~9),功能按键(删除和确认)
输入 6 位数字模拟密码,每次输入 6
位后,键盘数字按键随机变换坐标,功能按键坐标固定
点击确定后,显示清零后数字按键随机变换坐标
矩阵键盘按键尺寸固定,每个按键长宽为 80px * 80px,LCD
显示区长宽为 240px * 80px
创建项目
本次使用 CLion ,如果还没安装:CLion
安装
进入 CLion -> New Project -> (左侧)Qt Widget Executable
修改项目路径
选择 Qt CMake prefix path 其实就是 CMakeLists.txt 中的
set(CMAKE_PREFIX_PATH "D:/09_SW/_Dev/Qt/6.5.3/mingw_64")
Language standard 选择 C++14 即可
最后点击右下角 Create
创建后,默认创建的 CMakeLists.txt 会有点问题
官方不太建议用这三行来开启 MOC, RCC, UIC
1 2 3 set (CMAKE_AUTOMOC ON ) set (CMAKE_AUTORCC ON ) set (CMAKE_AUTOUIC ON )
我们把它换成
1 qt_standard_project_setup()
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 cmake_minimum_required (VERSION 4.1 ) project (ex15_keyPad) set (CMAKE_CXX_STANDARD 14 ) set (CMAKE_PREFIX_PATH "D:/09_SW/_Dev/Qt/6.5.3/mingw_64" ) find_package (Qt6 COMPONENTS Core Gui Widgets REQUIRED) qt_standard_project_setup() add_executable (ex15_keyPad main.cpp key_pad.cpp key_pad.h key_pad.ui) target_link_libraries (ex15_keyPad Qt6::Core Qt6::Gui Qt6::Widgets) if (WIN32 AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) set (DEBUG_SUFFIX) if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug" ) set (DEBUG_SUFFIX "d" ) endif () set (QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}" ) if (NOT EXISTS "${QT_INSTALL_PATH}/bin" ) set (QT_INSTALL_PATH "${QT_INSTALL_PATH}/.." ) if (NOT EXISTS "${QT_INSTALL_PATH}/bin" ) set (QT_INSTALL_PATH "${QT_INSTALL_PATH}/.." ) endif () endif () if (EXISTS "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" ) add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/" ) add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${QT_INSTALL_PATH}/plugins/platforms/qwindows${DEBUG_SUFFIX}.dll" "$<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins/platforms/" ) endif () foreach (QT_LIB Core Gui Widgets) add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${QT_INSTALL_PATH}/bin/Qt6${QT_LIB}${DEBUG_SUFFIX}.dll" "$<TARGET_FILE_DIR:${PROJECT_NAME}>" ) endforeach (QT_LIB) endif ()
默认创建的这个 main.cpp 也没啥用
接下来创建UI文件
此时会创建三个文件:key_pad.ui, key_pad.h,
key_pad.cpp
初步设置界面
先用鼠标拖动的方式大致规划一下界面
双击 key_pad.ui 会自动进入 Qt Designer
我们先规定 QWidget ,垂直布局,长宽和最大/最小长宽均为 240 x 400
这样就保证了用户拖不动大小,界面不会乱
整体大致分为两块,一个 LCD Number 用于显示,一个 Widget 作为容器
将两个组件改一下名字,lcdShow, padWidget
计算器设为无边框,将 KeyPad 的 Layout
中,几个边距(Margin)和内部间隔(Spacing)调为 0
将 lcdShow 和 padWidget 所在的布局,比例调为 1, 4
保存一下,回到 CLion,构建一下,没报错就是正常的
补全 main.cpp 测试一下
点击展开
1 2 3 4 5 6 7 8 9 #include "key_pad.h" #include "cmake-build-debug/ex15_keyPad_autogen/include/ui_key_pad.h" int main (int argc, char *argv[]) { QApplication a (argc, argv) ; KeyPad win; win.show (); return QApplication::exec (); }
先运行看看效果
写代码
key_pad.h
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #ifndef EX15_KEYPAD_KEY_PAD_H #define EX15_KEYPAD_KEY_PAD_H #include <QWidget> #include <QGridLayout> #include <QString> QT_BEGIN_NAMESPACE namespace Ui { class KeyPad ; } struct KeyInfo { QString text; int value; }; QT_END_NAMESPACE class KeyPad : public QWidget { Q_OBJECT public : explicit KeyPad (QWidget *parent = nullptr ) ; ~KeyPad () override ; public slots: void lcdShow () const ; private : void fixed_keypad () const ; void random_keypad () const ; Ui::KeyPad *ui; QGridLayout *pad_layout; }; #endif
key_pad.cpp
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include "key_pad.h" #include "ui_key_pad.h" #include <QPushButton> #include <QRandomGenerator> #include <QDateTime> static QList<KeyInfo> pads = { {"1" , 1 }, {"2" , 2 }, {"3" , 3 }, {"4" , 4 }, {"5" , 5 }, {"6" , 6 }, {"7" , 7 }, {"8" , 8 }, {"9" , 9 },{"删除" , 11 }, {"0" , 0 }, {"确定" , 12 } }; KeyPad::KeyPad (QWidget *parent) : QWidget (parent), ui (new Ui::KeyPad) { ui->setupUi (this ); pad_layout = new QGridLayout (ui->padWidget); pad_layout->setContentsMargins (0 ,0 ,0 ,0 ); pad_layout->setSpacing (0 ); fixed_keypad (); } void KeyPad::fixed_keypad () const { for (int x = 0 ; x < 4 ; ++x) { for (int y = 0 ; y < 3 ; ++y) { int pos = x * 3 + y; auto btn = new QPushButton (pads[pos].text, ui->padWidget); btn->setProperty ("value" , pads[pos].value); btn->setMinimumSize (80 , 80 ); btn->setMaximumSize (80 , 80 ); pad_layout->addWidget (btn, x, y, 1 , 1 ); connect (btn, &QPushButton::clicked, this , &KeyPad::lcdShow); } } } void KeyPad::random_keypad () const { QRandomGenerator random (QDateTime::currentSecsSinceEpoch()) ; QList<int > indexes; for (int i = 0 ; i < pads.size (); ++i) { if (pads[i].value < 10 ) { indexes.append (i); } } std::shuffle (indexes.begin (), indexes.end (), random); QPushButton *btn; int pos_index = 0 ; for (int x = 0 ; x < 4 ; ++x) { for (int y = 0 ; y < 3 ; ++y) { int pos = x * 3 + y; if (pos == 9 || pos == 11 ) { btn = new QPushButton (pads[pos].text, ui->padWidget); btn->setProperty ("value" , pads[pos].value); } else { btn = new QPushButton (pads[indexes[pos_index]].text, ui->padWidget); btn->setProperty ("value" , pads[indexes[pos_index]].value); ++pos_index; } btn->setMinimumSize (80 , 80 ); btn->setMaximumSize (80 , 80 ); pad_layout->addWidget (btn, x, y, 1 , 1 ); connect (btn, &QPushButton::clicked, this , &KeyPad::lcdShow); } } } KeyPad::~KeyPad () { delete ui; } void KeyPad::lcdShow () const { auto btn = qobject_cast <QPushButton *>(sender ()); if (btn == nullptr ) { return ; } int lcd_value = ui->lcdShow->intValue (); int v = btn->property ("value" ).toInt (); if (v == 11 ) { lcd_value /= 10 ; ui->lcdShow->display (lcd_value); } else if (v == 12 ) { ui->lcdShow->display (0 ); random_keypad (); } else { lcd_value *= 10 ; lcd_value += v; ui->lcdShow->display (lcd_value); } }
main.cpp
点击展开
1 2 3 4 5 6 7 8 9 10 11 #include <QApplication> #include "key_pad.h" #include "cmake-build-debug/ex15_keyPad_autogen/include/ui_key_pad.h" int main (int argc, char *argv[]) { QApplication a (argc, argv) ; KeyPad win; win.show (); return QApplication::exec (); }
效果:
这不是最终版,因为目前的样式还是 Qt 默认的样式,我们要用 QSS
进行渲染一下按键
QSS 按键渲染
接下来我们需要加入 QSS按键渲染,结果如下图:
其实就是加上 CSS 语法的字符串,设置为 Qt 组件的 StyleSheet,调用
setStyleSheet 函数
key_pad.cpp 中
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 // // Created by abinng on 2026/3/26. // // You may need to build the project (run Qt uic code generator) to get "ui_key_pad.h" resolved #include "key_pad.h" #include "ui_key_pad.h" #include <QPushButton> #include <QRandomGenerator> #include <QDateTime> // static QList<QString> nums = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "删除", "0", "确定"}; static QList<KeyInfo> pads = { {"1", 1}, {"2", 2}, {"3", 3}, {"4", 4}, {"5", 5}, {"6", 6}, {"7", 7}, {"8", 8}, {"9", 9},{"删除", 11}, {"0", 0}, {"确定", 12} }; + static QString lcdStyle = R"( + background-color: #282726; + color: #ebe9eb; + font-size: 55px; + padding: 0px 5px 0px 5px; + text-align: right; + margin-bottom: 0px; + )"; KeyPad::KeyPad(QWidget *parent) : QWidget(parent), ui(new Ui::KeyPad) { ui->setupUi(this); + ui->lcdShow->setStyleSheet(lcdStyle); pad_layout = new QGridLayout(ui->padWidget); pad_layout->setContentsMargins(0,0,0,0); pad_layout->setSpacing(0); random_keypad(); } void KeyPad::fixed_keypad() const { for (int x = 0; x < 4; ++x) { for (int y = 0; y < 3; ++y) { int pos = x * 3 + y; auto btn = new QPushButton(pads[pos].text, ui->padWidget); btn->setProperty("value", pads[pos].value); btn->setMinimumSize(80, 80); // 设置最小尺寸 btn->setMaximumSize(80, 80); // 设置最大尺寸 pad_layout->addWidget(btn, x, y, 1, 1); connect(btn, &QPushButton::clicked, this, &KeyPad::lcdShow); } } } void KeyPad::random_keypad() const { QRandomGenerator random(QDateTime::currentSecsSinceEpoch()); QList<int> indexes; for (int i = 0; i < pads.size(); ++i) { if (pads[i].value < 10) { indexes.append(i); } } std::shuffle(indexes.begin(), indexes.end(), random); QPushButton *btn; int pos_index = 0; for (int x = 0; x < 4; ++x) { for (int y = 0; y < 3; ++y) { int pos = x * 3 + y; if (pos == 9 || pos == 11) { btn = new QPushButton(pads[pos].text, ui->padWidget); btn->setProperty("value", pads[pos].value); + btn->setStyleSheet(R"( + QPushButton { + background-color: #f99f2b; + font-size: 25px; + color: #ebe9eb; border-radius: 0px; border: 1px solid rgb(44,44,46); + } + QPushButton:pressed { + background-color: #c67e20 + })"); } else { btn = new QPushButton(pads[indexes[pos_index]].text, ui->padWidget); btn->setProperty("value", pads[indexes[pos_index]].value); + btn->setStyleSheet(R"( + QPushButton { + background-color: #5e5d5c; + font-size: 22px; + color: #ebe9eb; border-radius: 0px; border: 1px solid rgb(44,44,46); + } + QPushButton:pressed { + background-color: #9f9e9e + })"); ++pos_index; } btn->setMinimumSize(80, 80); // 设置最小尺寸 btn->setMaximumSize(80, 80); // 设置最大尺寸 pad_layout->addWidget(btn, x, y, 1, 1); connect(btn, &QPushButton::clicked, this, &KeyPad::lcdShow); } } } KeyPad::~KeyPad() { delete ui; } void KeyPad::lcdShow() const { auto btn = qobject_cast<QPushButton *>(sender()); if (btn == nullptr) { return ; } int lcd_value = ui->lcdShow->intValue(); int v = btn->property("value").toInt(); if (v == 11) { lcd_value /= 10; ui->lcdShow->display(lcd_value); } else if (v == 12) { ui->lcdShow->display(0); random_keypad(); } else { if (lcd_value <= 9999) { lcd_value *= 10; lcd_value += v; ui->lcdShow->display(lcd_value); } } }
这么一来就好了
项目内存管理改进与主逻辑实现
每次点击确定按键,调用 random_keypad() 时,都会循环用
new 操作创建按钮
此时父对象会产生大量子对象,内存占用率高
我们全局创建一个按钮列表,在构造函数中进行 new 初始化分配空间
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 KeyPad::KeyPad (QWidget *parent) : QWidget (parent), ui (new Ui::KeyPad) { ui->setupUi (this ); ui->lcdShow->setStyleSheet (lcdStyle); pad_layout = new QGridLayout (ui->padWidget); pad_layout->setContentsMargins (0 ,0 ,0 ,0 ); pad_layout->setSpacing (0 ); QPushButton *btn; for (int i = 0 ; i < pads.size (); ++i) { btn = new QPushButton (pads[i].text, ui->padWidget); btn->setProperty ("value" , pads[i].value); btn->setMinimumSize (80 , 80 ); btn->setMaximumSize (80 , 80 ); if (i == 9 || i == 11 ) { btn->setStyleSheet (R"( QPushButton { background-color: #f99f2b; font-size: 25px; color: #ebe9eb; border-radius: 0px; border: 1px solid rgb(44,44,46); } QPushButton:pressed { background-color: #c67e20 })" ); } else { btn->setStyleSheet (R"( QPushButton { background-color: #5e5d5c; font-size: 22px; color: #ebe9eb; border-radius: 0px; border: 1px solid rgb(44,44,46); } QPushButton:pressed { background-color: #9f9e9e })" ); } connect (btn, &QPushButton::clicked, this , &KeyPad::lcdShow); btns.append (btn); } random_keypad (); }
之后 random_keypad() 中只要改变 btn
的指向并添加进布局就好
lcdShow()
中,需要优化为,六位数字时,打乱键盘,多于6位数字时,清空lcd并显示刚刚点击的数字
点击展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void KeyPad::lcdShow () const { static int cnt = 0 ; auto btn = qobject_cast <QPushButton *>(sender ()); if (btn == nullptr ) { return ; } int lcd_value = ui->lcdShow->intValue (); int v = btn->property ("value" ).toInt (); if (v == 11 ) { if (cnt <= 0 ) { return ; } lcd_value /= 10 ; ui->lcdShow->display (lcd_value); --cnt; } else if (v == 12 ) { ui->lcdShow->display (0 ); random_keypad (); cnt = 0 ; } else { ++cnt; if (cnt > 6 ) { ui->lcdShow->display (v); cnt = 1 ; return ; } if (cnt == 6 ) { random_keypad (); } lcd_value *= 10 ; lcd_value += v; ui->lcdShow->display (lcd_value); } }
这里的 lcd 屏幕显示 6 位数字是 lcdNumber 的一个属性,可以在 Qt
Designer 中调:
总代码
key_pad.h key_pad.cpp
main.cpp
点击展开
key_pad.h key_pad.cpp main.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #ifndef EX15_KEYPAD_KEY_PAD_H #define EX15_KEYPAD_KEY_PAD_H #include <QWidget> #include <QGridLayout> #include <QString> QT_BEGIN_NAMESPACE namespace Ui { class KeyPad ; } struct KeyInfo { QString text; int value; }; QT_END_NAMESPACE class KeyPad : public QWidget { Q_OBJECT public : explicit KeyPad (QWidget *parent = nullptr ) ; ~KeyPad () override ; public slots: void lcdShow () const ; private : void fixed_keypad () const ; void random_keypad () const ; Ui::KeyPad *ui; QGridLayout *pad_layout; }; #endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #ifndef EX15_KEYPAD_KEY_PAD_H #define EX15_KEYPAD_KEY_PAD_H #include <QWidget> #include <QGridLayout> #include <QString> QT_BEGIN_NAMESPACE namespace Ui { class KeyPad ; } struct KeyInfo { QString text; int value; }; QT_END_NAMESPACE class KeyPad : public QWidget { Q_OBJECT public : explicit KeyPad (QWidget *parent = nullptr ) ; ~KeyPad () override ; public slots: void lcdShow () const ; private : void fixed_keypad () const ; void random_keypad () const ; Ui::KeyPad *ui; QGridLayout *pad_layout; }; #endif
1 2 3 4 5 6 7 8 9 10 11 #include <QApplication> #include "key_pad.h" #include "cmake-build-debug/ex15_keyPad_autogen/include/ui_key_pad.h" int main (int argc, char *argv[]) { QApplication a (argc, argv) ; KeyPad win; win.show (); return QApplication::exec (); }
个人实现
我自己复现后又给按键加上了图标,但是遇到了点问题,就是不显示图标
原因在 CMakeLists.txt 中,不知道什么原因,明明加上了
qt_standard_project_setup() ,还是编译成功但没显示图片
刚开始一直不知道是这个原因,检查了好久。。。
后来加上下面这个就可以了
代码:
点击展开
key_pad.h key_pad.cpp main.cpp res.qrc CMakeLists.txt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #ifndef EX15_MYKEYPAD_KEY_PAD_H #define EX15_MYKEYPAD_KEY_PAD_H #include <QWidget> #include <QGridLayout> #include <QLcdNumber> #include <QList> #include <QPushButton> #include <QDebug> QT_BEGIN_NAMESPACE namespace Ui {class KeyPad ;} QT_END_NAMESPACE struct KeyInfo { QString text; int value; QString imagePath; }; enum KeyRole { Num0 = 0 , Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, DeleteBtn = 11 , ConfirmBtn = 12 }; enum PadMode { QssMode, ImageMode }; class KeyPad : public QWidget { Q_OBJECT public : explicit KeyPad (QWidget *parent = nullptr , PadMode mode = PadMode::QssMode) ; ~KeyPad () override ; public slots: void lcdChange () ; private : void init_keypad () ; void random_keypad () ; void loadStyleSheet () ; QLCDNumber *lcdShow; QWidget *padWidget; QGridLayout *padLayout; QList<QPushButton *> m_btns; QString m_currentInput; PadMode m_mode; }; #endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 #include "key_pad.h" #include <QBoxLayout> #include <QDateTime> #include <QRandomGenerator> #include <QFile> #include <QLatin1StringView> #define QSS_STYLE_IMAGE ":/styles/style_image.qss" #define QSS_STYLE_QSS ":/styles/style_qss.qss" namespace { const QList<KeyInfo> pads = { {"1" , 1 , ":/images/1.png" }, {"2" , 2 , ":/images/2.png" }, {"3" , 3 , ":/images/3.png" }, {"4" , 4 , ":/images/4.png" }, {"5" , 5 , ":/images/5.png" }, {"6" , 6 , ":/images/6.png" }, {"7" , 7 , ":/images/7.png" }, {"8" , 8 , ":/images/8.png" }, {"9" , 9 , ":/images/9.png" }, {"⌫" , 11 , ":/images/del.png" }, {"0" , 0 , ":/images/0.png" }, {"✓" , 12 , ":/images/ok.png" } }; } KeyPad::KeyPad (QWidget *parent, PadMode mode) : QWidget (parent), m_mode (mode) { this ->setGeometry (50 , 100 , 240 , 400 ); auto main_layout = new QVBoxLayout (this ); main_layout->setSpacing (0 ); main_layout->setContentsMargins (0 , 0 , 0 , 0 ); lcdShow = new QLCDNumber (this ); lcdShow->setDigitCount (6 ); lcdShow->display ("" ); lcdShow->setSegmentStyle (QLCDNumber::Flat); auto lcdContainer = new QWidget (this ); lcdContainer->setMinimumSize (240 , 80 ); lcdContainer->setMaximumSize (240 , 80 ); auto containerLayout = new QVBoxLayout (lcdContainer); if (m_mode == PadMode::ImageMode) { containerLayout->setContentsMargins (15 , 15 , 15 , 5 ); } else { containerLayout->setContentsMargins (0 , 0 , 0 , 0 ); } containerLayout->addWidget (lcdShow); padWidget = new QWidget (this ); padLayout = new QGridLayout (padWidget); padLayout->setContentsMargins (0 , 0 , 0 , 0 ); padLayout->setSpacing (0 ); init_keypad (); loadStyleSheet (); random_keypad (); main_layout->addWidget (lcdContainer); main_layout->addWidget (padWidget); main_layout->setStretch (0 , 1 ); main_layout->setStretch (1 , 4 ); } KeyPad::~KeyPad () {} void KeyPad::loadStyleSheet () { QString stylePath = (m_mode == PadMode::ImageMode) ? QSS_STYLE_IMAGE : QSS_STYLE_QSS; QFile file (stylePath) ; if (file.open (QFile::ReadOnly)) { this ->setStyleSheet (QLatin1String (file.readAll ())); file.close (); } } void KeyPad::init_keypad () { QPushButton *btn; for (int x = 0 ; x < 4 ; ++x) { for (int y = 0 ; y < 3 ; ++y) { int pos = x * 3 + y; btn = new QPushButton (padWidget); btn->setProperty ("value" , pads[pos].value); btn->setMinimumSize (80 , 80 ); btn->setMaximumSize (80 , 80 ); if (m_mode == 1 ) { btn->setIcon (QIcon (pads[pos].imagePath)); btn->setIconSize (QSize (50 , 50 )); } else { btn->setText (pads[pos].text); if (pads[pos].value >= DeleteBtn) { btn->setProperty ("btnType" , "func" ); } else { btn->setProperty ("btnType" , "num" ); } } connect (btn, &QPushButton::clicked, this , &KeyPad::lcdChange); m_btns.append (btn); } } } void KeyPad::random_keypad () { auto random = QRandomGenerator::global (); QList<int > indexes; for (int i = 0 ; i < pads.size (); ++i) { if (pads[i].value <= KeyRole::Num9) { indexes.append (i); } } std::shuffle (indexes.begin (), indexes.end (), *random); int pos_indes = 0 ; for (int x = 0 ; x < 4 ; ++x) { for (int y = 0 ; y < 3 ; ++y) { int pos = x * 3 + y; QPushButton *btn; if (pads[pos].value >= KeyRole::DeleteBtn) { btn = m_btns[pos]; } else { btn = m_btns[indexes[pos_indes]]; ++pos_indes; } padLayout->addWidget (btn, x, y, 1 , 1 ); } } } void KeyPad::lcdChange () { auto btn = qobject_cast <QPushButton *>(sender ()); if (btn == nullptr ) { return ; } int v = btn->property ("value" ).toInt (); if (v == KeyRole::DeleteBtn) { if (m_currentInput.isEmpty ()) { return ; } m_currentInput.chop (1 ); lcdShow->display (m_currentInput.isEmpty () ? "" : m_currentInput); } else if (v == KeyRole::ConfirmBtn) { m_currentInput.clear (); lcdShow->display ("" ); } else { if (m_currentInput.size () >= 6 ) { m_currentInput.clear (); } m_currentInput.append (QString::number (v)); lcdShow->display (m_currentInput); if (m_currentInput.size () == 6 ) { random_keypad (); } } }
1 2 3 4 5 6 7 8 9 10 11 12 #include <QApplication> #include <QWidget> #include "key_pad.h" int main (int argc, char *argv[]) { QApplication app (argc, argv) ; KeyPad win (nullptr , PadMode::QssMode) ; win.show (); return QApplication::exec (); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <RCC > <qresource prefix ="/img" > <file > images/num1.png</file > <file > images/num2.png</file > <file > images/num3.png</file > <file > images/num4.png</file > <file > images/num5.png</file > <file > images/num6.png</file > <file > images/num7.png</file > <file > images/num8.png</file > <file > images/num9.png</file > <file > images/delete.png</file > <file > images/num0.png</file > <file > images/ok.png</file > </qresource > </RCC >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 cmake_minimum_required (VERSION 3.16 )project (ex15_myKeyPad CXX)set (CMAKE_CXX_STANDARD 17 )set (CMAKE_CXX_STANDARD_REQUIRED ON )set (CMAKE_PREFIX_PATH "D:/09_SW/_Dev/Qt/6.5.3/mingw_64" )set (CMAKE_AUTOMOC ON )set (CMAKE_AUTOUIC ON )set (CMAKE_AUTORCC ON )find_package (Qt6 REQUIRED COMPONENTS Core Widgets)qt_standard_project_setup() qt_add_executable(ex15_myKeyPad main.cpp key_pad.cpp key_pad.h res.qrc ) target_link_libraries (ex15_myKeyPad PRIVATE Qt6::Core Qt6::Widgets)set_target_properties (ex15_myKeyPad PROPERTIES WIN32_EXECUTABLE OFF MACOSX_BUNDLE ON )
样式:
点击展开
style_qss.qss style_image.qss 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 KeyPad { background-color : #282726 ; } QLCDNumber { background-color : #1e1e1e ; color : #ebe9eb ; border : none; border-bottom : 3px solid #f99f2b ; } QPushButton[btnType="func" ] { background-color : #f99f2b ; font-family : 'Segoe UI' , Arial; font-size : 32px ; font-weight : bold; color : #ffffff ; border : 1px solid rgb (44 ,44 ,46 ); border-radius : 4px ; margin : 1px ; } QPushButton[btnType="func" ] :hover { background-color : #ffb04f ; } QPushButton[btnType="func" ] :pressed { background-color : #c67e20 ; } QPushButton[btnType="num" ] { background-color : #5e5d5c ; font-family : 'Segoe UI' , Arial; font-size : 28px ; font-weight : bold; color : #ebe9eb ; border : 1px solid rgb (44 ,44 ,46 ); border-radius : 4px ; margin : 1px ; } QPushButton[btnType="num" ] :hover { background-color : #6e6d6c ; } QPushButton[btnType="num" ] :pressed { background-color : #9f9e9e ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 KeyPad { background-color : #f4f7f6 ; } QLCDNumber { background-color : #2c2c2e ; color : #ffffff ; border : none; border-radius : 12px ; } QPushButton { background : transparent; border : none; } QPushButton:pressed { background-color : rgba (0 , 0 , 0 , 30 ); border-radius : 10px ; }
可选按键装饰
提供了两种按键装饰:图标和纯QSS
且分离了QSS代码到静态文件,通过文件读取来适配QSS样式,方便后续新增皮肤
style 下,style_qss.qss 和
style_image.qss
1 2 3 4 5 6 7 8 9 10 11 12 #define QSS_STYLE_IMAGE ":/styles/style_image.qss" #define QSS_STYLE_QSS ":/styles/style_qss.qss" void KeyPad::loadStyleSheet () { QString stylePath = (m_mode == PadMode::ImageMode) ? QSS_STYLE_IMAGE : QSS_STYLE_QSS; QFile file (stylePath) ; if (file.open (QFile::ReadOnly)) { this ->setStyleSheet (QLatin1String (file.readAll ())); file.close (); } }
代码结构优化
将 key_pad.cpp 中,静态的字符串定义在匿名命名空间中
通过枚举明确值的实际意义
构造函数新增默认形参,保证可选样式
已发布 Github: abinng/Qt-Modern-Keypad:
个人学习Qt时的做的一个小Demo