本专题导航,Click Me
这篇文章进一步实现csv文件的创建与写入,并存入U盘。更具体的功能说明如下:
因为在第一章中我们已经完成了交叉编译环境的配置,后面的程序开发均是在PC端的Ubuntu18.04环境下,并将可执行文件放在树莓派上执行。
csv是逗号分隔值的英文缩写,csv文件是一种用来存储数据的纯文本文件。对csv文件进行的写入主要用到了QT的QFile和QTextStream类,其使用可查看官方的帮助文档,都有相对详细的描述。除了库函数的使用之外,还需要注意两点:
清楚csv文件的基本概念、QT相关类的使用和注意事项之后,就可以写代码了。这里贴一个代码实现,放在“新建文件”按钮触发的槽函数中。
/**************************************
函数名称:MainWindow::slot_creatFile_clicked
函数功能:“新建文件”按钮触发的槽函数.
入口参数:无
出口参数:无
**************************************/
void MainWindow::on_creatFile_clicked()
{
//生成文件路径+名称
QDateTime currTime = QDateTime::currentDateTime();
QString sCurrTime = currTime.toString("yyyyMMdd_hhmmss");
sCurrTime.append(".csv");
sCurrTime.prepend(workPath);
cout << sCurrTime.toStdString() << endl;
//新建文件
QFile file(sCurrTime);
//用只写方式打开文件,并将原来文件中内容清空
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
QTextStream fOut(&file);
//设置编码格式
fOut.setCodec("UTF-8");
//写入表头
fOut << title.join(",") << "\r\n";
//写入一列,内容为1~100数字
for(quint16 i=1; i<=100; i++)
{
fOut << i;
fOut << "\r\n";
}
file.close(); //关闭文件
}
其中,表头和工作路径是定义在mainwindow.h文件中的MainWindow类中的私有变量,定义如下:
const QString workPath = "/home/pi/WorkFiles/"; //工作路径
const QStringList title = {"时间", "设备编号", "识别结果", "是否合格"}; //表头
U盘属于SCSI硬盘,在linux中被表示为“sdx~”。其中“sd”表示设备类型为SCSI硬盘,“x”为盘号(a为基本盘,b为基本从属盘,c为辅助主盘,d为辅助从属盘),“~”代表分区,前四个分区用1~4表示,它们是主分区或扩展分区,从5开始是逻辑分区。
U盘插入后,/dev 路径下会出现sd开头的文件,比如sda和sda4(也有可能是sdb和sdb4等等)。我们就根据这个特点去在程序中做U盘插入的检测。在terminal中输入:
find /dev/ -name "sd*"
可在 /dev 文件夹下找到所有 sd 开头的文件,如下图。
现在我们已经可以用命令行检测U盘是否插入了,下一步就是在QT中实现检测。QT中的QProcess类可以帮助我们开启一个进程并通过命令行执行相应的操作,相关代码如下:
QProcess piTerminal;
//相当于输入命令行:sh -c find /dev/ -name "sd*"
//QStringList() constructs an empty string list
piTerminal.start("sh", QStringList() << "-c" << "find /dev/ -name \"sd*\""); //开启进程并写入命令行
piTerminal.waitForFinished(); //等待执行完成
QString ret = piTerminal.readAllStandardOutput(); //读取命令行中返回的内容
假设U盘对应的标识符为sda4,实现挂载U盘至linux文件系统(本文挂载至 /home/pi/MountUSB)的命令行是:
sudo mount /dev/sda4 /home/pi/MountUSB
其中 mount 命令的第一个参数应为U盘对应的标识符,可以从上一步命令行返回的内容(ret)中提取; mount 命令的第二个参数是挂载的目录,这里设置为 /home/pi/MountUSB 。
注:需要提前在 /home/pi/ 路径下创建 MountUSB 文件夹。
文件拷贝用的是命令 cp ,由于这里是直接拷贝文件夹(其中包括所有的csv文件),需要参数 -r 实现递归拷贝。命令行代码为:
cp -r /home/pi/WorkFiles/ /home/pi/MountUSB/
其中,命令 cp 的第一个参数为csv文件存放的路径,第二个参数为U盘挂载的路径,实现将所有csv文件(整个文件夹)拷贝至U盘。
文件(夹)重命名所用命令为 mv,假设当前时间为2022年2月8日,22:18:50,则命令行代码为:
mv /home/pi/MountUSB/WorkFiles /home/pi/MountUSB/20220208_221850
其中,命令mv的第一个参数为拷贝至U盘的文件夹路径;第二个参数为将文件夹重命名为当前时间后的文件夹路径。
完成文件拷贝后即可卸载U盘,所用命令为umount,命令行如下:
sudo umount /dev/sda4
其中,命令umount的参数为U盘对应的标识符,同样从1.检测U盘插入中命令行返回的内容(ret)中提取得到。
拷贝文件至U盘相关的代码放在定时器溢出信号触发的槽函数中,即定时检测U盘是否插入,若插入则执行:
说明:提示用户“文件拷贝完成,可拔出U盘”的实现主要用到函数QMessageBox::information,用户需要点击“OK”,程序才可继续运行。
实现上述功能的主要代码如下:
/**************************************
函数名称:MainWindow::slot_timer_usb_timeout
函数功能:定时器(用于U盘)溢出时触发的槽函数.
入口参数:无
出口参数:无
**************************************/
void MainWindow::slot_timer_usb_timeout()
{
//相当于输入命令行:find /dev/ -name "sd*"
//QStringList() constructs an empty string list
piTerminal.start("sh", QStringList() << "-c" << "find /dev/ -name \"sd*\"");
piTerminal.waitForFinished();
QString ret = piTerminal.readAllStandardOutput();
QStringList list = ret.split("\n"); //根据"\n"切分terminal返回的内容
//cout << list.count() << endl;
if(list.count() > 3)
{
cerr << "有" << (list.count()-2) << "个USB设备插入" << endl;
}
else if(list.count() == 2)
{
cerr << "无法检测到USB设备" << endl;
}
if(flagUSB == 0)
{
if(list.count() == 3) //有新的USB设备插入
{
flagUSB = 1;
timer_usb->stop(); //关闭定时器
cout << "U盘已插入" << endl;
//窗口显示提示信息
QWidget *window = new QWidget();
window->setGeometry(800, 420, 320, 240); //显示在屏幕中央
QLabel *label = new QLabel("U盘已插入", window);
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
label->setGeometry(0, 0, 320, 240);
window->show();
//挂载U盘
list.removeOne("");
list.removeLast();
piTerminal.start("sudo", QStringList() << "mount" << list << mntPath);
piTerminal.waitForFinished();
ret = piTerminal.readAllStandardOutput();
if (ret == "")
{
cout << "U盘已挂载" << endl;
}
else
{
cerr << "U盘挂载失败" << endl;
}
//拷贝文件夹
label->setText("U盘已插入\n文件拷贝中...");
piTerminal.start("cp", QStringList() << "-r" << workPath << mntPath);
piTerminal.waitForFinished();
ret = piTerminal.readAllStandardOutput();
if (ret != "")
{
cerr << "拷贝文件夹失败" << endl;
}
//文件夹重命名为当前时间
QDateTime currTime = QDateTime::currentDateTime(); //获取当前时间
QString sCurrTime = currTime.toString("yyyyMMdd_hhmmss"); //转化为字符串
sCurrTime.prepend(mntPath);
QString tempPath = "WorkFiles";
tempPath.prepend(mntPath);
piTerminal.start("mv", QStringList() << tempPath << sCurrTime);
piTerminal.waitForFinished();
if (ret != "")
{
cerr << "文件夹重命名失败" << endl;
}
//卸载U盘设备
piTerminal.start("sudo", QStringList() << "umount" << list);
piTerminal.waitForFinished();
if (ret == "")
{
cout << "U盘已卸载" << endl;
}
else
{
cout << "U盘设备卸载失败" << endl;
}
window->close();
delet window;
window = nullptr;
QMessageBox::information(this, "", "文件拷贝完成,可拔出U盘");
timer_usb->start(1000); //开启定时器
}
}
else if(flagUSB == 1)
{
if(list.count() == 1) //没有检测到USB设备
{
flagUSB = 0;
cout << "U盘已拔出" << endl;
}
}
}
最开始在实现检测U盘插入的时候考虑过单独编写shell实现检测,再将检测结果通过进程间通信“告诉”QT。但发现实现U盘的检测并不复杂,且未找到较为简便的进程间通信方法,最终用QProcess类实现了该功能。如果有更好的实现方式欢迎交流~
✿✿ヽ(°▽°)ノ✿