• QTreeWidget实现动态加载本地文件系统


       QT之前没有接触过,之所以做这个也是被临时拉去GoldenFarm组去做渲染的客户端;还别说,虽说是第一次,做出来的这个东西倒是挺让我满意的。先说一下具体需求,然后再上图吧:

            渲染时在选择场景文件时,用户既可以选择网盘(即:服务器上用户的存储目录)中的文件,又可以选择本地文件系统中的文件,而我实现的功能就是后者,也就是要将本地文件系统映射为树结构目录,这里采用动态加载的方式,何谓动态加载呢,就是只有当用户点击之后才会根据你点击的路径,加载该路径下的所有文件或文件夹,而不是一次性将所有的本地文件系统全部映射到目录树中,这样做能很好的解决性能问题。比如:用户开始看到“我的电脑”,然后当用户点击我的电脑后,你的下级目录(即:C盘,D盘,或E盘)才会加载,然后再点击C盘,C盘下的内容又再次加载。看下效果图吧。

          

    实现思路分析:

            这种形式最好的实现方式是利用两个类通过它们之间的协作来实现,一个用来处理本地文件系统扫描,一个用来处理界面的显示,二者之间通过SIGNAL(信号)和SLOT(槽)进行相互协作。类之间的协作关系见下图:

                         

            具体描述为:界面显示类在初始化Init()阶段设置树控件的基本外观以及建立信号与槽的绑定。这里要绑定三个信号与槽:

           1> 本地树控件中的节点点击响应信号与SelectItem槽的绑定,树节点的点击信号是系统行为无需我们手动声明,而SelectItem槽的作用是,根据点击的节点获取它的绝对路径,然后发送信号给文件系统扫描类对该目录进行扫描。在该connect中,发送者是QTreeWidge树控件,接收者是界面显示本身,信号是系统树控件点击信号itemClicked,槽是界面显示类中的selectItem();

           2> 绑定发送出去的信号(界面类中定义的信号:sendToDirScan())与 文件系统扫描类中的处理槽Scan() ,即:对点击树节点后,获取它的绝对路径后,触发sendToDirScan信号,将绝对路径发送出去;而文件系统扫描类中的Scan槽,收到该绝对路径,就开始扫描该绝对路径下的文件或文件夹,并将符合条件的item再发送给界面显示类。在该connect中,发送者是界面显示类,接收者是文件系统扫描类,信号是界面显示类中定义的sendToDirScan,槽是文件系统扫描类中的实现Scan;

           3> 绑定文件系统扫描类中扫描到满足条件item的信号ItemScaned() 与 界面显示类中处理添加树节点的槽AddItem(),即:文件系统扫描类扫描到目录下满足条件的文件或文件夹后,将扫描结果发送给界面显示类进行处理显示。在该connect中,发送者是文件系统扫描类,接收者是界面显示类,信号是文件系统扫描类中定义的ItemScaned(),槽是界面显示类中定义的AddItem();

           从上面的关系我们可以得出这样的一个结论:信号的发送有两种:一种是系统自带的信号;一种是用户自定义的信号。对于前者,我们无需声明,只需要绑定特定的处理槽就好,对于后者我们需要自己定义好对应的信号与槽。而在很多情况下,槽的处理过程中会包含着信号,比如:在响应树节点点击后的槽中,我们获取到绝对路径后就要发送信号;又比如在文件扫描类的扫描槽Scan中,当我们扫描到满足条件的item后,也会发送信号。

    添加节点小算法:

            用户在点击“选择场景文件”后,弹出上面的对话框,用户在开始只能看到“我的电脑”,当用户点击“我的电脑”后,文件系统扫描到C、D、E等盘,发送给界面显示;当用户点击C盘后,文件扫描类扫描C盘下的目录与文件,发送个界面显示类显示。这里我们就面临一个问题:在插入树节点的时候如何判断层级关系?这里我们需要用到哈希结构来保存这一层级关系,哈希结构中保存的是:目录的绝对路径,以及树节点地址。为什么需要保存这两个信息呢?保存目录的绝对路径是为了判断层级关系,保存树节点地址是为了能够取出树的某个节点,当该节点下有其它文件或目录时,可以将其它文件或目录作为子节点插入该节点下。比如:当前存在某个哈希值:<"C:in" , *item>,然后又过来一个路径:"C:inhouqd",注意:在判断时我们要判断:C:in是否存在,即它的上级目录,通过哈希值比较发现C:in存在,则C:inhouqd这个节点应该是item的子节点,然后我们从哈希里面取出item,然后利用C:inhouqd生成item_1,将item_1插入到节点item底下。然后将<"C:inhouqd" , *item_1>再保存下来,如此往复即可。

            注意:在哈希表中我们只插入目录的绝对路径,对于文件不用插入,因为文件不是目录,不会包含其它的文件或目录,即文件下不会形成层级目录。

            情景模拟:1》点击“我的电脑”后,哈希表中存入的值以及界面显示对应的关系为:

                                    

                               2》点击“C:”后,哈希表中存入的值以及界面显示对应的关系为(注意:文件的绝对路径不会保存在Hash表中,因为它不需要记录层级关系):

                                     

                              3》点击“C:in”,哈希表中存入的值以及界面显示对应的关系为:

                                      

            通过上面的分析,应该对实现方式有个很清晰明了的认识了吧!!

    实现代码:

            这里将文件扫描类的代码和界面显示类的代码的.h文件和.cpp文件都贴上吧,赋值下来稍作修改应该可以直接使用:

    文件扫描类DirScan头文件:DirScan.h

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #ifndef DIRSCAN_H  
    2. #define DIRSCAN_H  
    3.   
    4. #include <QObject>  
    5. #include <QFileInfo>  
    6.   
    7. class DirScan : public QObject  
    8. {  
    9.     Q_OBJECT  
    10. public:  
    11.     explicit DirScan(QObject *parent = 0);  
    12.       
    13.     void AsncScan(const QString strPath);  
    14.   
    15. signals:  
    16.     void ItemScaned(const QString &strRootPath, const QFileInfo &ItemInfo , const int i);  
    17.       
    18. protected slots:  
    19.     void Scan(const QString strPath);  
    20.   
    21. private:  
    22.     static int k ;  
    23.       
    24. };  
    25.   
    26. #endif // DIRSCAN_H  

    文件扫描类DirScan的cpp文件:DirScan.cpp

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #include "dirscan.h"  
    2.   
    3. #include <QtCore>  
    4.   
    5. int DirScan::k = 0 ;  
    6.   
    7. DirScan::DirScan(QObject *parent) :  
    8.     QObject(parent)  
    9. {  
    10. }  
    11.   
    12. void DirScan::AsncScan(const QString strPath)  
    13. {  
    14.     QtConcurrent::run(this, &DirScan::Scan, strPath);  
    15. }  
    16.   
    17. void DirScan::Scan(const QString strPath)  
    18. {  
    19.     //! 第一次肯定是显示磁盘驱动器  
    20.     if(k == 0 && !QString::compare(strPath , QString(tr("我的电脑")))){  
    21.         QFileInfoList drivers = QDir::drives();  
    22.         int q = 0 ;  
    23.         do{  
    24.             QFileInfo d = drivers.at(q++);  
    25.   
    26.             qDebug("-----sendBack:%s " , d.filePath().toLatin1().data());  
    27.             emit ItemScaned(strPath , d , k);  
    28.         }while(q < drivers.size());  
    29.         k++ ;  
    30.   
    31.     //! 其它就是盘符下面的内容  
    32.     }else{  
    33.             QDir dir(strPath);  
    34.             if(dir.exists())  
    35.             {  
    36.                 k ++ ;  
    37.                 QFileInfoList fileList = dir.entryInfoList();  
    38.                 int i = 0 ;  
    39.                 do{  
    40.                     QFileInfo file = fileList.at(i++);  
    41.   
    42.                     emit ItemScaned(strPath, file , k);  
    43.   
    44.   
    45.                 }while(i < fileList.size());  
    46.   
    47.             }  
    48.     }  
    49.   
    50.     return ;  
    51. }  

    界面显示类mainwindow的头文件:mainWindow.h

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #ifndef MAINWINDOW_H  
    2. #define MAINWINDOW_H  
    3.   
    4. #include <QMainWindow>  
    5. #include <QFileInfo>  
    6. #include <QTreeWidgetItem>  
    7. #include <QHash>  
    8. #include "dirscan.h"  
    9.   
    10. namespace Ui {  
    11. class MainWindow;  
    12. }  
    13.   
    14. class MainWindow : public QMainWindow  
    15. {  
    16.     Q_OBJECT  
    17.       
    18. public:  
    19.     explicit MainWindow(QWidget *parent = 0);  
    20.     ~MainWindow();  
    21.     void init();  
    22.     QString getItemFullPath(QTreeWidgetItem* item);  
    23.       
    24. protected slots:  
    25.     void AddItem(const QString &strRootPath, const QFileInfo &ItemInfo , const int k );  
    26.     void selectItem(QTreeWidgetItem * , int);  
    27.   
    28. signals:  
    29.     void sendToDirScan(const QString &selectedItem );  
    30.   
    31. private:  
    32.     Ui::MainWindow *ui;  
    33.   
    34. private:  
    35.     DirScan *dirScan ;  
    36.     QList<QTreeWidgetItem *> root ;  
    37.     QString rootPath ;  
    38.   
    39. private:  
    40.     QHash<QString/*path*/, QTreeWidgetItem *> m_StoreDirItem;  
    41. };  
    42.   
    43. #endif // MAINWINDOW_H  

    界面显示类mainwindow的cpp文件:mainwindow.cpp

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #include "mainwindow.h"  
    2. #include "ui_mainwindow.h"  
    3. #include <QMessageBox>  
    4.   
    5. MainWindow::MainWindow(QWidget *parent) :  
    6.     QMainWindow(parent),  
    7.     ui(new Ui::MainWindow)  
    8. {  
    9.     ui->setupUi(this);  
    10.     ui->treeWidget->setColumnCount(1);  
    11.     ui->treeWidget->setHeaderLabel("LocalFileSystem");  
    12.     dirScan = new DirScan();  
    13.   
    14.     //! 绑定扫DirScan扫描完的信号,发送过来添加进树  
    15.     if(!connect(dirScan , SIGNAL(ItemScaned(QString,QFileInfo,int)) , this , SLOT(AddItem(QString,QFileInfo,int)))){  
    16.         qDebug("--error:1-- ");  
    17.     }  
    18.   
    19.     //! 绑定本地树控件的单击事件,经过处理后发送出去,参数为点击的path  
    20.     if(!connect(ui->treeWidget , SIGNAL(itemClicked(QTreeWidgetItem*,int)) , this , SLOT(selectItem(QTreeWidgetItem* , int)))){  
    21.         qDebug("--error:2-- ");  
    22.     }  
    23.   
    24.     //! 绑定发送出去的事件,即发送给DirScan的事件  
    25.     if(!connect(this , SIGNAL(sendToDirScan(QString)) , dirScan , SLOT(Scan(QString)))){  
    26.         qDebug("--error:3-- ");  
    27.     }  
    28.   
    29.     qDebug("--structre function-- ");  
    30. }  
    31.   
    32. MainWindow::~MainWindow()  
    33. {  
    34.     delete ui;  
    35. }  
    36.   
    37.   
    38.   
    39. void MainWindow::init()  
    40. {  
    41.     QTreeWidgetItem *top = new QTreeWidgetItem(ui->treeWidget , QStringList(QString(tr("我的电脑"))));  
    42.     root.append(top);  
    43.     ui->treeWidget->insertTopLevelItems(0 , root);  
    44. }  
    45.   
    46. void MainWindow::AddItem(const QString &strRootPath, const QFileInfo &ItemInfo , const int k)  
    47. {  
    48.   
    49.     if(ItemInfo.isDir()){  
    50.   
    51.         if(ItemInfo.fileName() == QLatin1String(".") || ItemInfo.fileName() == QLatin1String("..")){  
    52.             return ;  
    53.         }  
    54.   
    55.         QString fullPath = ItemInfo.absolutePath().replace("/" , "\") ;  
    56.   
    57. //        if(!QString::compare(strRootPath , rootPath)){  
    58. //           return ;  
    59. //        }  
    60.   
    61.         qDebug("(fullPath: %s) " , fullPath.toLatin1().data());  
    62.   
    63.         // 这个只是第一次走,剩下的肯定都是包含的,因为盘符是在最外面的  
    64.         if(!m_StoreDirItem.contains(fullPath)){  
    65.   
    66.             QString showname = ( k ? ItemInfo.fileName() : ItemInfo.filePath() );  
    67.             qDebug("---not----contains----k = %d ---------showname = %s. " , k , showname.toLatin1().data());  
    68.             QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget->findItems(QString(tr("我的电脑")) , 0 , 0).at(0) , QStringList(showname));  
    69.   
    70.             m_StoreDirItem.insert(fullPath , item);  
    71.   
    72.         }else{  
    73.   
    74.             qDebug("---------contains--------- ");  
    75.             QTreeWidgetItem *item = m_StoreDirItem.value(fullPath);  
    76.             QTreeWidgetItem *item_1 = new QTreeWidgetItem(QStringList(ItemInfo.fileName()));  
    77.   
    78.             int j ;  
    79.             for(j=0 ; j < item->childCount() ; j++){  
    80.                 if(!QString::compare(ItemInfo.fileName() , item->child(j)->text(0))){  
    81.                     break ;  
    82.                 }  
    83.             }  
    84.   
    85.             if(j == item->childCount()){  
    86.                 item->addChild(item_1);  
    87.                 m_StoreDirItem.insert(ItemInfo.absoluteFilePath().replace("/","\") , item_1);  
    88.             }  
    89.         }  
    90.   
    91.     }else if(ItemInfo.isFile()){  
    92.         qDebug(":::::::I am a file. ");  
    93.   
    94.         QString fileFullPath = ItemInfo.filePath();  
    95.         QString filename = ItemInfo.fileName();  
    96.   
    97.         QString file_path ;  
    98.   
    99.         file_path = ItemInfo.absolutePath().replace("/","\");  
    100.   
    101.         qDebug("::::::file_path = %s " , file_path.toLatin1().data());  
    102.   
    103.         if(!m_StoreDirItem.contains(file_path)){  
    104.   
    105.             qDebug("----not contains---- ");  
    106.             QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeWidget , QStringList(file_path));  
    107.             QTreeWidgetItem *item_1 = new QTreeWidgetItem(item , QStringList(filename));  
    108.             item->addChild(item_1);  
    109.   
    110.             m_StoreDirItem.insert(file_path , item);  
    111.         }else{  
    112.   
    113.             qDebug("---contains--- ");  
    114.             QTreeWidgetItem *item = m_StoreDirItem.value(file_path);  
    115.             QTreeWidgetItem *item_1 = new QTreeWidgetItem(QStringList(filename));  
    116.             int j ;  
    117.             for( j = 0 ; j < item->childCount() ; j++){  
    118.                 if(!QString::compare(filename , item->child(j)->text(0))){  
    119.                     break ;  
    120.                 }  
    121.             }  
    122.   
    123.             qDebug("---j = %d and childCount = %d  " , j , item->childCount());  
    124.             if(j == item->childCount()){  
    125.                 item->addChild(item_1);  
    126.             }  
    127.         }  
    128.     }  
    129.   
    130.     return ;  
    131.   
    132. }  
    133.   
    134. void MainWindow::selectItem(QTreeWidgetItem *item, int index)  
    135. {  
    136.      QString itemFullPath = getItemFullPath(item);  
    137.   
    138.      qDebug("send signal and itemFullPath = %s ...  " , itemFullPath.toLatin1().data());  
    139.      emit sendToDirScan(itemFullPath);  
    140.   
    141.      return ;  
    142. }  
    143.   
    144. //! 通过查找父节点,定位某节点的绝对路径  
    145. QString MainWindow::getItemFullPath(QTreeWidgetItem *item)  
    146. {  
    147.     QString itemFullPath = item->text(0);  
    148.   
    149.     while(item->parent() != NULL && QString::compare(item->parent()->text(0) , QString(tr("我的电脑")))){  
    150.         qDebug("------parentPath = %s " , item->parent()->text(0).toLatin1().data());  
    151.         itemFullPath = item->parent()->text(0).replace("/","") + "\" + itemFullPath ;  
    152.         item = item->parent();  
    153.     }  
    154.   
    155.     if(!QString::compare(item->text(0) , QString(tr("我的电脑")))){  
    156.         return QString(tr("我的电脑"));  
    157.     }  
    158.   
    159.     return itemFullPath ;  
    160. }  

    说在最后:

            以上的代码是我进行测试时候的代码,和生产环境中的代码还有一定的区别,代码中有些冗余的地方以及一些改进的地方。改进的地方是:1> Dir::drives()扫描出来的是本地的所有磁盘驱动器,包括系统下可使用的盘以及DVD磁盘驱动器,我们可以通过判断该设备是否是可读以及可写的方式将DVD磁盘驱动器过滤掉。即:d.isReadable() && d.isReadable();2> 只显示符合特定后缀名的文件,例如:只显示".mb"的文件或者其它,取出文件后缀名,然后过滤掉。

            以上就是两天事件的所有工作,在利用哈希这个小算法上还是挺有可鉴之处的。珍惜任何一点点你不懂的地方,然后让它变为你最强的地方,这是鸣人通往火影的生存之道,加油!!

  • 相关阅读:
    Linux (Ubuntu)安装ssh
    Linux (Ubuntu)提示ifconfig:找不到命令
    Docker介绍
    微服务用到的技术
    移动端BI的设计
    Cobbler Web管理(二)
    基于CentOS7环境下的Cobbler部署介绍(一)
    使用google-perftools优化nginx内存管理提升性能
    解决 nginx 配置TLS1.2无效,总是TLS1.0的问题
    在nginx中将爬虫过来的请求转到指定的后端服务
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/4061653.html
Copyright © 2020-2023  润新知