【一】前言:

经过一段时间的C++和Qt学习,作为对这一阶段学习成果的检验,我决定使用Qt平台模仿C++的控制台输入输出编写一个项目。

初学C++的时候,程序获取用户输入是通过标准输入输出流对象实现的,如下图:

我们编写的项目也按照这个思路来,也编写输入输出流对程序的输入输出进行封装

【二】相关的知识点:

C++基础部分:

类的编写,以及类的继承

虚函数

利用友元函数对运算符重载

指针的基本应用

迭代器的应用(QString的迭代器)

多文件组织

Qt部分:

Qt常用控件

混合式ui设计技巧

利用QThread类实现和管理多线程

Qt信号与槽机制

【三】UI设计:

为了方便设计,这里使用Qt Creator的可视化设计来设计我们交互的窗口界面:

本项目的ui设计比较简单,包含两个控件,一个是文本浏览器(对应的类是QTextBrowser, 命名为:textBrowser 用来显示用户的输入内容以及程序的输出内容),另一个是行文本编译器(对应的类是QLineEdit,命名为lineEdit 用来接收用户的输入内容),布局采用垂直布局。

【四】交互机制的设计:

说明: 一个线程一般只能有一个循环,由于我们的程序的主线程需要处理各种事件,如果在运行的时候阻塞线程等待用户输入,就无法处理窗口的事件,因此我们使用QThread类额外开一个工作线程,将用户的输入过程放在这个工作线程中实现。(这里暂且不考虑使用线程同步,而使用信号与槽的机制来进行工作线程和主线程的信息交流)

流程:

输入流程:用户输入内容-->敲击回车-->主线程发送用户输入的信号-->工作线程的槽函数接受到信号停止对工作线程的阻塞,并对输入的信息进行处理,传给对应的变量

输出流程:工作线程将要输出的变量转化成Qt字符串通过信号发出-->主线程的槽函数接收字符串-->主线程将字符串显示在文本浏览器控件(QTextBrowser)上

【五】代码实现

所有的文件:

Qt的UI设计文件:mainwindow.ui

实现输入输出的工作线程的类所在头文件:  ioThread.h

实现窗口设计和信号与槽设计的头文件:  mainwindow.h

实现输入输出的工作线程的类所在源文件:  ioThread.cpp

实现窗口设计和信号与槽设计和绑定的源文件:mainwindow.cpp

程序运行的入口源文件: main.cpp

类的设计:

mainwindow.h头文件中:

包含一个类的申明:

class mainwindow

源代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include "ioThread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void keyPressEvent(QKeyEvent* event);   // 虚函数,键盘事件的重写,用于实现用户按下回车后的逻辑void closeEvent(QCloseEvent* event);    // 虚函数,对窗口关闭事件的重写,用于让工作线程安全退出
private:Ui::MainWindow *ui;IoThread aThread;   // 保存工作线程
signals:void inputMessage(QString msg); // 信号,用于向工作线程发送用户输入的字符串
public slots:void outputMessageEmit(QString msg);    // 槽,用于接收工作线程发送的需要输出信号void newlineEmit(); // 槽,用于接收工作线程的换行信号};
#endif // MAINWINDOW_H

说明:该文件中是窗口类的申明,主体部分由Qt Creator自动生成,这里我们额外添加了一个信号和两个槽函数用于主线程与工作线程的信息交流。

在 ioThread.h文件中:

包含4个类的申明:

class MsgInStream                  定义输入流

class StreamLineEnd               定义换行对象,对应的是标准输入输出流中的endl

class MsgOutStream                定义输出流

class IoThread                          定义输入输出工作线程

源代码:

#ifndef IOTHREAD_H
#define IOTHREAD_H
#include <QThread>
#include <QKeyEvent>
#include <QTextBrowser>
class MsgInStream:public QObject{
// 必须要继承QObject才可以使用信号与槽
// 输入流Q_OBJECT
private:QString inputMessage;   // 存储用户从行编辑器中输入的内容,类似控制台程序的缓存区public:MsgInStream():ifFinishedInput(false){};~MsgInStream(){};// 利用友元重载运算符friend MsgInStream& operator>>(MsgInStream& input,int& value);  // 读取整数friend MsgInStream& operator>>(MsgInStream& input,double& value);   // 读取双精度浮点数friend MsgInStream& operator>>(MsgInStream& input,QString& value);  // 读取Qt字符串friend MsgInStream& operator>>(MsgInStream& input,QChar& value);  // 读取一个字符bool ifFinishedInput;   // 写在operator>>重载友元函数中,如果未完成读入(按行)则进入死循环等待
public slots:void inputMsgGet(QString str);  // 槽函数用于获取主线程的lineEdit读取的字符串
};class StreamLineEnd{
public:explicit StreamLineEnd(){};~StreamLineEnd(){};
};class MsgOutStream:public QObject{
// 输出流Q_OBJECT
public:MsgOutStream(){};~MsgOutStream(){};friend MsgOutStream& operator<<(MsgOutStream& output,const int& value); // 输出整型friend MsgOutStream& operator<<(MsgOutStream& output,const double& value);  // 输出双精度friend MsgOutStream& operator<<(MsgOutStream& output,const QString& value); // 输出Qt字符串friend MsgOutStream& operator<<(MsgOutStream& output,const StreamLineEnd& endl);    // 换行friend MsgOutStream& operator<<(MsgOutStream& output,const QChar& endl); // 输出一个字符
signals:void outputMessage(QString msg);    // 通过信号与槽来实现输出void newline();
};class IoThread: public QThread{
//Q_OBJECT
public:void threadStart(); // 用于启动线程void threadStop(); // 线程终止MsgInStream msgin;MsgOutStream msgout;StreamLineEnd endl;IoThread(){};~IoThread(){wait();qDebug()<<"安全退出";};protected:void run() Q_DECL_OVERRIDE;    // 虚函数重写,当作控制台中的main函数};#endif // IOTHREAD_H

说明:为了能够使用信号与槽必须使用宏 Q_OBJECT,而要使用这个宏我们的类必须是继承了Qt基本类QObject,对于类StreamLineEnd只需要它作为占位使用,所以不需要添加类成员,构造和析构函数都为空即可,对于IoThread需要说明的是虚函数run可以当作控制台程序中的主函数使用,只需要将要实现的代码逻辑写在其中

在源文件mainwindow.cpp中

源代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);this->setWindowTitle("交互窗口输入输出测试");this->aThread.threadStart();    // 开启工作线程// 以下进行主线程和工作线程信息交流的信号与槽的连接connect(this,&MainWindow::inputMessage,&(this->aThread.msgin),&MsgInStream::inputMsgGet);connect(&(this->aThread.msgout),&MsgOutStream::outputMessage,this,&MainWindow::outputMessageEmit);connect(&(this->aThread.msgout),&MsgOutStream::newline,this,&MainWindow::newlineEmit);ui->lineEdit->setFocus();
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::keyPressEvent(QKeyEvent* event){if(event->key() == Qt::Key_Return){// 检查键盘事件是否是enterQString inputStr = ui->lineEdit->text();ui->lineEdit->clear();if(inputStr.length() != 0){// 如果用户有输入了就发射信号qDebug()<<inputStr;ui->textBrowser->insertPlainText("INPUT: "+inputStr);ui->textBrowser->append("");// 默认换行emit inputMessage(inputStr);}else{ui->textBrowser->append("<font color = yellow> INPUT: \"NONE\" </font>");ui->textBrowser->append("");    // 换行}}
}void MainWindow::closeEvent(QCloseEvent* event){if(this->aThread.isRunning()){this->aThread.threadStop();this->aThread.wait();}event->accept();
}void MainWindow::outputMessageEmit(QString msg){ui->textBrowser->insertPlainText(msg);  // 在当行插入字符串
}void MainWindow::newlineEmit(){ui->textBrowser->append("");    // 追加字符串并换行
}

在源文件ioThread.cpp中

源代码:

# include "ioThread.h"
void MsgInStream::inputMsgGet(QString str){this->inputMessage = str;this->ifFinishedInput = true;
}void IoThread::threadStart(){this->start();
}
void IoThread::threadStop(){this->terminate();  // 终止线程,强制终止,配合主窗口关闭事件的wait使用
}MsgInStream& operator>>(MsgInStream& input,int& value){QString value_str;while(!input.ifFinishedInput){};    // 如果来自窗口的信息未被读取则再此阻塞工作线程// 如果出现字符'0'到'9'和'-','+'之外的字符则停止读入QString::iterator iter = input.inputMessage.begin();while(true){if((*iter)<'0'||(*iter)>'9'){// 非数字字符的识别和处理if(iter != input.inputMessage.begin()){// 包含的情况:非负号,或负号在后break;}}// 数字字符的识别和处理value_str += QString(*iter);++iter;}value = value_str.toInt();if(*iter == ' '){value_str += ' ';}input.inputMessage.replace(value_str,"");if(value_str.length() == 0 || input.inputMessage.length() == 0){// 只要保留数据的字符串长度为0则默认后面的字符串无意义,直接停止读入input.ifFinishedInput = false;  // 数据读完后将标记更新}return input;
}MsgInStream& operator>>(MsgInStream& input,double& value){QString value_str;while(!input.ifFinishedInput){};    // 如果来自窗口的信息未被读取则再此阻塞工作线程// 如果出现字符'0'到'9'和'-','+'之外的字符则停止读入QString::iterator iter = input.inputMessage.begin();while(true){if(((*iter)<'0'||(*iter)>'9')&&(*iter)!='.'){// 非数字字符的识别和处理if(iter != input.inputMessage.begin()){// 包含的情况:非负号且非正号,或负号在后和正号在后break;}}// 数字字符的识别和处理value_str += QString(*iter);++iter;}value = value_str.toDouble();if(*iter == ' '){value_str += ' ';}input.inputMessage.replace(value_str,"");if(value_str.length() == 0 || input.inputMessage.length() == 0){// 只要保留数据的字符串长度为0则默认后面的字符串无意义,直接停止读入input.ifFinishedInput = false;  // 数据读完后将标记更新}return input;
}MsgInStream& operator>>(MsgInStream& input,QString& value){while(!input.ifFinishedInput){};    // 如果来自窗口的信息未被读取则再此阻塞工作线程value = ""; // 首先要将引用value清空,否则value已有的值会影响后面的判断QString::iterator iter = input.inputMessage.begin();while(true){if(iter == input.inputMessage.end()){input.inputMessage.replace(value,"");break;}if(*iter == ' '){input.inputMessage.replace(value+' ',"");break;}else{value += QString(*iter);++iter;}}if(input.inputMessage.length() == 0){// 输入字符串为空,直接停止读入input.ifFinishedInput = false;  // 数据读完后将标记更新}return input;
}MsgInStream& operator>>(MsgInStream& input,QChar& value){while(!input.ifFinishedInput){};    // 如果来自窗口的信息未被读取则再此阻塞工作线程QString::iterator iter = input.inputMessage.begin();value = *iter;input.inputMessage.replace(0,1,""); // 使用replace的另一种重载版本,去除第一个字符if(input.inputMessage.length() == 0){// 输入字符串为空,直接停止读入input.ifFinishedInput = false;  // 数据读完后将标记更新}return input;
}MsgOutStream& operator<<(MsgOutStream& output,const int& value){QString value_str = QString::number(value);value_str = "OUTPUT: "+value_str;emit output.outputMessage(value_str);   // 发射输出信号return output;
}MsgOutStream& operator<<(MsgOutStream& output,const double& value){QString value_str = QString::number(value);value_str = "OUTPUT: "+value_str;emit output.outputMessage(value_str);return output;
}MsgOutStream& operator<<(MsgOutStream& output,const QString& value){QString value_str;value_str+=QString("OUTPUT: ")+value;emit output.outputMessage(value_str);return output;
}MsgOutStream& operator<<(MsgOutStream& output,const StreamLineEnd& endl){Q_UNUSED(endl);emit output.newline();  // 发射换行信号return output;
}MsgOutStream& operator<<(MsgOutStream& output,const QChar& value){QString value_str;value_str+=QString("OUTPUT: ")+value;emit output.outputMessage(value_str);return output;
}
void IoThread::run(){// 重载run函数int a,b;while(true){msgin>>a>>b;msgout<<a<<endl;msgout<<b<<endl;}quit();
}

主函数源文件main.cpp

由Qt Creator生成

源代码

#include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}

【六】运行效果

【七】总结

通过小项目的编写确实是一种检验自己知识点掌握和锻炼代码能力的一种有效途径,本人能力有限,程序中可能存在的问题欢迎大家指出,之后在这个栏目里我会继续分享一些有趣的小项目,让我们共同学习,共同进步。

【项目一】基于Qt平台的交互式输入与输出窗口相关推荐

  1. QT5/C++项目:基于QT的跨平台网络对战象棋(一)(推荐★★★★)

    QT5/C++项目:基于QT的跨平台网络对战象棋(一)(推荐★★★★) 文章目录 QT5/C++项目:基于QT的跨平台网络对战象棋(一)(推荐★★★★) 本篇副标题: 本篇博客讲了什么or解决了什么问 ...

  2. QT5/C++项目:基于QT的跨平台网络对战象棋(三)(推荐★★★★)

    QT5/C++项目:基于QT的跨平台网络对战象棋(三)(推荐★★★★) 文章目录 QT5/C++项目:基于QT的跨平台网络对战象棋(三)(推荐★★★★) 本篇副标题: 本篇博客讲了什么or解决了什么问 ...

  3. 嵌入式项目实战——基于QT的视频监控系统设计(四)

    嵌入式项目实战--基于QT的视频监控系统设计(四) 进入到五一假期的第四天,通过前三天的分享,相信你应该已经掌握了QT的基本使用.UDP网络编程.v4l2视频处理模块.多线程编程等基本的知识点.其实昨 ...

  4. 超详细基于Qt平台实现C/C++调用Matlab函数全流程

    超详细基于Qt平台实现C/C++调用Matlab函数全流程 1. 基本调用方式介绍 2. 环境配置 3. 将Matlab程序写成函数形式 4. Matlab配置C编译器,将.m文件转换成动态链接库 4 ...

  5. 嵌入式项目实战——基于QT的视频监控系统设计(三)

    嵌入式项目实战--基于QT的视频监控系统设计(三) 进入到五一假期第三天,继续我们的项目.本来五一假期还是想好好休息一下的,因为最近学习的状态不太好,刷题都没有思路了,但是身边的同学太卷了,不过我还是 ...

  6. 基于Qt平台开发安卓应用

    基于Qt平台开发安卓应用 在之前的Qt学习中,使用QtGUI来实现简单的界面与后台功能设计.这样开发出来的应用往往界面元素比较单一,美感几乎可以省略,与其他移动平台开发的App相比,确实逊色不少.基于 ...

  7. 基于Qt平台的集串口调试助手、示波器、图像显示功能于一体的上位机。

    基于Qt平台的集串口调试助手.示波器.图像显示功能于一体的上位机. 可用的上位机及通信协议可在这里下载,通信协议可以很方便的移植到stm32上 链接:https://pan.baidu.com/s/1 ...

  8. 嵌入式项目实战——基于QT的视频监控系统设计(二)

    嵌入式项目实战--基于QT的视频监控系统设计(二) 昨天我分享了关于QT的基本使用方法,掌握了这些基本的方法就可以设计一个简单的视频监控界面.下面我们开始分享完成这个嵌入式项目同样重要的知识点--UD ...

  9. 嵌入式项目实战——基于QT的视频监控系统设计(一)

    嵌入式项目实战--基于QT的视频监控系统设计(一) 这个五一因为疫情,只能待在家里,想了想不如将我之前做的一个小的嵌入式的练习项目分享出来,供入门嵌入式的同学们学习.基于QT的视频监控系统设计虽然是个 ...

最新文章

  1. 微型计算机显卡必须插在主板的,第一章 计算机基础知识(2)
  2. linux命令之watch -- 周期性地执行命令
  3. 基于java家教管理系统_基于jsp的家教信息管理-JavaEE实现家教信息管理 - java项目源码...
  4. 关闭SQLite3中的journal暂存档
  5. zoj 1006 Do the Untwist 簡單字符串
  6. 关于Feign的几个问题
  7. Hadoop1——创建虚拟机
  8. 配置devtools热部署
  9. 你需要启用steam社区界面功能以进行购买_STEAM播放器成了:不用买游戏就能单独购买游戏音轨...
  10. 开源)嗨,Java,你可以生成金山词霸的二维码分享海报吗?
  11. 【从蛋壳到满天飞】JS 数据结构解析和算法实现-链表
  12. STM32CubeMX学习笔记(15)——电源管理(PWR)低功耗睡眠模式
  13. Mask RCNN综述以及建筑物实例分割
  14. 《人工智能的未来》摘录
  15. 盘点2022热词、网络流行语!富而喜悦摆渡人走红!
  16. Excel的一些函数操作
  17. [转]关于计算机研究生报考方向的简要介绍
  18. 解决Cmake编译 OPENCV_DNN_CUDA 报错: CMake Error at modules/dnn/CMakeLists.txt:39.
  19. python中定义元组的符号_python 3 基础之元组tuple,详解
  20. 几款免费的数据恢复软件测试

热门文章

  1. [转]给初学者的建议
  2. 普通键盘和机械键盘的区别
  3. 离散数学——最大、小元 超详细结合例题理解
  4. 专访华大基因靳大卫:以火眼实验室为“根据地”,我们要打造数字化抗疫新标杆...
  5. Oracle Indexes(索引)
  6. 短链脱氢酶催化合成奥利司他中间体的机制研究
  7. 经纬财富:菏泽黄金投资如何止损
  8. postgresql数据库中使用使用UUID
  9. Linux中nmap脚本的目录,nmap脚本指南
  10. ERROR 1064 (42000) You have an error in your SQL syntax; check the manual that corresponds to your