#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;
}

👉 一个函数干三件事:

  1. 读 LED 状态

  2. 改按钮文字

  3. 返回 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 的本质:

“当某个对象发生某件事时,自动调用另一个对象的某个函数”


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐