c++ QT 开发第二天,用ui按钮点亮实体led
为什么不 deletepushButton?pushButton的父对象是MainWindowQt 会自动释放所有子对象所以这里什么都不用写程序启动↓创建窗口↓创建按钮 + 居中↓绑定 LED 文件↓读取 LED 当前状态↓用户点击按钮↓读取当前状态↓写入相反状态 (0/1)↓更新按钮文字信号 / 槽。
#include "mainwindow.h"
#include <QDebug>
#include <QGuiApplication>
#include <QScreen>
#include <QRect>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
/* 获取屏幕的分辨率,Qt官方建议使用这
* 种方法获取屏幕分辨率,防上多屏设备导致对应不上
* 注意,这是获取整个桌面系统的分辨率
*/
QList <QScreen *> list_screen = QGuiApplication::screens();
/* 如果是ARM平台,直接设置大小为屏幕的大小 */
#if __arm__
/* 重设大小 */
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
/* 默认是出厂系统的LED心跳的触发方式,想要控制LED,
* 需要改变LED的触发方式,改为none,即无 */
system("echo none > /sys/class/leds/sys-led/trigger");
#else
/* 否则则设置主窗体大小为800x480 */
this->resize(800, 480);
#endif
pushButton = new QPushButton(this);
/* 居中显示 */
pushButton->setMinimumSize(200, 50);
pushButton->setGeometry((this->width() - pushButton->width()) /2 ,
(this->height() - pushButton->height()) /2,
pushButton->width(),
pushButton->height()
);
/* 开发板的LED控制接口 */
file.setFileName("/sys/devices/platform/leds/leds/sys-led/brightness");
if (!file.exists())
/* 设置按钮的初始化文本 */
pushButton->setText("未获取到LED设备!");
/* 获取LED的状态 */
getLedState();
/* 信号槽连接 */
connect(pushButton, SIGNAL(clicked()),
this, SLOT(pushButtonClicked()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::setLedState()
{
/* 在设置LED状态时先读取 */
bool state = getLedState();
/* 如果文件不存在,则返回 */
if (!file.exists())
return;
if(!file.open(QIODevice::ReadWrite))
qDebug()<<file.errorString();
QByteArray buf[2] = {"0", "1"};
/* 写0或1 */
if (state)
file.write(buf[0]);
else
file.write(buf[1]);
/* 关闭文件 */
file.close();
/*重新获取LED的状态 */
getLedState();
}
bool MainWindow::getLedState()
{
/* 如果文件不存在,则返回 */
if (!file.exists())
return false;
if(!file.open(QIODevice::ReadWrite))
qDebug()<<file.errorString();
QTextStream in(&file);
/* 读取文件所有数据 */
QString buf = in.readLine();
/* 打印出读出的值 */
qDebug()<<"buf: "<<buf<<endl;
file.close();
if (buf == "1") {
pushButton->setText("LED点亮");
return true;
} else {
pushButton->setText("LED熄灭");
return false;
}
}
void MainWindow::pushButtonClicked()
{
/* 设置LED的状态 */
setLedState();
}
一、这整个程序是干什么的?
👉 一句话概括:
用 Qt 做一个窗口,窗口中只有一个按钮
点击按钮 → 通过/sys/class/leds/.../brightness
控制开发板 LED 的亮 / 灭
二、整体结构(先有个全局概念)
MainWindow
│
├── QPushButton *pushButton // 界面上的按钮
├── QFile file // 指向 LED 亮度文件
│
├── 构造函数 MainWindow() // 创建界面、按钮、初始化
├── setLedState() // 设置 LED 亮/灭
├── getLedState() // 读取 LED 当前状态
└── pushButtonClicked() // 按钮点击槽函数
三、构造函数 MainWindow(最重要)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
1️⃣ 调用父类构造函数
: QMainWindow(parent)
-
MainWindow继承自QMainWindow -
这句是 初始化父类
-
parent一般是nullptr
2️⃣ 获取屏幕分辨率(Qt 官方推荐)
QList<QScreen *> list_screen = QGuiApplication::screens();
-
获取所有屏幕
-
防止多屏设备分辨率不一致
-
list_screen.at(0)是主屏幕
3️⃣ ARM 和 PC 平台区分(宏判断)
#if __arm__
ARM 平台(开发板)
this->resize(list_screen.at(0)->geometry().width(),
list_screen.at(0)->geometry().height());
👉 窗口大小 = 屏幕大小(全屏)
system("echo none > /sys/class/leds/sys-led/trigger");
🔴 非常关键的一句:
-
Linux 默认 LED 是 心跳闪烁
-
想手动控制 LED
-
必须把触发方式改成
none
非 ARM(PC 上调试)
#else
this->resize(800, 480);
#endif
👉 PC 上就固定窗口大小
4️⃣ 创建按钮(重点)
pushButton = new QPushButton(this);
-
动态创建按钮
-
this是父对象 → 自动内存管理 -
按钮会显示在主窗口中
5️⃣ 设置按钮大小和位置(居中)
pushButton->setMinimumSize(200, 50);
按钮最小尺寸:200 × 50
pushButton->setGeometry(
(this->width() - pushButton->width()) / 2,
(this->height() - pushButton->height()) / 2,
pushButton->width(),
pushButton->height()
);
👉 这段数学计算的目的只有一个:
让按钮在窗口正中间
6️⃣ 设置 LED 控制文件路径(Linux 核心)
file.setFileName("/sys/devices/platform/leds/leds/sys-led/brightness");
这个文件:
-
"0"→ LED 灭 -
"1"→ LED 亮
👉 Qt 本质上就是在读写 Linux 文件
7️⃣ 判断 LED 文件是否存在
if (!file.exists())
pushButton->setText("未获取到LED设备!");
-
如果不是在开发板上运行
-
或路径不对
-
按钮提示错误信息
8️⃣ 读取 LED 初始状态
getLedState();
-
启动程序时
-
自动读取 LED 是亮还是灭
-
同时更新按钮文字
9️⃣ 信号槽(Qt 核心机制)
connect(pushButton, SIGNAL(clicked()),
this, SLOT(pushButtonClicked()));
意思是:
按钮被点击 → 自动调用
pushButtonClicked()
四、析构函数(为什么是空的)
MainWindow::~MainWindow()
{
}
为什么不 delete pushButton?
👉 因为:
-
pushButton的父对象是MainWindow -
Qt 会 自动释放所有子对象
-
所以这里什么都不用写
五、setLedState() —— 设置 LED 状态
void MainWindow::setLedState()
{
1️⃣ 先读当前状态
bool state = getLedState();
-
true→ 当前是亮 -
false→ 当前是灭
👉 点击按钮后是 取反操作
2️⃣ 文件不存在直接返回
if (!file.exists())
return;
3️⃣ 打开 LED 文件
file.open(QIODevice::ReadWrite);
-
必须
ReadWrite -
sysfs 文件读写都走这里
4️⃣ 写入 0 / 1
QByteArray buf[2] = {"0", "1"};
if (state)
file.write(buf[0]); // 原来亮 → 写0 → 关灯
else
file.write(buf[1]); // 原来灭 → 写1 → 开灯
👉 按钮就是一个“开关”
5️⃣ 关闭文件 + 重新读取状态
file.close();
getLedState();
六、getLedState() —— 获取 LED 状态
bool MainWindow::getLedState()
{
1️⃣ 文件检查 + 打开
if (!file.exists())
return false;
2️⃣ 读取文件内容
QTextStream in(&file);
QString buf = in.readLine();
-
buf可能是"0"或"1"
3️⃣ 打印调试信息
qDebug() << "buf: " << buf;
4️⃣ 根据值更新按钮文字
if (buf == "1") {
pushButton->setText("LED点亮");
return true;
} else {
pushButton->setText("LED熄灭");
return false;
}
👉 一个函数干三件事:
-
读 LED 状态
-
改按钮文字
-
返回 LED 状态
七、pushButtonClicked() —— 槽函数
void MainWindow::pushButtonClicked()
{
setLedState();
}
非常简单:
按钮点一下 → 切换 LED 状态
八、完整运行流程(面试级总结)
程序启动
↓
创建窗口
↓
创建按钮 + 居中
↓
绑定 LED 文件
↓
读取 LED 当前状态
↓
用户点击按钮
↓
读取当前状态
↓
写入相反状态 (0/1)
↓
更新按钮文字
信号 / 槽
一、先忘掉“信号 / 槽”这两个词
我们先把它们换成你熟悉的东西:
| Qt 名字 | 用“人话”理解 |
|---|---|
| 信号 (signal) | 某件事发生了 |
| 槽 (slot) | 这件事发生后要执行的函数 |
👉 信号 = 事件通知
👉 槽 = 事件处理函数
二、用“按钮”做最直观的类比
想象现实中的一个按钮:
你按下按钮
↓
按钮告诉系统:我被按了!
↓
系统执行你写好的代码
在 Qt 里:
clicked() ←—— 信号(按钮被点了)
pushButtonClicked() ←—— 槽(你要干什么)
三、你这行代码到底在干什么?(重点)
connect(pushButton, SIGNAL(clicked()),
this, SLOT(pushButtonClicked()));
我帮你逐个翻译成中文:
当 pushButton 这个按钮
发出 clicked() 这个信号时
就调用 this(MainWindow)里的 pushButtonClicked() 函数
一句话白话版:
“按钮被点 → 自动执行 pushButtonClicked()”
四、拆开 connect 的四个参数
connect( A, B,
C, D );
| 参数 | 你的代码 | 人话解释 |
|---|---|---|
| A | pushButton | 谁发出信号 |
| B | clicked() | 发生了什么事 |
| C | this | 谁来处理 |
| D | pushButtonClicked() | 具体干什么 |
五、为什么不直接在按钮里写代码?
你可能会想:
为什么不写:
pushButton->clicked() { setLedState(); }
原因很重要:
Qt 的设计思想是:
“谁触发” 和 “干什么” 完全分离
好处:
-
按钮不用知道 LED 是什么
-
LED 不关心是按钮、定时器还是网络控制
-
程序结构非常清晰
六、槽函数到底是什么?
你这个槽函数:
void MainWindow::pushButtonClicked()
{
setLedState();
}
说白了:
-
它就是一个 普通成员函数
-
只是被 Qt 在合适的时候自动调用
👉 没有魔法
👉 没有线程
👉 没有中断
七、没有信号槽,程序会发生什么?
你现在的程序逻辑是:
程序启动
↓
什么都不会发生
↓
除非你点击按钮
👉 点击按钮本身 什么都不会做
只有因为这句:
connect(...)
Qt 才知道:
“哦,按钮点了要去干这件事”
八、换一个你熟悉的“嵌入式”类比(关键)
你是电子 / 嵌入式方向的,这样理解更快 👇
Qt 信号槽 ≈ STM32 中断回调
| STM32 | Qt |
|---|---|
| 按键中断 | clicked() 信号 |
| EXTI 触发 | 信号发出 |
| 中断服务函数 | 槽函数 |
HAL_GPIO_EXTI_Callback() |
pushButtonClicked() |
👉 中断来了 → 自动执行回调
👉 信号发了 → 自动执行槽
九、你可以做的最简单实验(强烈建议)
把槽函数改成这样:
void MainWindow::pushButtonClicked()
{
qDebug() << "按钮被点击了!";
}
运行后:
-
不点按钮 → 什么都不会打印
-
点按钮 → 控制台出现文字
👉 这一步你一定能“顿悟”
十、用一句话彻底记住信号槽
connect 的本质:
“当某个对象发生某件事时,自动调用另一个对象的某个函数”
更多推荐
所有评论(0)