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:\bin" , *item>,然后又过来一个路径:"C:\bin\houqd",注意:在判断时我们要判断:C:\bin是否存在,即它的上级目录,通过哈希值比较发现C:\bin存在,则C:\bin\houqd这个节点应该是item的子节点,然后我们从哈希里面取出item,然后利用C:\bin\houqd生成item_1,将item_1插入到节点item底下。然后将<"C:\bin\houqd" , *item_1>再保存下来,如此往复即可。

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

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

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

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

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

实现代码:

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

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

[cpp] view plaincopy

  1. #ifndef DIRSCAN_H
  2. #define DIRSCAN_H
  3. #include <QObject>
  4. #include <QFileInfo>
  5. class DirScan : public QObject
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit DirScan(QObject *parent = 0);
  10. void AsncScan(const QString strPath);
  11. signals:
  12. void ItemScaned(const QString &strRootPath, const QFileInfo &ItemInfo , const int i);
  13. protected slots:
  14. void Scan(const QString strPath);
  15. private:
  16. static int k ;
  17. };
  18. #endif // DIRSCAN_H

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

[cpp] view plaincopy

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

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

[cpp] view plaincopy

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include <QMainWindow>
  4. #include <QFileInfo>
  5. #include <QTreeWidgetItem>
  6. #include <QHash>
  7. #include "dirscan.h"
  8. namespace Ui {
  9. class MainWindow;
  10. }
  11. class MainWindow : public QMainWindow
  12. {
  13. Q_OBJECT
  14. public:
  15. explicit MainWindow(QWidget *parent = 0);
  16. ~MainWindow();
  17. void init();
  18. QString getItemFullPath(QTreeWidgetItem* item);
  19. protected slots:
  20. void AddItem(const QString &strRootPath, const QFileInfo &ItemInfo , const int k );
  21. void selectItem(QTreeWidgetItem * , int);
  22. signals:
  23. void sendToDirScan(const QString &selectedItem );
  24. private:
  25. Ui::MainWindow *ui;
  26. private:
  27. DirScan *dirScan ;
  28. QList<QTreeWidgetItem *> root ;
  29. QString rootPath ;
  30. private:
  31. QHash<QString/*path*/, QTreeWidgetItem *> m_StoreDirItem;
  32. };
  33. #endif // MAINWINDOW_H

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

[cpp] view plaincopy

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

说在最后:

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

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

时间: 2024-10-29 05:28:00

QTreeWidget实现动态加载本地文件系统的相关文章

easyui-combobox 动态加载本地数据

<input id="year" class="easyui-combobox" name="year" style="width: 100px;" data-options="valueField:'id',textField:'text'" /> //以年份为例 $(function() { var date = new Date(); var year = date.getFullYear

#iOS问题记录#动态Html加载本地CSS和JS文件

所谓动态Html,指代码中组合生成的html字符串: 若需要加载本地CSS,图片,JS文件,则, 1,需要文件的全路径: 2,需要"file:///"标志: 例如: //获取文件全路径 NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"[email protected]" ofType:@"png"]; //代码加载图片 [_mStrHtmlUrl appendForma

Android动态加载jar/dex

http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html 前言 在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势.本文对网上Android动态加载jar的资料进行梳理和实践在这里与大家一起分享,试图改善频繁升级这一弊病. 声明 欢迎转载,但请保留文章原始出处:) 博客园:http:/

【转】Android类动态加载技术

http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html Android应用开发在一般情况下,常规的开发方式和代码架构就能满足我们的普通需求.但是有些特殊问题,常常引发我们进一步的沉思.我们从沉思中产生顿悟,从而产生新的技术形式. 如何开发一个可以自定义控件的Android应用?就像eclipse一样,可以动态加载插件:如何让Android应用执行服务器上的不可预知的代码?如何对Android应用加密,而只在执行时自解密,从而防

移动端性能优化动态加载JS、CSS

JS CODE (function() { /** * update: * 1.0 */ var version = "insure 1.1.0"; var Zepto = Zepto || null, jQuery = jQuery || null, $ = Zepto || jQuery; var showLoading = false, isUsePackMode = false; // 是否使用合并模式,true则加载分页面合并的JS,CSS if (window.locati

js的动态加载、缓存、更新以及复用(一)

使用范围: OA.MIS.ERP等信息管理类的项目,暂时不考虑网站. 遇到的问题: 完成一个项目,往往需要引用很多js文件,比如jQuery.js.easyUI等.还有自己写的一些列js文件,那么这些文件如何方便的加载,如果文件有变化如何才能让客户端及时更新缓存?如果能够提高点运行效率,那就更好了. 目标: 1.  可以方便的引用js文件. 2.  尽量使用各种缓存,避免频繁从服务器读取文件. 3.  如果js文件有更新或者增加.减少几个减少js文件,需要客户端能够自动.立刻更新. 4.  Js

Linux内核启动及加载根文件系统

</pre></h1><p><span style="font-family:KaiTi_GB2312;font-size:18px;">上接博文<<a target=_blank href="http://blog.csdn.net/gqb_driver/article/details/8931775" style="text-decoration: none; font-family: 'Mi

[AngularJS] 使用AngularAMD动态加载Service

[AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Controller.本篇文章以此为基础,介绍如何使用AngularAMD来动态加载Service,让SPA的启动过程更加轻量化,用以提升使用者的操作体验.并且也透过这样挂载式的设计,让项目功能更加模块化,增加开发与维护的工作效率.主要为自己留个纪录,也希望能帮助到有需要的开发人员. AngularAMD 安装

关于apk加壳之动态加载dex文件

由于自己之前做了一个关于手机令牌的APK软件,在实现的过程中尽管使用了native so进行一定的逻辑算法保护,但是在自己逆向破解的过程中发现我的手机令牌关键数据能够“轻易地”暴露出来,所以我就想进一步的对其进行加固.于是,我使用的网上常用的梆梆加固.爱加密和阿里的聚安全应用来对我的apk进行一个加固保护.加固后,出于好奇心,我想对这些加固的原理进行一个了解,便于我自己能够实现这个加固的方法.于是开始了网上关于这方面的学习,我将这些加固的大致原理进行了一个总结,发现它们实现的最主要的方法就是利用