文件系统可视化与文件管理

  经过一段时间的学习,终于完成了文件系统的一个可视化与文件管理,简单记录一下整个程序的想法。
  主要分为以下几个模块:

1.界面搭建

界面展示

2.目录树可视化

  此处通过QDir读取文件夹,然后通过QStandardItem创建目录树条目,在QTreeView中呈现。源码如下:

void Widget::loadLogFiles(const QString path, QStandardItemModel *model){
    model->removeRows(0, model->rowCount());

    QDir d(path);
    d.setFilter(QDir::Files           | QDir::Hidden |
                QDir::NoSymLinks      | QDir::AllDirs|
                QDir::NoDotAndDotDot);
    const QFileInfoList list = d.entryInfoList();

    int i=0;
    while(i<list.count()){
        QStyle *style = QApplication::style();
        QString fileType;
        QFileInfo file = list[i];

        LogStandardItem *dir = new LogStandardItem(file.fileName());
        dir->setCheckable(true);
        dir->setToolTip(file.path()+"/"+file.fileName());

        if(file.isDir()){
            dir->setIcon(style->standardIcon(QStyle::SP_DirIcon));
            fileType = "日志文件夹";
        }else{
            dir->setIcon(style->standardIcon(QStyle::SP_FileIcon));
            fileType = file.suffix();
        }

        QString sizeItem = QString::number(file.size());
        int rowItem = model->indexFromItem(dir).row();
        QString timeItem = QDateTime(file.lastModified()).
                toString("yyyy-MM-dd hh:mm:ss");

        model->appendRow(dir);
        model->setItem(rowItem, 1, new LogStandardItem(sizeItem));
        model->setItem(rowItem, 3, new LogStandardItem(timeItem));
        model->setItem(rowItem, 2, new LogStandardItem(fileType));

        if(file.isDir()) loadLogFiles(file.path()+"/"+file.fileName(), dir);
        i++;
    }
}

void Widget::loadLogFiles(const QString path, QStandardItem *model){
    QDir d(path);
    d.setFilter(QDir::Files | QDir::Hidden |
                QDir::NoSymLinks | QDir::AllDirs|
                QDir::NoDotAndDotDot);
    const QFileInfoList list = d.entryInfoList();

    int i=0;
    while(i<list.count()){
        QStyle *style = QApplication::style();
        QString fileType;
        QFileInfo file = list[i];

        LogStandardItem *dir = new LogStandardItem(file.fileName());
        dir->setCheckable(true);
        dir->setToolTip(file.path()+"/"+file.fileName());

        if(file.isDir()){
            dir->setIcon(style->standardIcon(QStyle::SP_DirIcon));
            fileType = "日志文件夹";
        }else{
            dir->setIcon(style->standardIcon(QStyle::SP_FileIcon));
            fileType = file.suffix();
        }
        
        QString sizeItem = QString::number(file.size());
        QString timeItem = QDateTime(file.lastModified()).
                toString("yyyy-MM-dd hh:mm:ss");

        model->appendRow(dir);
        int rowItem = dir->index().row();
        model->setChild(rowItem, 1, new LogStandardItem(sizeItem));
        model->setChild(rowItem, 3, new LogStandardItem(timeItem));
        model->setChild(rowItem, 2, new LogStandardItem(fileType));

        if(file.isDir()) loadLogFiles(dir->toolTip(), dir);
        i++;
    }
}

3.复选框三态的实现

  由于设置复选框是针对每一个条目QStandardItem的,其本身是没有半选状态的,所以我们要重新实现QStandardItem的三态,从而让其具有父子之间的联系。我们创建一个继承QStandardItem类的子类,重写其setData函数,代码如下:

void LogStandardItem::setData(const QVariant &value, int role){
    if(role == Qt::CheckStateRole){
        Qt::CheckState CheckState = (Qt::CheckState) value.toInt();
        switch(CheckState){
        case Qt::Unchecked:{
            for(int i=0; i<rowCount(); i++){
                child(i)->setData(Qt::Unchecked, Qt::CheckStateRole);
            }
            QStandardItem::setData(value, role);
            if(parent()) parent()->setData(Qt::PartiallyChecked, role);
        }
            return;
        case Qt::PartiallyChecked:{
            Qt::CheckState current_state = checkState();
            int CheckedNum = 0;
            int UnCheckedNum = 0;
            bool isPartially = false;
            Qt::CheckState ChildState;
            int m_rowCount = rowCount();
            for(int i=0; i<m_rowCount; i++){
                ChildState = child(i)->checkState();
                switch(ChildState){
                case Qt::PartiallyChecked:		isPartially = true;break;
                case Qt::Unchecked:				UnCheckedNum++;break;
                case Qt::Checked: 				CheckedNum++; break;
                default:						CheckedNum++;break;
                }
            }

            Qt::CheckState NowState;
            if(isPartially)                     NowState = Qt::PartiallyChecked;
            else if(UnCheckedNum == m_rowCount) NowState = Qt::Unchecked;
            else if(CheckedNum == m_rowCount)   NowState = Qt::Checked;
            else                                NowState = Qt::PartiallyChecked;

            if(current_state != NowState){
                QStandardItem::setData(NowState, role);
                if(parent()) parent()->setData(Qt::PartiallyChecked, role);
            }
        }
            return;
        case Qt::Checked:{
            for(int i=0, num = rowCount(); i<num; i++){
                child(i)->setData(Qt::Checked, Qt::CheckStateRole);
            }
            QStandardItem::setData(value, role);
            if(parent()) parent()->setData(Qt::PartiallyChecked, role);
        }
            return;
        default:
            break;
        }
    }
    QStandardItem::setData(value, role);
}

实现了复选框的三态,其实就可以实现删除选中的文件或者文件夹的功能了。

4.双击显示日志内容

  对于此功能,我们采用子线程去读取日志内容,然后将读取的内容显示于控件当中。首先我们创建关于此功能的信号与槽:

connect(m_pLogTree, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(loadLogContent(QModelIndex)));

然后将所点击的目录对应的路径传输于子线程,设置信号与槽,在子线程读取完成以后主线程将内容显示。槽函数如下:

void Widget::loadLogContent(const QModelIndex &index){
    QStandardItem *item = m_pLogTreeModel->itemFromIndex(index);
    QFileInfo f(item->toolTip());

    if(f.isFile()){
        connect(&m_tShowLogContentThread, SIGNAL(readOverSignal()),
                this, SLOT(displayContent()));
        m_pContentDisplayWid->setToolTip(item->toolTip());

        m_tShowLogContentThread.m_sPath = item->toolTip();
        m_tShowLogContentThread.start();
        m_tShowLogContentThread.wait();
        m_pStatusBar->setText("正在浏览日志:"+f.fileName());
    }
}

在子线程中读取:

void ReadLogThread::run(){
    mutex.lock();
    QFile file(m_sPath);
    QTextStream contentRead(&file);
    QString content;
    m_lLogContent.clear();
    while(!contentRead.atEnd()){
        content = contentRead.readLine();
        m_lLogContent.append(content);
    }
    emit readOverSignal();
    mutex.unlock();
}

通过信号readOverSignal激活主线程槽函数displayContent,通过其来将内容显示于QListWidget中:

void Widget::displayContent(){
    QStringList disContent = m_tShowLogContentThread.m_lLogContent;

    // 如果日志数大于0,展示第一页,如果日志数=0, 依然显示0
    if(disContent.length() == 0) FlushContentPage(0);
    else FlushContentPage(1);
}

void Widget::FlushContentPage(int page){
    m_nContentPage = page;

    while(m_pContentDisplayWid->count()>0){
        QListWidgetItem *item = m_pContentDisplayWid->takeItem(0);
        delete item;
    }

    if(m_nContentPage>0){
        QStringList disContent = m_tShowLogContentThread.m_lLogContent;

        for(int i=(m_nContentPage-1)*19; i<m_nContentPage*19 &&
            i<m_tShowLogContentThread.m_lLogContent.length(); i++)
        {
            QListWidgetItem *Item = new QListWidgetItem(m_pContentDisplayWid);
            Item->setSizeHint(QSize(0, 20));
            Item->setText(qPrintable(m_tShowLogContentThread.m_lLogContent[i]));
            m_pContentDisplayWid->addItem(Item);
        }
    }
    QString pageInformation = QString::number(m_nContentPage) + "/" +
            QString::number(qCeil(m_tShowLogContentThread.
                                  m_lLogContent.length()*1.0/19*1.0));
    m_pShowPg->setText(pageInformation);
}

5.为QListWidget增加翻页功能

先创建控件,通过QSignalMapper 将多个按钮统一到一块,点击不同的按钮,QSignalMapper则会得到不同的索引值,从而根据索引值,来判断索要进行的操作。

QSignalMapper *btMapper = new QSignalMapper;
connect(m_pDelPgsBtn, SIGNAL(clicked(bool)), btMapper, SLOT(map()));
connect(m_pDelPgBtn, SIGNAL(clicked(bool)), btMapper, SLOT(map()));
connect(m_pAddPgBtn, SIGNAL(clicked(bool)), btMapper, SLOT(map()));
connect(m_pAddPgsBtn, SIGNAL(clicked(bool)), btMapper, SLOT(map()));
btMapper->setMapping(m_pDelPgsBtn, 0);
btMapper->setMapping(m_pDelPgBtn, 1);
btMapper->setMapping(m_pAddPgBtn, 2);
btMapper->setMapping(m_pAddPgsBtn, 3);
connect(btMapper, SIGNAL(mapped(int)), this, SLOT(TurnPage(int)));

然后创建对应的槽函数,以实现翻页功能:

void Widget::TurnPage(const int index){
    int page = m_nContentPage;

    switch (index) {
    case 0:     //向前翻十页
        if(page >10) 			FlushContentPage(page-10);
        else if(page > 0) 		FlushContentPage(1);
        else 					FlushContentPage(0);
        break;
    case 1:     // 向前翻一页
        if(page >1) 			FlushContentPage(page-1);
        else if(page == 0) 		FlushContentPage(0);
        break;
    case 2:     // 向后翻一页
        if(page < qCeil(m_tShowLogContentThread.m_lLogContent.length() * 1.0/19 * 1.0) && page != 0)
        						FlushContentPage(page+1);
        else if(page == 0) 		FlushContentPage(0);
        break;
    case 3:     // 向后翻十页
        if(page <= qCeil(m_tShowLogContentThread.m_lLogContent.length() * 1.0 / 19 * 1.0) - 10 && page != 0) 
        						FlushContentPage(page+10);
        else if(page != 0){page = qCeil(m_tShowLogContentThread.m_lLogContent.length()*1.0/19*1.0);
            					FlushContentPage(page);
        }else 					FlushContentPage(0);
        break;
    }
}

6.检测文件夹

  如果文件夹中有新的文件,则展示于程序中,如果程序中某些对应的文件夹删除,则将其一并删除:同样此功能采用子线程来实现,子线程我们设置为2秒钟扫描一次文件夹:

void Widget::checkFileAndTree(){
    QTimer *tim = new QTimer();
    tim->start(2000);
    connect(tim, SIGNAL(timeout()), this, SLOT(checkFileAndTreeOnce()));
}

void Widget::checkFileAndTreeOnce(){
    m_tUpdateTreeThread.start();
    m_tUpdateTreeThread.wait();
}

检测函数放于子线程中,此处呈现部分代码,其他的无非QStandardItemModel换成QStandardItem及之前的添加函数:

void UpdateTreeThread::run(){
    mutex.lock();
    checkFileInTree(m_sPath, m_tModel);
    checkTreeInFile(m_tModel);
    mutex.unlock();
}

void UpdateTreeThread::checkFileInTree(QString path, QStandardItemModel *model){
    QDir d(path);
    d.setFilter(QDir::Files             | QDir::Hidden |
                QDir::NoSymLinks        | QDir::AllDirs|
                QDir::NoDotAndDotDot);
    const QFileInfoList list = d.entryInfoList();

    if(!list.length()) model->removeRows(0, model->rowCount());
    else if(!model->rowCount()) addLists(path, model);
    else{
        int i=0;
        while(i<list.length()){
            QFileInfo file = list[i];
            QString filePath = file.path()+"/"+file.fileName();
            bool fileExist = false;
            int j=0;
            
            while(j<model->rowCount()){
                if(filePath == model->item(j)->toolTip()){
                    if(!fileExist){
                        fileExist = true;
                        if(file.isDir()) checkFileInTree(filePath, model->item(j));
                        j++;
                    }else model->removeRow(model->item(j)->row());
                }else j++;
            }

            if(!fileExist){
                QStyle *style = QApplication::style();
                QString fileType;

                LogStandardItem *item = new LogStandardItem(file.fileName());
                item->setCheckable(true);
                item->setToolTip(file.path()+"/"+file.fileName());

                if(file.isDir()){
                    item->setIcon(style->standardIcon(QStyle::SP_DirIcon));
                    fileType = "日志文件夹";
                }else{
                    item->setIcon(style->standardIcon(QStyle::SP_FileIcon));
                    fileType = file.suffix();
                }

                QString sizeItem = QString::number(file.size());
                QString timeItem = QDateTime(file.lastModified()).
                        toString("yyyy-MM-dd hh:mm:ss");

                model->appendRow(item);
                int rowItem = item->index().row();
                model->setItem(rowItem, 1, new LogStandardItem(sizeItem));
                model->setItem(rowItem, 3, new LogStandardItem(timeItem));
                model->setItem(rowItem, 2, new LogStandardItem(fileType));

                if(file.isDir()) addLists(item->toolTip(), item);
            }
            i++;
        }
    }
}

void UpdateTreeThread::checkTreeInFile(QStandardItemModel *model){
    if(model->rowCount() == 0)return;
    int i=0;
    while(i<model->rowCount()){
        QString path = model->item(i)->toolTip();
        QFileInfo file(path);
        if(!file.exists()) model->removeRow(model->item(i)->row());
        else{
            if(file.isDir()) checkTreeInFile(model->item(i));
            i++;
        }
    }
}

最终就可以实现上述文件呈现及对应的操作的功能。
  这段程序也有不足的地方,记录一下,以供以后努力:
1.代码模块化能力较弱
2.对于线程的使用水平太低
3.对于程序运行的成本考虑太少,严重浪费计算资源

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值