面试-Qt应用开发.md 51 KB


title: 面试-Qt应用开发 tags: [嵌入式Linux, 面试, Qt, C++, 面向对象, 类与对象, 继承, 重载, 多态, 虚函数, 命名空间, Qt Creator, qmake, ui文件, 信号与槽, connect, Q_OBJECT, 控件, QPushButton, QComboBox, QLineEdit, 布局, QBoxLayout, QGridLayout, 模型视图, QStandardItemModel, QListView, QTableView, QFile, QTextStream, QPainter, QChart, QThread, QMutexLocker, QTcpSocket, QTcpServer, QUdpSocket, QMediaPlayer, QSqlDatabase, QSqlTableModel, SQLite, 交叉编译, linuxfb, eglfs, I.MX6U] created: 2026-09-18 updated: 2026-09-18

pdf_ref: "《I.MX6U嵌入式Qt开发指南V1.2》第一章~第二十八章:在Ubuntu下编写C++、C++基础、初识Qt、使用Qt Designer开发、Qt信号与槽、Qt Creator使用技巧、Qt控件、文本读写、绘图与图表、多线程、网络编程、多媒体、数据库、I.MX6U Qt开发 ~ 视频监控项目"

面试-Qt应用开发

💡 关联知识:[[05-Qt应用开发/01-C++基础与面向对象]]、[[05-Qt应用开发/02-Qt入门与项目结构]]、[[05-Qt应用开发/03-信号与槽]]、[[05-Qt应用开发/04-Qt控件详解]]、[[05-Qt应用开发/05-布局容器与模型视图]]、[[05-Qt应用开发/06-文本绘图与图表]]、[[05-Qt应用开发/07-多线程网络多媒体与数据库]]、[[05-Qt应用开发/08-嵌入式Qt交叉编译与项目实战]];延伸阅读:[[编程学习项目/使用Qt和C++构造UI界面/1. Qt概述]]、[[实际项目开发文档/加解密客户端(Qt+LibreSSL)/1.项目源代码]]

覆盖《I.MX6U嵌入式Qt开发指南》全书 8 大主题、40 道题。每题含「答案要点 / 详细解答(代码、对比表)/ 2 条追问」,可直接用于复习、查询与面试自测。


一、C++基础(5 题)

Q1.1 C++ 相比 C 有哪些新特性?cin/coutscanf/printf 有何区别?命名空间有什么用?

答案要点:新增 bool 类型、变量可随用随定义并支持直接初始化;cin/cout 替代 scanf/printf,无需指定格式符;命名空间用于解决名字冲突。

详细解答

  • C++ 可直接 int x(100); 初始化,变量不必集中在函数开头定义。
  • 输入输出用 cin >> x >> y;cout << x << y << endl;endl 等价于换行 \n,但 endl 会刷新缓冲。效率上 scanf/printf 更高,便利性上 cin/cout 更好。注意变量间要用 << 而非逗号。
  • 命名空间:标准库名字都加 std:: 前缀,using namespace std; 后可直接写 vectorcout。也能自定义:

    namespace A {
    int x = 1;
    void fun() { cout << "A namespace" << endl; }
    }
    using namespace A;
    int main() {
    fun();          /* 已声明 using,可直接调用 */
    A::x = 3;       /* :: 表示作用域 */
    A::fun();
    return 0;
    }
    
  • 注意 #include <iostream> 是标准流(无 .h,需命名空间);#include <iostream.h> 是非标准 C 库。

追问

  1. std::cin 中的 :: 是什么运算符?(作用域运算符,表示“std 命名空间下的”)
  2. 什么时候必须写全名而不用 using namespace?(库或公司自定义命名空间存在同名冲突时)

Q1.2 什么是类和对象?栈中与堆中实例化对象有何区别?构造函数和析构函数有什么特点?

答案要点:类是对事物的抽象,对象是类的实例;栈上对象自动释放,堆上对象用 new/delete 手动管理;构造在实例化时自动调用一次,析构在生命周期结束时自动调用。

详细解答

class Dog {
public:
    string name;
    int age;
    void run() { cout << name << " " << age << endl; }
};

Dog dog1;              /* 栈中实例化,函数结束自动析构 */
dog1.name = "旺财"; dog1.age = 2;  dog1.run();

Dog *dog2 = new Dog(); /* 堆中实例化,需手动 delete */
dog2->name = "富贵";    dog2->age = 1; dog2->run();
delete dog2; dog2 = NULL;

访问限定符:public(公有)、private(私有)、protected(保护),默认 private

构造函数特点:与类名同名、可重载、没有返回类型(void 也不行)、实例化时自动调用且仅一次。析构函数特点:~类名()、无参数、无返回值、生命周期结束时释放资源。类内实现或类外 :: 实现均可。

追问

  1. 不写构造函数和析构函数会怎样?(编译器会生成空的默认版本)
  2. this 指针是什么?(指向当前实例对象的指针,只能在成员函数使用,成员函数默认首参为 T * const this

Q1.3 C++ 有哪几种继承方式?protected 继承后基类成员在派生类中的权限如何变化?

答案要点:public / protected / private 三种;权限按“取更严格者”规则变化;默认为 private。

详细解答

继承方式 基类 public 基类 protected 基类 private
public public protected 不可直接访问
protected protected protected 不可直接访问
private private private 不可直接访问
class Animal { public: string color; int weight; };
class Dog : public Animal { public: string name; int age; void run(); };
/* Dog 的实例可直接使用继承来的 color / weight */

继承达到代码重用、提高效率的目的。Qt 中大量通过继承来重写/扩展已有类。

追问

  1. 基类私有成员在派生类中能直接访问吗?(不能,但可通过基类公有/保护成员间接访问)
  2. 一个类能继承多个基类吗?(可以,多继承)

Q1.4 什么是函数重载和运算符重载?哪些运算符不能重载?

答案要点:同一作用域内同名但参数列表不同的函数;运算符重载本质是函数重载,形式为 operator 运算符(参数表).::sizeof?: 等不能重载。

详细解答

函数重载:参数个数、类型或顺序必须不同,仅返回类型不同不能构成重载

class Dog {
public:
    void getWeight(int w)    { cout << w << "kG" << endl; }
    void getWeight(double w) { cout << w << "kG" << endl; }
};

运算符重载:

class Dog {
public:
    int weight;
    Dog operator+(const Dog &d) {      /* 重载 + */
        Dog dog;
        dog.weight = this->weight + d.weight;
        return dog;
    }
};

不可重载:成员访问 .、成员指针访问 .*->*、域运算符 ::sizeof、条件运算符 ?:、预处理符号 #

追问

  1. 为什么要重载运算符?(让自定义类型也能像基本类型一样使用 +== 等,形式统一)
  2. 重载时对象属性如何访问?(通过 this,因为对象是作为参数传入的)

Q1.5 什么是多态?形成多态的三个条件是什么?虚函数与纯虚函数有何区别?

答案要点:同一调用根据对象实际类型执行不同函数;条件为继承、同名虚函数、基类指针/引用调用;虚函数必须有实现,纯虚函数 =0 无实现、使类成为抽象类。

详细解答

形成多态必须具备三个条件:① 存在继承关系;② 继承关系中有同名虚函数(基类用 virtual 声明);③ 存在基类类型的指针或引用,通过它调用虚函数。

class Animal { public: virtual void run() { cout << "Animal" << endl; } };
class Dog : public Animal { public: void run() { cout << "Dog" << endl; } };
class Cat : public Animal { public: void run() { cout << "Cat" << endl; } };

Animal *animal;          /* 基类指针,未实例化 */
Dog dog; Cat cat;
animal = &dog; animal->run();   /* 输出 Dog */
animal = &cat; animal->run();   /* 输出 Cat */
  • 虚函数virtual ReturnType Fun(Param),必须实现;使编译器动态绑定(后期绑定)。
  • 纯虚函数virtual void funtion1() = 0;,没有实现,用来规范派生类接口;含纯虚函数的类是抽象类,不能实例化,只能作接口。

追问

  1. 去掉 virtual 会怎样?(静态链接到基类函数,调用基类自己的 run(),多态失效)
  2. QWidgetpaintEvent() 为什么能被子类重写?(它在基类中定义为虚函数,这正是多态的典型应用)

二、Qt入门与项目结构(5 题)

Q2.1 什么是 Qt?Qt 与 Qt Creator 是什么关系?Qt C++ 与 QML 如何选择?

答案要点:Qt 是跨平台 C++ 开发库(不止 GUI);Qt Creator 是 IDE;Qt C++ 功能强、成熟,QML 适合做炫酷界面,二者常结合使用。

详细解答:Qt 除了控件、布局、交互,还内置多线程、数据库、图像/音视频、网络、文件等模块;支持 Windows/Linux/Unix/Android/iOS/QNX/VxWorks 等平台,一份代码稍作修改即可跨平台运行。Qt 还有 Python、Ruby 等绑定。Qt Creator 是官方 IDE,负责编辑、编译、调试、UI 设计。

Qt C++ 效率更高、底层功能突出;QML 是描述性语言,配合 Qt Quick 做移动端风格的界面,但底层(文件读写、图像处理、多线程通信)仍靠 C++。低版本(Qt5.6 以下)QML 不够成熟,Qt5.7 以上更完善。本教程主打 Qt C++

追问

  1. “跨平台”对 Qt 意味着什么?(同一份源码在不同平台用不同套件编译即可运行)
  2. 嵌入式里为什么常用 QWidget 作为基类?(它不带标题栏/状态栏,适合全屏界面)

Q2.2 一个 Qt Widgets 工程由哪些文件组成?三种基类 QMainWindow/QWidget/QDialog 如何选?

答案要点.pro(工程)、.h(声明)、.cpp(实现)、.ui(界面);QMainWindow 带菜单/工具栏/状态栏,QWidget 是界面基类且最“轻”,QDialog 是对话框。

详细解答

文件 作用
*.pro 项目管理文件,增删文件时 Qt Creator 自动维护,也可手动加配置
*.h 类的声明;用到信号的类必须有 Q_OBJECT
*.cpp 类的实现
*.ui XML 描述的界面文件,编译时生成 ui_*.h

基类选择:

  • QMainWindow:主窗口,自带菜单栏、工具栏、状态栏,适合嵌套窗口程序;
  • QWidget:可视界面类的基类,QMainWindow 由它继承而来,功能更少、更轻,嵌入式常选它(不要标题栏/状态栏);
  • QDialog:对话框,较少作为主基类。

    #include "mainwindow.h"
    #include <QApplication>
    int main(int argc, char *argv[]) {
    QApplication a(argc, argv);   /* 每个 Qt 程序必须有一个 */
    MainWindow w;
    w.show();
    return a.exec();              /* 进入事件循环 */
    }
    

追问

  1. 为什么 Qt 程序的 main() 最后是 a.exec()?(把控制权交给 Qt,进入事件循环处理用户/系统事件)
  2. QMainWindowQWidget 的继承关系?(QMainWindow 继承自 QWidget

Q2.3 解释 .pro 文件中常见条目的含义。

答案要点QT += 引入模块,greaterThan(...) 按 Qt 大版本追加模块,CONFIG += c++11 用 C++11,TARGET 指定可执行文件名,SOURCES/HEADERS/FORMS 管理文件。

详细解答

QT       += core gui                              # 默认模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets   # Qt5 需加 widgets
CONFIG   += c++11
DEFINES  += QT_DEPRECATED_WARNINGS
TARGET   = 01_led                                 # 不写则默认工程名
SOURCES += main.cpp mainwindow.cpp
HEADERS += mainwindow.h
FORMS   += mainwindow.ui
QT       += serialport                            # 串口模块
QT       += serialbus                             # CAN 模块
QT       += charts                                # 图表模块
QT       += multimedia                            # 多媒体模块
QT       += sql                                   # 数据库模块
QT       += network                               # 网络模块

第 1 行 core 与 gui 是默认设置;部署规则 qnx: ... / else: unix:!android: ... 用于指定安装路径。

追问

  1. 要用 Qt 串口需要在 .pro 加什么?(QT += core gui serialport
  2. .pri 文件是什么?(语法同 .pro,可被 include() 引入,便于复用与拆分大型工程)

Q2.4 .ui 文件是如何被程序使用的?ui_mainwindow.hsetupUi()Ui::MainWindow 各是什么?

答案要点.ui 由设计器生成 XML,编译时生成 ui_mainwindow.hUi::MainWindow 是界面对象类,setupUi(this) 负责把界面控件实例化并挂到主窗口。

详细解答

/* mainwindow.h */
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }     /* 前置声明,注意与下面的 MainWindow 不同 */
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
    Ui::MainWindow *ui;                /* 指向界面对象的指针 */
};

/* mainwindow.cpp */
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);                 /* 界面初始化,控件以 this 为父对象 */
}
MainWindow::~MainWindow() {
    delete ui;                         /* new 且无父对象的对象需手动 delete */
}

ui_mainwindow.h构建后才生成的(位于 build 目录),所以使用 UI 设计的信号槽连接,需要编译后才能在 setupUi() 里看到。

追问

  1. 为什么使用 ui-> 里控件必须写在 setupUi(this) 之后?(该调用才完成控件初始化)
  2. Q_OBJECT 宏的作用?(Qt 元对象系统的基础,凡用到信号槽的类都要加)

Q2.5 用 Qt Designer(UI 文件)开发和纯代码开发各有什么优缺点?Qt Creator 有哪些实用技巧?

答案要点:Designer 直观快捷但控件多时难管理、信号槽连接不直观;纯代码利于管理与审查、能锻炼布局能力;Qt Creator 帮助文档和快捷键很实用。

详细解答

  • Designer 优点:拖拽布局、直观快捷;缺点:控件多时不易管理,必须打开 ui 文件加控件,信号槽连接要么编译后在 setupUi() 看、要么进设计器看,不直观。
  • 纯代码优点:便于代码管理与版本控制,布局与逻辑一目了然;缺点:需要熟悉各控件 API。
  • 本教程从第五章起统一用代码绘制界面。

Qt Creator 技巧:左侧“帮助”可查类/方法/信号槽用法;Ctrl + N 新建项目;Ctrl + R 编译并运行;Ctrl + Shift + T 查看类的继承关系;F1 查看光标处符号的帮助;按住 Ctrl 点击符号跳转到定义。

追问

  1. 无 UI 文件时如何设置窗口大小?(this->resize(800, 480);setGeometry(0,0,800,480)
  2. 嵌入式常用的分辨率是多少?(本教程统一 800×480)

三、信号与槽(5 题)

Q3.1 什么是信号与槽?connect() 的基本语法是什么?SIGNAL/SLOT 宏的作用?

答案要点:信号是特定情况下发射的事件,槽是响应函数;QObject::connect(sender, SIGNAL(sig()), receiver, SLOT(slot()));两个宏把函数签名转成字符串。

详细解答:信号(Signal)是特定情况下被发射的事件,如按钮的 clicked()、下拉框的 currentIndexChanged()。槽(Slot)是对信号响应的函数,本质是普通 C++ 函数,可定义在 public/protected/private 下,可带参数,也能被直接调用;特殊之处在于能与信号关联,信号发射时自动执行。

connect(sender, SIGNAL(clicked()), receptor, SLOT(close()));
connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));

connect()QObject 的静态函数,QObject 是所有 Qt 类的基类,故常省略限定符。SIGNAL/SLOT 把信号和槽的参数转换为相应字符串。带参时只需写参数类型,可省略参数名。

追问

  1. connect 中发送者与接收者的顺序能颠倒吗?(不能,sender 与信号在一起,receiver 与槽在一起)
  2. 信号与槽的参数必须完全一致吗?(至少信号参数不能少于槽参数,否则编译或运行错误)

Q3.2 信号与槽有哪些连接规则?

答案要点:一个信号可连多个槽、多个信号可连同一个槽、一个信号可连另一个信号;槽按连接顺序执行;使用的类必须加 Q_OBJECT;信号发射后槽立即执行,全部槽执行完才继续。

详细解答

/* 一对多:按连接顺序依次执行 */
connect(pushButton, SIGNAL(clicked()), this, SLOT(hide()));
connect(pushButton, SIGNAL(clicked()), this, SLOT(close()));

/* 多对一:任一按钮点击都关闭 */
connect(pushButton_2, SIGNAL(clicked()), this, SLOT(close()));
connect(pushButton_3, SIGNAL(clicked()), this, SLOT(close()));

/* 信号连信号:一个信号发射会触发另一个信号 */
connect(pushButton, SIGNAL(objectNameChanged(QString)),
        this, SIGNAL(windowTitleChanged(QString)));

要点:严格情况下参数个数与类型需一致;类中必须加 Q_OBJECT 宏;信号发射时槽立即执行,像普通函数调用一样,全部槽完成才执行发射点之后的代码。

追问

  1. 不写 Q_OBJECT 会怎样?(信号槽机制无法工作,编译或链接报错)
  2. 一个信号连多个槽时,执行顺序如何?(按建立连接的先后顺序)

Q3.3 如何在项目中自定义信号?为什么信号只需声明?

答案要点:在类中 signals: 下声明,只需声明无需实现;使用时用 emit 发射;信号无返回类型。

详细解答

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
signals:
    /* 声明一个信号,只需声明,无需定义 */
    void pushButtonTextChanged();
};

信号由 moc 自动生成实现,因此不能也不必自己写函数体;信号没有 public/protected/private 概念,名称建议贴合含义(如“按钮文本改变后”)。

追问

  1. 发射信号用什么关键字?(emit,如 emit pushButtonTextChanged();
  2. 信号能有返回值吗?(不能)

Q3.4 如何在项目中自定义槽?槽函数有什么特点?

答案要点:在类中 slots: 下声明,并在 cpp 中实现(必须实现);槽可以是任意成员函数、普通全局函数或静态函数;参数和返回值要与信号匹配。

详细解答

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
public slots:
    void changeButtonText();     /* 声明槽 */
    void pushButtonClicked();
private:
    QPushButton *pushButton;
};

/* 实现槽:槽里改变按钮文本 */
void MainWindow::changeButtonText() {
    pushButton->setText("被点击了!");
}
/* 实现按钮点击槽:用 emit 发射自定义信号 */
void MainWindow::pushButtonClicked() {
    emit pushButtonTextChanged();
}

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    this->resize(800, 480);
    pushButton = new QPushButton(this);
    pushButton->setText("我是一个按钮");
    connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
    connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));
}

槽特点:① 可以是任何成员函数、普通全局函数、静态函数;② 与信号的参数和返回值要一致。本例流程:点击按钮 → pushButtonClicked() → 发射 pushButtonTextChanged()changeButtonText() 响应,按钮文本变为“被点击了!”。

追问

  1. 槽函数只声明不实现行吗?(不行,编译报错)
  2. QPushButton 还有哪些构造重载?(QPushButton(const QString &text, QWidget *parent)QPushButton(const QIcon &icon, const QString &text, QWidget *parent)

Q3.5 如何断开信号与槽?disconnect() 有哪几种常用形式?

答案要点:用 disconnect(),可断开某对象的全部连接、某信号的全部连接、或与指定接收者的连接。

详细解答

/* 断开 myObject 的一切连接 */
disconnect(myObject, 0, 0, 0);        /* 等价 myObject->disconnect(); */

/* 断开连接到 mySignal() 的全部连接 */
disconnect(myObject, SIGNAL(mySignal()), 0, 0);  /* 等价 myObject->disconnect(SIGNAL(mySignal())); */

/* 与指定接收者断开 */
disconnect(myObject, 0, myReceiver, 0);          /* 等价 myObject->disconnect(myReceiver); */

初学者一般很少主动断开连接;但动态创建/销毁对象时要注意避免悬空连接。

追问

  1. Qt4 与 Qt5 的 connect 写法有何区别?(Qt5 支持函数指针语法,编译期检查,更安全;Qt4 依赖 SIGNAL/SLOT 字符串,运行期解析)
  2. 对象被销毁后其连接会自动清理吗?(QObject 析构时会自动断开相关连接)

四、控件(5 题)

Q4.1 Qt 的按钮类控件有哪些?各自常用信号是什么?

答案要点QPushButton(clicked)、QToolButton(带图标/菜单)、QRadioButton(toggled,互斥)、QCheckBox(stateChanged,三态)、QCommandLinkButtonQDialogButtonBox

详细解答

控件 特点 常用信号
QPushButton 普通按钮,可显示文本/图标 clicked()
QToolButton 工具按钮,常用于工具栏,可带下拉菜单 clicked()
QRadioButton 单选按钮,同组互斥 toggled(bool)
QCheckBox 复选按钮,支持三态 stateChanged(int)
QCommandLinkButton 命令链接按钮,Vista 风格 clicked()
QDialogButtonBox 对话框标准按钮集合 clicked(QAbstractButton*)

常用方法:setText()setIcon()setCheckable()setChecked()setEnabled()setMinimumSize()

追问

  1. setCheckable(true) 有什么效果?(按钮变为可选中/取消的切换状态,如“开始/关闭”)
  2. 单选按钮如何分组保证互斥?(放在同一父容器或用 QButtonGroup

Q4.2 输入类控件有哪些?QComboBoxQLineEditQSpinBox 常用方法是什么?

答案要点QComboBoxQFontComboBoxQLineEditQTextEditQPlainTextEditQSpinBoxQDoubleSpinBoxQDateTimeEditQDialQScrollBarQSliderQKeySequenceEdit

详细解答

控件 用途 常用方法
QComboBox 下拉选择 addItem()addItems()setCurrentIndex()currentText()
QLineEdit 单行输入 setText()text()setEchoMode()setPlaceholderText()
QTextEdit 多行富文本 setPlainText()toPlainText()insertPlainText()
QPlainTextEdit 多行纯文本 setPlainText()appendPlainText()
QSpinBox 整数微调 setRange()setValue()value()valueChanged(int)
QDoubleSpinBox 浮点微调 同上
QDateTimeEdit 日期时间 setDateTime()dateTime()
QDial / QSlider 旋钮 / 滑块 setValue()valueChanged(int)

追问

  1. QLineEdit 密码框怎么实现?(setEchoMode(QLineEdit::Password)
  2. QComboBox 如何给选项附带数据?(addItem(text, userData) + currentData()

Q4.3 显示类控件有哪些?QLabel 如何显示图片?QProgressBar 如何更新进度?

答案要点QLabelQCalendarWidgetQLCDNumberQProgressBarQFrameQTextBrowserQGraphicsViewQLabel::setPixmap() 显示图片,setScaledContents(true) 自适应;QProgressBar::setValue() 更新进度。

详细解答

QLabel *label = new QLabel(this);
label->setPixmap(QPixmap(":/images/openedv.png"));
label->setScaledContents(true);      /* 图片随控件缩放 */

QProgressBar *bar = new QProgressBar(this);
bar->setRange(0, 100);
bar->setValue(50);                    /* 更新到 50% */

QLCDNumber 显示数字风格的数值,QCalendarWidget 提供日历,QTextBrowser 支持富文本浏览(带链接),QGraphicsView 用于图形场景显示。

追问

  1. QLabel 的自动换行怎么设置?(setWordWrap(true)
  2. 图像资源为什么要放进 .qrc?(编译进可执行文件,运行时用 :/ 前缀访问,避免路径问题)

Q4.4 容器类控件有哪些?QTabWidgetQStackedWidgetQListWidget 如何配合?

答案要点QGroupBoxQScrollAreaQToolBoxQTabWidgetQStackedWidgetQMdiAreaQDockWidgetQStackedWidget 常与 QListWidget 通过 setCurrentIndex(int) 槽联动。

详细解答

/* 左边列表,右边堆叠页面,点击列表切换页面 */
stackedWidget = new QStackedWidget();
listWidget = new QListWidget();
connect(listWidget, SIGNAL(currentRowChanged(int)),
        stackedWidget, SLOT(setCurrentIndex(int)));

/* QTabWidget:直接 addTab 添加带标签页 */
tabWidget->addTab(pageWidget, "蓝牙聊天");
tabWidget->addTab(pageWidget2, "蓝牙列表");
tabWidget->setCurrentIndex(0);

QGroupBox 用于给一组控件加标题边框;QScrollArea 提供滚动区域(内容大于可视区时使用);QToolBox 是抽屉式分组;QTabWidget 是标签页;QStackedWidget 是“同一时间只显示一页”的堆叠容器。

追问

  1. QStackedWidgetQTabWidget 的本质关系?(QTabWidget 内部基于堆叠布局,多了标签栏)
  2. 内容超出窗口时用什么容器?(QScrollArea

Q4.5 “基于项”的控件与“基于模型”的控件有什么区别?各举三例。

答案要点:基于模型的控件(QListView/QTreeView/QTableView/QColumnView)由外部 Model 提供数据,适合大数据与多视图共享;基于项的控件(QListWidget/QTreeWidget/QTableWidget)自带内部模型,直接用 addItem 等操作,简单方便。

详细解答

维度 基于模型(View) 基于项(Widget)
代表 QListView、QTreeView、QTableView QListWidget、QTreeWidget、QTableWidget
数据来源 外部 Model(如 QStandardItemModel) 内部自带模型
操作方式 setModel() addItem()setItem()
适用 大数据、复杂结构、多视图共享 中小数据、快速开发

QListWidget 继承 QListViewQTreeWidget 继承 QTreeViewQTableWidget 继承 QTableView——即“控件版”是“视图版”的便捷封装。

追问

  1. 一般处理大数据用哪种?(基于模型的 View,避免为每项创建对象)
  2. 列表项如何放置自定义控件?(QListWidget::setItemWidget(item, widget)

五、布局与模型视图(5 题)

Q5.1 为什么推荐用布局管理器而不是绝对定位?Qt 有哪些布局类?

答案要点:布局能随窗口大小自动调整、适配不同分辨率、便于维护;主要有 QBoxLayout(含 QHBoxLayout/QVBoxLayout)、QGridLayoutQFormLayout

详细解答:绝对定位(setGeometry/move)写死后,窗口缩放或换屏幕分辨率时界面就错乱;嵌入式设备分辨率各异,布局管理是必须。常见布局:

布局 排列方式 典型场景
QHBoxLayout 水平 一排按钮、标签+输入框
QVBoxLayout 垂直 上下分区
QGridLayout 网格 串口/CAN 参数表
QFormLayout 两列表单 登录、设置表单

追问

  1. 嵌入式里界面的基准分辨率教材用多少?(800×480)
  2. 布局能嵌套吗?(能,布局内可再 addLayout,实现复杂界面)

Q5.2 写出布局的常用 API。QSpacerItem 有什么用?

答案要点addWidget()addLayout()addStretch()setContentsMargins()setSpacing()setAlignment()QSpacerItem 在布局中提供空白,用于撑开或对齐。

详细解答

QVBoxLayout *vboxLayout = new QVBoxLayout();
vboxLayout->addWidget(textBrowser);
vboxLayout->addWidget(textEdit);
vboxLayout->setContentsMargins(0, 0, 0, 0);   /* 左上右下外边距 */
vboxLayout->setSpacing(10);                    /* 控件间距 */
mainWidget->setLayout(vboxLayout);

/* 网格布局:把标签放 (0,i),下拉框放 (1,i) */
gridLayout->addWidget(label[i], 0, i);
gridLayout->addWidget(comboBox[i], 1, i);

/* 空间间隔 */
QSpacerItem *hSpacer = new QSpacerItem(10, 10,
        QSizePolicy::Expanding, QSizePolicy::Minimum);
hBoxLayout->addSpacerItem(hSpacer);

QSpacerItem 继承 QLayoutItem,含水平/垂直间隔,用来把控件挤向一侧或在控件之间留白。

追问

  1. addStretch()QSpacerItem 的区别?(前者是快捷方式,本质也是插入可伸缩间隔)
  2. setContentsMarginssetSpacing 分别控制什么?(布局四周外边距 / 相邻控件间距)

Q5.3 Qt 模型/视图框架由哪几部分组成?QStandardItemModel 如何设置表头与数据?

答案要点:Model(数据)、View(显示)、Delegate(渲染与编辑)三部分;QStandardItemModel 是通用模型,setHorizontalHeaderLabels() 设表头,appendRow() 逐行添加。

详细解答

QStandardItemModel *model = new QStandardItemModel();
QStringList labels;
labels << "姓名" << "年龄" << "性别";
model->setHorizontalHeaderLabels(labels);        /* 设置列数与表头文字 */

/* 逐行添加数据 */
QList<QStandardItem *> row;
row << new QStandardItem("啊万") << new QStandardItem("27") << new QStandardItem("男");
model->appendRow(row);

tableView->setModel(model);                       /* 视图绑定模型 */

好处是数据与显示分离:同一份 Model 可被 List/Tree/Table 多种 View 共享,也可加 QSortFilterProxyModel 做排序过滤。

追问

  1. Delegate 的作用?(决定每一项如何绘制、如何进入编辑状态,如自定义进度条单元格)
  2. 视图只读怎么设置?(setEditTriggers(QAbstractItemView::NoEditTriggers)

Q5.4 QListViewQTreeViewQTableView 如何填充数据?

答案要点:三者都继承 QAbstractItemView,通过 setModel() 绑定模型;列表用 QStringListModelQStandardItemModel,树/表用 QStandardItemModelappendRow/setItem

详细解答

/* 列表视图:用字符串列表模型 */
QStringList list; list << "A" << "B" << "C";
QStringListModel *stringListModel = new QStringListModel(list);
QListView *listView = new QListView;
listView->setModel(stringListModel);
listView->setViewMode(QListView::IconMode);   /* 图标模式 */

/* 表视图:先设表头,再用 setItem(row, col, item) */
QStandardItemModel *tableModel = new QStandardItemModel();
tableModel->setHorizontalHeaderLabels(QStringList() << "编号" << "姓名");
for (int r = 0; r < 3; r++)
    for (int c = 0; c < 2; c++)
        tableModel->setItem(r, c, new QStandardItem(QString::number(r * 2 + c)));
tableView->setModel(tableModel);

/* 树视图:一级标题 appendRow,子项用父项 appendRow */
QStandardItemModel *treeModel = new QStandardItemModel(treeView);
treeModel->setHorizontalHeaderLabels(QStringList() << "分类" << "说明");
QStandardItem *root = new QStandardItem("传感器");
root->appendRow(new QStandardItem("AP3216C"));
treeModel->appendRow(root);
treeView->setModel(treeModel);

追问

  1. QTreeView 如何展开全部节点?(expandAll()
  2. QTableView 如何隐藏网格线?(setShowGrid(false)

Q5.5 “基于项”的 QListWidget/QTreeWidget/QTableWidget 如何用?怎样在列表项里放自定义控件?

答案要点:直接 addItem()/setItem()QTableWidgetsetRowCount/setColumnCountsetItem(row,col,new QTableWidgetItem(...));列表项内嵌控件用 setItemWidget(item, widget)

详细解答

/* QListWidget:添加文本项 */
QListWidget *listWidget = new QListWidget(this);
listWidget->addItem("0.mp3");
listWidget->addItem("1.mp3");

/* QTableWidget:设置行列与表头,再填单元格 */
QTableWidget *tableWidget = new QTableWidget(3, 2, this);
tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "年龄");
tableWidget->setItem(0, 0, new QTableWidgetItem("啊万"));
tableWidget->setItem(0, 1, new QTableWidgetItem("27"));

/* 在列表项中嵌入自定义 Widget(智能家居的控制卡片就是这么做的) */
QListWidgetItem *item = new QListWidgetItem();
listWidget->addItem(item);
listWidget->setItemWidget(item, customWidget);

基于项控件本质是“封装好模型的视图”,操作直观;数据量大时应改用 View + Model。

追问

  1. QTableWidget 如何让表格自动适应内容?(resizeColumnsToContents()
  2. QListWidget 单击切换页面用什么信号?(currentRowChanged(int)QStackedWidget::setCurrentIndex

六、绘图、文件与图表(5 题)

Q6.1 QFile 打开文件有哪些模式?常用方法有哪些?路径分隔符要注意什么?

答案要点ReadOnly/WriteOnly/ReadWrite/Append/Truncate/Text 可组合;open/close/flush/exists/remove/size/pos/seek/atEnd;文件路径统一用 /

详细解答QFile 是读写文本、二进制和资源的 I/O 设备,可单独用,也可配合 QTextStream/QDataStream

QFile file("/sys/devices/platform/leds/leds/sys-led/brightness");
if (!file.exists()) return;
if (!file.open(QIODevice::ReadWrite))
    qDebug() << file.errorString();
file.write("1");
file.close();
模式 含义
ReadOnly 只读
WriteOnly 只写
ReadWrite 读写
Append 追加到尾部
Truncate 清空原内容
Text 文本模式,\n 自动翻译

关键点:无论 Windows、Linux 还是 Mac,QFile 的分隔符都是 /,不要用 \

追问

  1. 嵌入式里为什么常用 QFile 直接操作 /sys 节点?(Linux 一切皆文件,无需驱动即可控制 GPIO/LED)
  2. 如何读取全部内容?(file.readAll(),文本模式配合 QTextStream::readAll()

Q6.2 QTextStreamQFile 有何区别?什么时候用 QTextStream

答案要点QTextStream 是文本流的方便接口,常与 QFile 结合;支持流操作符、字段填充、对齐与数字格式化;读写纯文本时更简洁。

详细解答

/* 读取:用流读取全部文本 */
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream stream(&file);
textEdit->setPlainText(stream.readAll());
file.close();

/* 写入:用流插入运算符 */
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
out << textEdit->toPlainText();
file.close();

QFile 更底层(read/write/readLine 字节级),QTextStream 面向文本,提供 <</>>readLine()、编码与格式化选项。需要逐行处理或格式化输出时优先用它。

追问

  1. QTextStream 能操作 QString/QByteArray 吗?(能,它可在 QIODevice、QByteArray、QString 上操作)
  2. 读一行用什么?(stream.readLine()

Q6.3 Qt 绘图系统由哪些类组成?paintEvent() 的作用是什么?何时会触发?

答案要点QPainter(绘制)、QPaintDevice(绘图设备基类)、QPaintEngine(平台抽象);paintEvent() 是 QWidget 的受保护虚函数,界面初始化或需要刷新时执行,用 update() 主动触发。

详细解答

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);   /* this 是绘图设备 */
    painter.setRenderHints(QPainter::Antialiasing
                           | QPainter::SmoothPixmapTransform);
    painter.drawRect(10, 10, 100, 50);
}
  • QWidgetQPixmapQImageQLabel 等可视类都可作为“画布”。
  • paintEvent() 在父类中定义为空,专供子类画图;构造对象时会执行一次,之后要靠 update() 触发重绘。

追问

  1. 为什么动画要用定时器 + update()?(paintEvent 不会自动反复执行)
  2. 抗锯齿怎么开?(setRenderHints(QPainter::Antialiasing, true)

Q6.4 如何用 QPainter 绕图片中心旋转?变换顺序为什么重要?

答案要点:先 translate 把原点移到图形中心,再 rotate 旋转,然后把原点恢复;因为绘图变换会累积,顺序错了旋转中心就不对。

详细解答

QPainter painter(this);
QPixmap image; image.load(":/image/cd.png");
QRectF rect((width()-image.width())/2, (height()-image.height())/2,
            image.width(), image.height());

/* 1. 参考点从左上角移到 CD 中心 */
painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2);
/* 2. 按角度旋转 */
painter.rotate(angle);
/* 3. 参考点移回原点位置 */
painter.translate(-(rect.x() + rect.width()/2), -(rect.y() + rect.height()/2));

painter.drawImage(rect, image.toImage(), image.rect());
painter.drawRect(rect.toRect());

配合 QTimer 每 100ms update() 一次,angle++ 到 360 归零,CD 就转起来了。

追问

  1. QRectFQRect 的区别?(前者浮点坐标,绘图更精确)
  2. 如何只旋转某次绘制而不影响后续?(painter.save() / painter.restore()

Q6.5 Qt Charts 由哪些类组成?画动态曲线的原理是什么?

答案要点QChart(图表)、QChartView(视图)、QSplineSeries/QLineSeries(曲线)、QValueAxis(坐标轴);动态曲线的原理是“存有限个数据点,超出则删队首,反复重绘”。

详细解答.proQT += charts,头文件加 QT_CHARTS_USE_NAMESPACE(或 using namespace QtCharts)。基本流程:创建 QSplineSeries → 创建 QChartaddSeries → 创建两条 QValueAxisaddAxisseries->attachAxis()chartView->setChart(chart)

QList<int> data;
void MainWindow::receivedData(int value)
{
    data.append(value);
    while (data.size() > maxSize)     /* 超过上限删掉最早的 */
        data.removeFirst();

    splineSeries->clear();             /* 清空后重新追加,就形成“移动”效果 */
    int xSpace = maxX / (maxSize - 1);
    for (int i = 0; i < data.size(); ++i)
        splineSeries->append(xSpace * i, data.at(i));
}

追问

  1. 折线图用什么类?(QLineSeries
  2. 坐标轴如何绑定曲线?(splineSeries->attachAxis(axisX/axisY)

七、多线程与网络(5 题)

Q7.1 为什么要使用多线程?Qt 创建线程有哪两种方式?官方推荐哪种?

答案要点:耗时操作会阻塞主线程导致界面冻结,需放到子线程;方式是继承 QThread 重写 run(),或继承 QObject 再用 moveToThread();Qt4.8 后官方推荐后者,更灵活。

详细解答:嵌入式 CPU 主频低,摄像头拍照、图像处理、文件读写都可能卡界面。两种方式区别:

方式 新线程中执行的代码 灵活度
继承 QThread 只有 run() 在新线程,其它方法仍在主线程 一般
继承 QObject + moveToThread 整个对象的方法(槽函数)都在新线程 更灵活

追问

  1. 继承 QThread 时哪部分在新线程?(只有重写的 run()
  2. 如何避免忘记解锁?(用 QMutexLocker,作用域结束自动解锁)

Q7.2 继承 QThread 的线程如何写?如何正确地启动和退出?

答案要点:继承 QThread 重写 run();用 start() 启动(它调用 run()),用 quit() + wait() 退出,不要用 terminate()

详细解答

class WorkerThread : public QThread {
    Q_OBJECT
public:
    void run() override {
        QString result = "线程开启成功";
        sleep(2);                       /* 模拟耗时操作 */
        emit resultReady(result);
    }
signals:
    void resultReady(const QString &s);
};

/* 主线程 */
workerThread = new WorkerThread(this);
connect(workerThread, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
void MainWindow::pushButtonClicked() {
    if (!workerThread->isRunning())     /* 防止重复启动 */
        workerThread->start();
}
MainWindow::~MainWindow() {
    workerThread->quit();
    if (workerThread->wait(2000))
        qDebug() << "线程已经结束!" << endl;
}

追问

  1. start() 与直接调用 run() 有何区别?(start() 创建新线程并调用 run();直接调用等于在主线程执行)
  2. wait() 的作用?(阻塞等待线程结束,可带超时)

Q7.3 继承 QObject + moveToThread() 如何安全终止一个循环线程?

答案要点:用 moveToThread() 迁移 Worker,用 QMutexLocker 保护一个 bool 标志位,主线程调用 stopWork() 置位,工作循环检测到后跳出。

详细解答

class Worker : public QObject {
    Q_OBJECT
private:
    QMutex lock;
    bool isCanRun;
public slots:
    void doWork1(const QString &parameter) {
        isCanRun = true;
        while (1) {
            {   /* 限定锁的作用范围 */
                QMutexLocker locker(&lock);
                if (!isCanRun) break;
            }
            QThread::sleep(2);
            emit resultReady(parameter + "doWork1 函数");
        }
        emit resultReady("打断doWork1 函数");
    }
public:
    void stopWork() {                      /* 注意:不能放在槽函数下 */
        QMutexLocker locker(&lock);
        isCanRun = false;
    }
signals:
    void resultReady(const QString &result);
};

/* 使用 */
worker = new Worker;                       /* 继承 QObject 的多线程类不能指定父对象 */
worker->moveToThread(&workerThread);
connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(startWork(QString)), worker, SLOT(doWork1(QString)));

quit()/exit() 不会中途终止线程,terminate() 不安全。加锁虽然损耗性能,但能避免访问冲突。

追问

  1. 为什么 stopWork() 不能做成槽?(否则它会被排到新线程的事件队列里,无法及时打断)
  2. 线程结束后如何释放对象?(deleteLater() 在事件循环空闲时安全销毁)

Q7.4 Qt 网络编程有哪些类?如何获取本机网络信息?

答案要点:低级类 QTcpSocket/QTcpServer/QUdpSocket,高级类 QNetworkRequest/QNetworkReply/QNetworkAccessManagerQHostInfo 查主机名,QNetworkInterface 列网卡 IP。

详细解答

#include <QNetworkInterface>
#include <QHostInfo>

QString str = "主机名称:" + QHostInfo::localHostName() + "\n";
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
foreach (QNetworkInterface interface, list) {
    /* 每个接口可能有 IPv4 / IPv6 两个地址 */
    QList<QNetworkAddressEntry> entries = interface.addressEntries();
    foreach (QNetworkAddressEntry entry, entries) {
        str += entry.ip().toString() + "\n";
    }
}

使用网络需在 .proQT += network(嵌入式视频监控、云端 API 都用到)。

追问

  1. QHostInfo::fromName()localHostName() 区别?(前者按名字解析地址,后者取本机名)
  2. 访问一个 URL 用哪个类?(QNetworkAccessManager::get/post

Q7.5 用 Qt 写 TCP 服务端/客户端、UDP 单播/广播/组播的关键接口分别是什么?

答案要点:TCP 服务端 QTcpServer::listen() + nextPendingConnection();客户端 QTcpSocket::connectToHost();UDP QUdpSocket::bind() + writeDatagram()/readDatagram();组播再 joinMulticastGroup()

详细解答

/* TCP 服务端 */
tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
tcpServer->listen(QHostAddress::Any, 8888);
void MainWindow::onNewConnection() {
    tcpSocket = tcpServer->nextPendingConnection();
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}

/* TCP 客户端 */
tcpSocket = new QTcpSocket(this);
tcpSocket->connectToHost("192.168.1.100", 8888);

/* UDP:发送数据报,无需先 bind */
QUdpSocket udpSocket;
QByteArray data = "hello";
udpSocket.writeDatagram(data.data(), data.size(),
                        QHostAddress::Broadcast, 8888);   /* 广播 */

/* UDP 接收端 */
udpSocket.bind(QHostAddress::Any, 8888);
connect(&udpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
/* 收到后 readDatagram() 读出,注意必须先读走,否则收不到下一个数据报 */

追问

  1. UDP 单播与广播的区别?(单播发给指定 IP,广播发给同一网段所有主机)
  2. 组播要额外做什么?(接收端 joinMulticastGroup() 加入组播组)

八、多媒体、数据库与嵌入式Qt(5 题)

Q8.1 Qt 多媒体模块有哪些主要类?QSoundQSoundEffect 有什么区别?

答案要点QMediaPlayerQSoundQSoundEffectQAudioOutputQAudioInputQAudioRecorderQVideoWidgetQSound 只能异步播放本地 WAV,QSoundEffect 还能播放网络文件。

详细解答:Qt5 起弃用 phonon,改用 Qt Multimedia 模块,proQT += multimedia。Ubuntu 需安装 GStreamer 解码插件,Windows 需安装 LAVFilters。

| 类 | 播放源 | 特点 | | -- | ------ | ---- | | QSound | 本地 WAV | 异步、低延时、体积大,适合短提示音 | | QSoundEffect | 本地或网络 | 支持 QUrl 网络文件 |

播放音效示例:QSound::play(":/audio/bell.wav");

追问

  1. 嵌入式为什么常用 WAV 做按键音?(低延时、无解码负担)
  2. 播放器为何要装解码插件?(Qt 只提供接口,实际解码由平台插件完成)

Q8.2 QMediaPlayerQAudioRecorder 分别怎么用?

答案要点QMediaPlayer 设置媒体源并 play(),视频配合 QVideoWidget::setMediaPlayer() 显示;QAudioRecorder 设置输入设备、编码参数、容器格式与输出路径后 record()/stop()

详细解答

/* 播放器(音乐 / 视频) */
QMediaPlayer *player = new QMediaPlayer;
player->setMedia(QUrl::fromLocalFile("test.mp4"));
videoWidget->setMediaPlayer(player);     /* 视频画面输出到 QVideoWidget */
player->play();

/* 录音(语音识别项目用它录 16k 单声道 wav) */
QAudioRecorder *recorder = new QAudioRecorder(this);
QAudioEncoderSettings settings;
settings.setSampleRate(16000);           /* 百度语音要求 */
settings.setChannelCount(1);             /* 单声道 */
recorder->setEncodingSettings(settings, QVideoEncoderSettings(), "audio/x-wav");
recorder->setOutputLocation(QUrl::fromLocalFile("./16k.wav"));
recorder->record();                      /* 开始录音 */
recorder->stop();                        /* 停止录音 */

播放器还可通过 QMediaPlaylist 管理多首歌曲,或用 setPlaybackMode 设置循环。

追问

  1. 视频画面为什么不能直接贴到普通 QLabel 上?(需要 QVideoWidget 作为渲染目标)
  2. 录音保存 wav 要设置哪个属性?(容器格式设为 audio/x-wav

Q8.3 Qt 如何操作数据库?QSqlDatabaseQSqlQueryQSqlTableModel 各自的作用是什么?

答案要点QSqlDatabase 连接数据库,QSqlQuery 执行 SQL,QSqlTableModel 把表读成模型供 QTableView 显示;嵌入式常用 SQLite。

详细解答proQT += core gui sql。SQLite 是进程内、无服务器、零配置的轻量数据库,适合嵌入式。

/* 打开 / 创建数据库 */
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("alarm.db");
if (!db.open()) qDebug() << db.lastError();

/* 用 QSqlQuery 建表、增删改查 */
QSqlQuery query(db);
query.exec("create table alarm (id int primary key, time vchar(15), state vchar(10))");
query.prepare("insert into alarm values(?, ?, ?)");   /* 预处理防注入 */
query.addBindValue(0); query.addBindValue("06:00"); query.addBindValue("false");
query.exec();

/* 用 QSqlTableModel + QTableView 显示,可直接编辑单元格改库 */
QSqlTableModel *model = new QSqlTableModel(this, db);
model->setTable("alarm");
model->setEditStrategy(QSqlTableModel::OnFieldChange);
model->select();
QTableView *view = new QTableView;
view->setModel(model);

追问

  1. QSqlQueryQSqlTableModel 各自适合什么场景?(前者适合精确 SQL 操作,后者适合表格化显示与编辑)
  2. 想只读展示查询结果用什么?(QSqlQueryModel

Q8.4 嵌入式 Qt 如何交叉编译?运行时如何选择显示平台插件?

答案要点:用交叉工具链的 qmake + make,或在 Qt Creator 配置 ARM Kit;运行时用 -platform linuxfb-platform eglfs 指定平台插件。

详细解答

# 命令行交叉编译
/opt/Qt5.12.9/Tools/QtCreator/bin/qtcreator.sh &   # 带环境变量启动 Qt Creator
/path/to/arm-qmake 01_led.pro && make -j8
scp 01_led root@192.168.1.100:/home/root/

# 运行
./01_led                      # 使用默认平台插件
./01_led -platform eglfs      # EGL/OpenGL ES 全屏(有 GPU)
./01_led -platform linuxfb    # Linux Framebuffer(无 GPU)
插件 依赖 场景
linuxfb /dev/fb0 无 GPU、直接帧缓冲
eglfs EGL/OpenGL ES 有 GPU,I.MX6ULL 出厂系统常用
xcb X Server Ubuntu 桌面上仿真

⚠️ 来源说明:平台插件部分不属于《I.MX6U嵌入式Qt开发指南》内容,为扩展知识。

追问

  1. 交叉编译后为什么常要拷贝可执行文件到板子运行?(远程运行时环境变量/路径可能不一致)
  2. 先在哪里验证界面逻辑再交叉编译?(Ubuntu 桌面套件仿真)

Q8.5 在嵌入式 Qt 中操作硬件(LED/传感器/摄像头)的统一模式是什么?举一个综合项目说明。

答案要点:确认设备节点 → 用定时器/事件触发 → 用 QFile 或 C 的 open/read 读写 → 通过信号把数据交给界面;综合项目(如智能家居)再叠加串口、网络与云端 API。

详细解答:Qt 在嵌入式里操作硬件的通用套路:

  1. 找节点:LED .../sys-led/brightness、AP3216C /sys/class/misc/ap3216c/*、ICM20608 /dev/icm20608、摄像头 /dev/video1、串口 /dev/ttymxc2、CAN can0
  2. 定触发:低速量用 QTimer(如 500ms 采一次),交互用事件(keyPressEvent)或信号(readyReadframesReceived)。
  3. 读写数据:QFile(sysfs 文本)或 C 的 open()/read()(二进制结构体,如 icm20608 的 7 个 int)。
  4. 推给界面:用自定义信号(如 ap3216cDataChanged())通知主线程刷新。

综合实例(智能家居物联网):ESP8266 通过串口用 AT 指令接入 WIFI 与原子云 → 定时发心跳;控制端用 QNetworkAccessManager 走 HTTPS 拿设备列表与 token,用 QWebSocket 鉴权并订阅消息;点开关时下发 0x03 命令,云端转发到设备,设备端收到“开/关”后写 LED 节点。整个链路把串口 + 网络 + 硬件控制 + 信号槽 + 多模块协作都串了起来。

追问

  1. 摄像头/视频监控为什么必须开子线程?(采集与编码耗时会卡主线程)
  2. 为什么 Qt 蓝牙只能在 Linux/BlueZ 上跑?(依赖 BlueZ 协议栈,Windows 需自行移植)

内容来源:正点原子《I.MX6U嵌入式Qt开发指南V1.2》第一章 在Ubuntu下编写C++、第二章 C++基础、第三章 初识Qt、第四章 使用Qt Designer开发、第五章 Qt信号与槽、第六章 Qt Creator的使用技巧、第七章 Qt控件、第八章 文本读写、第九章 绘图与图表、第十章 多线程、第十一章 网络编程、第十二章 多媒体、第十三章 数据库、第十四章 I.MX6U Qt开发 ~ 第二十八章 视频监控项目;配套例程 Embedded-Qt-Tutorial/Qt/01Qt/02Qt/03Qt/04;平台插件 -platform linuxfb/-platform eglfs 部分为扩展知识。