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

前言

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

前期调研

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

U盘插拔检测

QDbus的方案

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

//在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的源码。可能你看到的源码版本与我的版本不同。


文章作者: 张小飞
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 张小飞 !
 上一篇
QFileSystemWatcher源码剖析(Linux) QFileSystemWatcher源码剖析(Linux)
简介QFileSystemWatcher的作用是监视本地文件夹的变化以及文件的变化。 概述QFileSystemWatcher的实现类是QFileSystemWatcherPrivate。 其中QFileSystemWatcherPriva
2020-06-27 张小飞
下一篇 
Qt源码之魔改QFileDialog之路-1 Qt源码之魔改QFileDialog之路-1
前言之前着急发Linux社区版本的WPS,所以这里都没有好好的去更改打开本地文件对话框那里的逻辑。由于这里的代码是从其它平台迁移过来的,所以这里的逻辑就没有动。对于用户交互来讲可以说是非常恶心心了。因为我们是做产品的,产品驱动技术。这次呢,
2020-06-26 张小飞
  目录