我在写MarkdownOne的时候, 需要有一个类似各大IDE都有的ProjectExplorer这样的东西. 我需要的功能比较简单, 就是显示外加一些简单操作而已. 因为自己平日里爱极了QtCreator这个IDE, 它的ProjectExplorer是相当赞的. 于是一开始我就想反正QtCreator是插件机制的各个模块之间应该耦合度比较低, 直接看它的源码下一些功夫应该可以把这部分给抠出来. 但是打开QtCreator1.0的源码看的时候, 发现比我预想的要复杂, 并不是一时半会儿就能搞定的. 还是自己写吧, 反正要的功能也不多, 自己实现也还算简单, 无非数据, 显示, 操作, 这三样还有个专业术语MVC.实现效果如下(红色区域部分):
因为目录结构其实是一个树形结构, 那么在显示方面, 当属TreeView了. 而Model呢, 自己递归遍历目录生成对应的数据, 也简单, 但除了自己实现之外还有更简洁的东西, Qt为我们提供了QFileSystemModel. 而在Qt里面没有Control的概念而是用Delegate来代替, 反正也差不多. 在这里ProjectExplorerView, ProjectExplorerModel, ProjectExplorerItemDelegate 分别继承于QTreeView,QFileSystemModel和QStyledItemDelegate.
在这其中, 其实ProjectExplorerView不是必须的, 因为QTreeView已经可以满足我们的需求, 这样做把一些内部实现放到了派生类中, 用户不必关心这些细节, 以方便以后再其他地方重复利用, 代码比较简单, 不废话了上代码:
//ProjectExplorerView.h
#ifndef PROJECTEXPLORERVIEW_H
#define PROJECTEXPLORERVIEW_H
#include <QTreeView>
class ProjectExplorerModel;
class ProjectExplorerItemMenu;
class ProjectExplorerMenu;
class ProjectExplorerView : public QTreeView
{
Q_OBJECT
public:
explicit ProjectExplorerView(QWidget *parent = 0);
/**
* @brief setRootPath Sets the directory that is being watched by the model to newPath
* @param path
*/
void setRootPath(const QString &path);
signals:
void signalDoubleClickedFile(const QString &filePath);
void signalDoubleClickedDir(const QString &dirPath);
public slots:
protected:
void mousePressEvent(QMouseEvent *event);
private:
void initSetting();
void initData();
void initGui();
private:
ProjectExplorerModel *pModel_;
ProjectExplorerItemMenu *pItemMenu_;
ProjectExplorerMenu *pMenu_;
};
#endif // PROJECTEXPLORERVIEW_H
//ProjectExplorerView.cpp
#include "ProjectExplorerView.h"
#include <QDebug>
#include <QFileInfo>
#include <QModelIndex>
#include <QMouseEvent>
#include "ProjectExplorerItemDelegate.h"
#include "ProjectExplorerModel.h"
#include "ProjectExplorerItemMenu.h"
#include "ProjectExplorerMenu.h"
ProjectExplorerView::ProjectExplorerView(QWidget *parent)
: QTreeView(parent)
, pModel_(NULL)
, pItemMenu_(NULL)
, pMenu_(NULL)
{
initSetting();
initData();
initGui();
}
void ProjectExplorerView::setRootPath(const QString &path)
{
pModel_->setRootPath(path);
this->setRootIndex(pModel_->index(path));
}
void ProjectExplorerView::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() == Qt::RightButton)
{
QModelIndex index = indexAt(event->pos());
if(!index.isValid())
{
qDebug() << "点了空白处";
pMenu_->move(event->globalPos());
pMenu_->show();
}
else
{
pItemMenu_->move(event->globalPos());
qDebug() << pModel_->fileName(index);
pItemMenu_->show();
}
event->accept();
return;
}
event->ignore();
return QTreeView::mousePressEvent(event);
}
void ProjectExplorerView::initSetting()
{
//this->setAccessibleName("Name");
//按键响应编辑
this->setEditTriggers(QAbstractItemView::EditKeyPressed);//只响应键盘的编辑事件
//this->setContextMenuPolicy(Qt::CustomContextMenu);
}
void ProjectExplorerView::initData()
{
pModel_ = new ProjectExplorerModel(this);
this->setModel(pModel_);
connect(this, &ProjectExplorerView::doubleClicked, [=](const QModelIndex &index){
QString path = pModel_->filePath(index);
QFileInfo info(path);
if(info.isDir())
{
Q_EMIT signalDoubleClickedDir(path);
}
else if(info.isFile())
{
Q_EMIT signalDoubleClickedFile(path);
}
else if(info.isSymLink())
{
QString source = info.symLinkTarget();
qDebug() << "这个快捷方式是? " << source;
}
else
{
qDebug() << "这是毛? " << info.path();
}
});
pMenu_ = new ProjectExplorerMenu(this);
pItemMenu_ = new ProjectExplorerItemMenu(this);
this->setItemDelegate(new ProjectExplorerItemDelegate());
}
void ProjectExplorerView::initGui()
{
}
或许一些也学Qt的小伙伴看到这一串代码中connect那一部分有点懵
connect(this, &ProjectExplorerView::doubleClicked, [=](const QModelIndex &index){
QString path = pModel_->filePath(index);
QFileInfo info(path);
if(info.isDir())
{
Q_EMIT signalDoubleClickedDir(path);
}
else if(info.isFile())
{
Q_EMIT signalDoubleClickedFile(path);
}
else if(info.isSymLink())
{
QString source = info.symLinkTarget();
qDebug() << "这个快捷方式是? " << source;
}
else
{
qDebug() << "这是毛? " << info.path();
}
});