二、各模块的基本实现——2. 基于QT+树莓派实现csv文件的写入并拷贝至U盘

各模块的基本实现——2. 基于QT+树莓派实现csv文件的写入并拷贝至U盘

本专题导航,Click Me

文章目录

  • 各模块的基本实现——2. 基于QT+树莓派实现csv文件的写入并拷贝至U盘
  • 前言
  • 一、csv文件的写入
  • 二、将文件拷贝至U盘
    • 1.检测U盘插入
    • 2.挂载U盘
    • 3.拷贝文件
    • 4.重命名文件夹
    • 5.卸载U盘
    • 6.主要代码
  • 总结

前言

这篇文章进一步实现csv文件的创建与写入,并存入U盘。更具体的功能说明如下:

  1. csv文件的名称以当前时间命名,具体格式为:yyyyMMdd_hhmmss(年月日_时分秒);
  2. 可以实现csv文件多行多列内容的写入;
  3. 可自动检测到有U盘插入,并将所有.csv文件拷贝至U盘,文件夹同样以上述格式的当前时间命名,拷贝完成后,在QT界面中提示用户:“文件拷贝完成,可拔出U盘”。

因为在第一章中我们已经完成了交叉编译环境的配置,后面的程序开发均是在PC端的Ubuntu18.04环境下,并将可执行文件放在树莓派上执行。

一、csv文件的写入

csv是逗号分隔值的英文缩写,csv文件是一种用来存储数据的纯文本文件。对csv文件进行的写入主要用到了QT的QFile和QTextStream类,其使用可查看官方的帮助文档,都有相对详细的描述。除了库函数的使用之外,还需要注意两点:

  1. 编码格式
    编码格式可用QTextStream::setCodec函数设置,本文设置为“UTF-8”。在Ubuntu中用LibreOffice打开该编码格式的csv文件时,选择相应的编码格式(UTF-8)不会出现乱码的问题,在windows下用excel直接打开时会出现乱码,因为excel是ANSI编码,而csv文件是UTF-8编码,如下图导入即可显示正常。
    二、各模块的基本实现——2. 基于QT+树莓派实现csv文件的写入并拷贝至U盘_第1张图片
  2. 分隔符
    在进行多行多列写入时,需要首先清楚csv文件的行列分隔符。列分隔符为逗号,即“,”;行分隔符为“\r\n”。

清楚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盘

1.检测U盘插入

U盘属于SCSI硬盘,在linux中被表示为“sdx~”。其中“sd”表示设备类型为SCSI硬盘,“x”为盘号(a为基本盘,b为基本从属盘,c为辅助主盘,d为辅助从属盘),“~”代表分区,前四个分区用1~4表示,它们是主分区或扩展分区,从5开始是逻辑分区。
U盘插入后,/dev 路径下会出现sd开头的文件,比如sdasda4(也有可能是sdbsdb4等等)。我们就根据这个特点去在程序中做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();    //读取命令行中返回的内容

2.挂载U盘

假设U盘对应的标识符为sda4,实现挂载U盘至linux文件系统(本文挂载至 /home/pi/MountUSB)的命令行是:

sudo mount /dev/sda4 /home/pi/MountUSB

其中 mount 命令的第一个参数应为U盘对应的标识符,可以从上一步命令行返回的内容(ret)中提取; mount 命令的第二个参数是挂载的目录,这里设置为 /home/pi/MountUSB
注:需要提前在 /home/pi/ 路径下创建 MountUSB 文件夹。

3.拷贝文件

文件拷贝用的是命令 cp ,由于这里是直接拷贝文件夹(其中包括所有的csv文件),需要参数 -r 实现递归拷贝。命令行代码为:

cp -r /home/pi/WorkFiles/ /home/pi/MountUSB/

其中,命令 cp 的第一个参数为csv文件存放的路径,第二个参数为U盘挂载的路径,实现将所有csv文件(整个文件夹)拷贝至U盘。

4.重命名文件夹

文件(夹)重命名所用命令为 mv,假设当前时间为2022年2月8日,22:18:50,则命令行代码为:

mv /home/pi/MountUSB/WorkFiles /home/pi/MountUSB/20220208_221850

其中,命令mv的第一个参数为拷贝至U盘的文件夹路径;第二个参数为将文件夹重命名为当前时间后的文件夹路径。

5.卸载U盘

完成文件拷贝后即可卸载U盘,所用命令为umount,命令行如下:

sudo umount /dev/sda4

其中,命令umount的参数为U盘对应的标识符,同样从1.检测U盘插入中命令行返回的内容(ret)中提取得到。

6.主要代码

拷贝文件至U盘相关的代码放在定时器溢出信号触发的槽函数中,即定时检测U盘是否插入,若插入则执行:

  1. 挂载U盘
  2. 拷贝文件
  3. 重命名文件夹
  4. 卸载U盘
  5. 提示用户“文件拷贝完成,可拔出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类实现了该功能。如果有更好的实现方式欢迎交流~

✿✿ヽ(°▽°)ノ✿

你可能感兴趣的:(基于机器视觉的仪表读数识别系统,qt,开发语言,ubuntu,c++)