Qt 官方示例 | 简单几步,小白也能制作一个串口终端

大家好,我是老吴。

我又成功写了一篇小文章,每当我没动力写文章的时候,我就会把写文章的门槛降低,这其实是很好的一个技巧,把一个大目标切成很多小目标,是对付拖延症的一个好方法。

正巧最近看了一些讨论敏捷开发的文章,或者是不是可以考虑一下怎么敏捷学习?

某个项目的长时间开发会消磨开发人员的耐心,长时间学习某样东西同样也会打击人的信心。

再说了,你怎么知道现在学习的东西将来是否会在工作上用得上。

所以,我的结论是:学到的知识尽量马上用。能用一点是一点,不够用了再回头补!

回到正题,学习 Qt 的最佳途径是阅读官方的手册和示例。

今天要分享的是 Qt 官方示例:terminal。

一个简易的串口终端工具:

点击查看大图

它演示了如何创建一个终端,并使用 QSerialPort 进行串口通讯。

源码文件:

terminal/
├── console.cpp
├── console.h
├── images/
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── settingsdialog.cpp
├── settingsdialog.h
├── settingsdialog.ui
├── terminal.pro
└── terminal.qrc

接下来,跟我一起来实现这个小工具吧。

正文目录:

1. 新建 Qt Widgets Application
2. 设计主界面
3. 实现主界面的功能
4. 设计设置界面
5. 实现设置界面的功能
6. 相关参考

1. 新建 Qt Widgets Application

在 Qt Creator 里 点击File > New File or Project > Applications > Qt Widgets Application > Choose:

点击查看大图

以 QMainWindow 作为基类,子类名为 MainWindow:

新建完成后会得到下列文件:

terminal/
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── terminal.pro
└── terminal.qrc

运行效果:

点击查看大图

此时只有跟 QMainWindow 一样的界面,还没有任何串口终端相关的功能。

2. 设计主界面

主界面分为 4 部分:

点击查看大图

1、菜单栏,用 QMenuBar 实现,用于提供串口终端的核心操作功能;

2、工具栏,用 QToolBar 实现,用于提供快捷操作;

3、中间区域留空,待会在 C++ 中通过继承 QPlainTextEdit 来实现,用于输入和输出串口信息;

4、状态栏,用 QstatusBar 实现,用于显示当前的连接状态。

在 Qt Designer 里执行下列操作:

1、菜单栏里添加 Calls、Tools 菜单项:

在 Calls 里添加子菜单项 Connect、Disconnect,分别对应连接和断开串口的功能。

在 Tools 里添加子菜单项 Configure、Clear,分别对应打开设置窗口、清空终端信息的功能。

2、工具栏里添加 Connect、Disconnect、Configure、Clear 按钮;

3、添加 Setting 对话框:

使用 Dialog without Buttions 模板,新建名为 "SettingsDialog" 的对话框。

运行效果:

点击查看大图

此时,菜单栏和工具栏里的菜单项并没有任何实际功能。

3. 实现 Setting 功能

由于连接串口前需要先设置参数,所以我们需要先实现 Setting 界面里的功能。

设计界面:

点击查看大图

此时,还没有实际的功能。

支持选择串口设备:

// 文件:settingsdialog.cpp
void SettingsDialog::fillPortsInfo(){
    ...
    // 拿到系统当前所有可用的串口设备列表
    const auto infos = QSerialPortInfo::availablePorts();

    // 初始化组合框
    for (const QSerialPortInfo &info : infos) {
        QStringList list;
        description = info.description();
        manufacturer = info.manufacturer();
        serialNumber = info.serialNumber();
        list << info.portName();
        ...
        m_ui->serialPortInfoListBox->addItem(list.first(), list);
    }
}

在 Qt 里,可以通过 QSerialPortInfo 来获取系统当前可用的串口设备列表,我们拿到串口信息后,就用它来初始化Serial Port 组合框。

当选择不同的串口设备时,要切换设备的描述信息:

connect(m_ui->serialPortInfoListBox,
QOverload<int>::of(&QComboBox::currentIndexChanged), this,&SettingsDialog::showPortInfo);

运行效果:

点击查看大图

支持设置串口参数:

// 文件:settingsdialog.cpp
void SettingsDialog::fillPortsParameters(){
    // 添加可选的波特率、数据位数等串口参数
    m_ui->baudRateBox->addItem(QStringLiteral("9600"), QSerialPort::Baud9600);
    m_ui->baudRateBox->addItem(QStringLiteral("19200"), QSerialPort::Baud19200);
    m_ui->baudRateBox->addItem(QStringLiteral("38400"), QSerialPort::Baud38400);
    m_ui->baudRateBox->addItem(QStringLiteral("115200"), QSerialPort::Baud115200);

    ...
}

在 Qt 里,串口相关的功能由 qtserialport 模块负责实现,只要就是 QSerialPort 和 QSerialPortInfo 这两个类。

运行效果:

点击查看大图

支持保存设置:

// 文件:settingsdialog.cpp
void SettingsDialog::apply(){
    updateSettings();
    hide();
}

// 将串口参数保存在变量 m_currentSettings 中
void SettingsDialog::updateSettings(){
    m_currentSettings.name = m_ui->serialPortInfoListBox->currentText();

    m_currentSettings.baudRate = static_cast<QSerialPort::BaudRate>(m_ui->baudRateBox->itemData(m_ui->baudRateBox->currentIndex()).toInt());

    ...
}

当点击 Apply 按钮时,就会调用到 apply(),进而将界面上的串口参数保存在 struct Settings m_currentSettings 中。

运行效果:

点击查看大图

4. 实现串口 Connect 功能

// 文件:mainwindow.cpp
void MainWindow::openSerialPort(){
    const SettingsDialog::Settings p = m_settings->settings();
    // 要打开哪个串口设备?
    m_serial->setPortName(p.name);
    ...

    // 仍然是那个熟悉的 open()
    if (m_serial->open(QIODevice::ReadWrite)) {
        m_console->setEnabled(true);
        m_ui->actionConnect->setEnabled(false);
        m_ui->actionDisconnect->setEnabled(true);
        m_ui->actionConfigure->setEnabled(false);
        ...
    }
}

当点击 Connect 按钮时,就会调用到 openSerialPort(),进而将执行到 QSerialPort 的 open()。

m_console 是一个 QPlainTextEdit 类型的对象,它负责显示串口。

运行效果:

点击查看大图

此时已经能打开串口设备了,但是还无法显示串口数据。

5. 实现串口读写功能

支持从串口读数据:

// 文件:mainwindow.cpp
void MainWindow::readData(){
    // 从串口硬件中读数据
    const QByteArray data = m_serial->readAll();
    // 显示在文本框中
    m_console->putData(data);
}

打开串口后,当串口有数据时,就调用 MainWindow::readData() 将数据读出来并显示到界面上。putData() 会调用

QPlainTextEdit::insertPlainText() 将数据显示在文本框里。

支持向串口写数据:

// 文件:console.cpp
void Console::keyPressEvent(QKeyEvent *e){
    switch (e->key()) {
    // 不处理某些按键
    case Qt::Key_Backspace:
    ...
        break;
    default:
        // 发送接收到用户输入的信号
        emit getData(e->text().toLocal8Bit());
    }
}

当用户通过键盘输入时,m_console 会捕获该事件并发出信号,主界面接收到该信号后,会执行真正的写串口操作:

// 文件:mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : ...
{
    ...
    connect(m_console, &Console::getData, this, &MainWindow::writeData);
}

void MainWindow::writeData(const QByteArray &data){
    // 熟悉的 write()
    m_serial->write(data);
}

运行效果:

点击查看大图

到此,这个 Qt 的官方示例子 terminal 的核心功能就实现完毕啦。

感兴趣的小伙伴们请自行阅读 Qt example 里的完整代码吧。

相关参考

《Qt 官方文档》

《C++ GUI Qt4 编程 (第二版)》

《Qt5 编程入门 (第二版)》

《Qt Creator 快速入门》

—— The End ——

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容