Qt源码之魔改QFileDialog之路-2 移动介质的处理

发布于 2019-12-24  245 次阅读


前言

移动介质,一般就是指当前Linux系统下的 U 盘 / 挂载硬盘。 这两个移动介质都是挂载在/media/yourusername下来做的处理。(fhs2.4)(实际上fhs3.0的标准已经改到/run/media/username下了)

前期调研

从网上找到关于u盘检测的插拔有两种方案(这两种方案硬盘挂载是检测不到的):

U盘插拔检测

QDbus的方案

  • 使用QDbus来检测U盘的插拔的处理,原文链接 http://www.qtcn.org/bbs/read-htm-tid-14535.html

防止网址被吞这里把代码上上

//在pro文件中应该加入
QT +=dbus

#include <QtDBus/QDBusConnection>
//以下为检测设备的插入
QDBusConnection::systemBus().connect( "org.freedesktop.Hal",
                        "/org/freedesktop/Hal/Manager",
                        "org.freedesktop.Hal.Manager",
                        "DeviceAdded",
                        this,
                        SLOT(slotDeviceAdded(QString )));
//以下为检查设备的拨出
QDBusConnection::systemBus().connect( "org.freedesktop.Hal",
                        "/org/freedesktop/Hal/Manager",
                        "org.freedesktop.Hal.Manager",
                        "DeviceRemoved",
                        this,
                        SLOT(slotDeviceRemoved(QString ))); 

//在slotDeviceAdded(QString udi)函数中,要使用到

QDBusInterface device("org.freedesktop.Hal", udi, "org.freedesktop.Hal.Device" , QDBusConnection::systemBus());
//通过HAL可以查询到设备为volume的设备,然后通过判断是否为/dev/sd*的设备,就可以判断出是否为U盘,然后调用mount就可以了。
//这时记录下U盘的UDI,在检测到设备拨出时,再查询一下U盘的UDI是否还在,就知道U盘是否被拨出了。

QSocketNotifier来实现

这个是某个老哥使用socket来实现文件监控的,实际上这个方案完全可以使用QFileSystemWatcher来检测/proc/mounts(实际上Qt源码QSystemWatcher,里边的监控文件的实现就是使用的QSocketNotifier=。=,既然这个老哥自己想出来了,还是把他的链接贴上去吧,https://blog.csdn.net/penghuilater/article/details/53410646 接下来我会讲QFileSystemWatcher的实现原理的),代码我还是照样贴上,防止网页被吞。

#ifndef VOLUMEMONITOR_H
#define VOLUMEMONITOR_H
#include <QObject>
#include <QSet>
#include <QSocketNotifier>
class VolumeMonitor : public QObject
{
    Q_OBJECT
public:
    VolumeMonitor(QObject *parent = 0);
    ~VolumeMonitor();
signals:
    void deviceAdded(const QString& addDev);
    void deviceRemoved(const QString& removeDe);
public slots:
    bool start();
    bool stop();
    bool isRunning();
    void onFileChanged();
private:
    int m_fileKde = -1;
    QSocketNotifier* m_socketNotifier;
    QSet<QString> m_fileContentSet;
};
#endif // VOLUMEMONITOR_H 


//-------------------------------------------------------

#include "volumemonitor.h"

#include <fcntl.h>
#include <unistd.h>
#include <QFile>
#include <QSet>
#include <QTextStream>
#include <QDebug>

namespace {
    const char mountFile[] = "/proc/mounts";
    QSet <QString> getMountFileContent() {
        QFile file(mountFile);
        QSet <QString> fileContentSet;
        if (file.exists()) {
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream textStream(&file);
                QString fileContent = textStream.readAll();

                QStringList items = fileContent.split("\n");
                foreach (QString item, items) {
                        fileContentSet << item;
                }
                file.close();
            }
        }

        return fileContentSet;
    }

    QString getMountPoint(const QString& record) {
        const QStringList items = record.split(" ");
        if (items.length() > 4) {
            return items.at(1);
        } else {
            return "";
        }
    }
}

VolumeMonitor::VolumeMonitor(QObject *parent)
    : QObject(parent)
{
}

bool VolumeMonitor::start() {
    //get the set of mounted device's info;
    m_fileContentSet = getMountFileContent();

    m_fileKde = open(mountFile, O_RDONLY);
    if (m_fileKde == -1) {
        qWarning() << "open /proc/mounts failed!";
        return false;
    }

    m_socketNotifier = new QSocketNotifier(m_fileKde,
                                           QSocketNotifier::Write, this);

    connect(m_socketNotifier, &QSocketNotifier::activated,
            this, &VolumeMonitor::onFileChanged);
    return true;
}

bool VolumeMonitor::isRunning() {
    if (m_fileKde!= -1 && m_socketNotifier) {
        return true;
    } else {
        return false;
    }
}

bool VolumeMonitor::stop() {
    if (this->isRunning()) {
        close(m_fileKde);
        m_fileKde = -1;
        delete m_socketNotifier;
        m_socketNotifier = nullptr;
        return true;
    } else {
        return false;
    }
}
void VolumeMonitor::onFileChanged() {
    QSet <QString> changedItemSet = getMountFileContent();
    for(const QString& item: changedItemSet - m_fileContentSet)
        emit deviceAdded(getMountPoint(item));
    for(const QString& item: m_fileContentSet - changedItemSet)
        emit deviceRemoved(getMountPoint(item));


    m_fileContentSet = changedItemSet;
}

VolumeMonitor::~VolumeMonitor()
{
    this->stop();
}

QStorageInfo

实际上Qt 5.4之后在QtCore中引入了一个新的类叫做QStorageInfo,这个类可以获取到相关的磁盘信息。这个并不是关键的,因为本次我也没有使用这个类,就是在这里简单的提一下。

解决方案

实际上这一篇的关键技术是QFileSystemWatcher,把/media/yourusername添加到监视目录中,然后就可以通过QFileInfo and QDir来获取到相关的信息,其中QFileDialog中的QFileSystemModel也可以跟随实时更新。这样依旧可以依旧获取到移动介质的信息以及插拔信息。这个才是最终的解决方案。接下来要讲一下QFileSystemWatcher的源码。可能你看到的源码版本与我的版本不同。


公交车司机终于在众人的指责中将座位让给了老太太