How can I asynchronously load data from large files in Qt?

前端 未结 4 1839
孤街浪徒
孤街浪徒 2020-12-16 05:13

I\'m using Qt 5.2.1 to implement a program that reads in data from a file (could be a few bytes to a few GB) and visualises that data in a way that\'s dependent on every byt

4条回答
  •  庸人自扰
    2020-12-16 05:48

    1. if you are planing to edit 10GB files forgot about QTextEdit. This ui->hexTextView->insertPlainText will simply eat whole memory before you will read 1/10 of the file. IMO you should use QTableView to present and edit data. To do that you should inherit QAbstractTableModel. In one row you should present 16 bytes. In first 16 columns in hex form and in next column in ASCII form. This shouldn't be to complex. Just read fearfully documentation of QAbstractTableModel. Caching data will be most important here. If I will have a time I will give code example.

    2. Forgot about use of multiple threads. This is bad case to use such thing and most probably you will create lots of problems related with synchronization.

    Ok I had some time here is code which is working (I've test it works smoothly):

    #include 
    #include 
    #include 
    
    class LargeFileCache : public QObject
    {
        Q_OBJECT
    public:
        explicit LargeFileCache(QObject *parent = 0);
    
        char geByte(qint64 pos);
        qint64 FileSize() const;
    
    signals:
    
    public slots:
        void SetFileName(const QString& filename);
    
    private:
        static const int kPageSize;
    
        struct Page {
            qint64 offset;
            QByteArray data;
        };
    
    private:
        int maxPageCount;
        qint64 fileSize;
    
        QFile file;
        QQueue pages;
    };
    
    #include 
    
    class LargeFileCache;
    
    class LageFileDataModel : public QAbstractTableModel
    {
        Q_OBJECT
    public:
        explicit LageFileDataModel(QObject *parent);
    
        // QAbstractTableModel
        int rowCount(const QModelIndex &parent) const;
        int columnCount(const QModelIndex &parent) const;
        QVariant data(const QModelIndex &index, int role) const;
    
    signals:
    
    public slots:
        void setFileName(const QString &fileName);
    
    private:
        LargeFileCache *cachedData;
    };
    
    #include "lagefiledatamodel.h"
    #include "largefilecache.h"
    
    static const int kBytesPerRow = 16;
    
    LageFileDataModel::LageFileDataModel(QObject *parent)
        : QAbstractTableModel(parent)
    {
        cachedData = new LargeFileCache(this);
    }
    
    int LageFileDataModel::rowCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return 0;
        return (cachedData->FileSize() + kBytesPerRow - 1)/kBytesPerRow;
    }
    
    int LageFileDataModel::columnCount(const QModelIndex &parent) const
    {
        if (parent.isValid())
            return 0;
        return kBytesPerRow;
    }
    
    QVariant LageFileDataModel::data(const QModelIndex &index, int role) const
    {
        if (index.parent().isValid())
            return QVariant();
        if (index.isValid()) {
            if (role == Qt::DisplayRole) {
                qint64 pos = index.row()*kBytesPerRow + index.column();
                if (pos>=cachedData->FileSize())
                    return QString();
                return QString::number((unsigned char)cachedData->geByte(pos), 0x10);
            }
        }
    
        return QVariant();
    }
    
    void LageFileDataModel::setFileName(const QString &fileName)
    {
        beginResetModel();
        cachedData->SetFileName(fileName);
        endResetModel();
    }
    
    #include "largefilecache.h"
    
    const int LargeFileCache::kPageSize = 1024*4;
    
    LargeFileCache::LargeFileCache(QObject *parent)
        : QObject(parent)
        , maxPageCount(1024)
    {
    
    }
    
    char LargeFileCache::geByte(qint64 pos)
    {
        // largefilecache
        if (pos>=fileSize)
            return 0;
    
        for (int i=0, n=pages.size(); i=0 && k< pages.at(i).data.size()) {
                pages.enqueue(pages.takeAt(i));
                return pages.back().data.at(k);
            }
        }
    
        Page newPage;
        newPage.offset = (pos/kPageSize)*kPageSize;
        file.seek(newPage.offset);
        newPage.data = file.read(kPageSize);
        pages.push_front(newPage);
    
        while (pages.count()>maxPageCount)
            pages.dequeue();
    
        return newPage.data.at(pos - newPage.offset);
    }
    
    qint64 LargeFileCache::FileSize() const
    {
        return fileSize;
    }
    
    void LargeFileCache::SetFileName(const QString &filename)
    {
        file.close();
        file.setFileName(filename);
        file.open(QFile::ReadOnly);
        fileSize = file.size();
    }
    

    It is shorter then I've expected and it needs some improvement, but it should be a good base.

提交回复
热议问题