之前写过一篇动态加载ComboBox,可参见下面这篇文章
QT之动态加载下拉框(QComboBox)
同理QTreeWidget也可以实现动态加载,在一些异步加载数据,并且数据加载比较耗时,非常实用。
效果
原理分析
要实现此类效果,要拆解下功能项:
第一步,自定义绘制树的分支图标
//查询qt文档,drawBranches接口可以实现自定义绘制分支图标
virtual void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
第二步,绘制一个动态图标显示在原来的分支图标上。
源码
TreeWidget.h
class TreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit TreeWidget(QWidget* parent = nullptr);
virtual ~TreeWidget();
/**
* @brief setGifPath
* 设置动态图片路径
* @param _path
*/
void setGifPath(const QString& _path);
/**
* @brief startMovie
* 开始动画
*/
void startMovie();
/**
* @brief stopMovie
* 停止播放动画
*/
void stopMovie();
protected:
virtual void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override;
private:
QMovie *m_movie = nullptr;
};
TreeWidget.cpp
TreeWidget::TreeWidget(QWidget *parent)
{
m_movie = new QMovie(this);
connect(m_movie,&QMovie::updated,this,[this](const QRect& rect){
this->viewport()->update();
});
}
TreeWidget::~TreeWidget()
{
if(nullptr != m_movie)
{
m_movie->deleteLater();
}
}
//设置动态图片路径
void TreeWidget::setGifPath(const QString &_path)
{
m_movie->setFileName(_path);
}
//开始播放动画
void TreeWidget::startMovie()
{
if(nullptr != m_movie)
{
m_movie->start();
}
}
//停止播放动画
void TreeWidget::stopMovie()
{
if(nullptr != m_movie)
{
m_movie->stop();
}
this->viewport()->update();
}
//绘制分支
void TreeWidget::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
{
bool _isload = index.data(Qt::UserRole + 1).toBool();
if(!_isload)
{
return QTreeWidget::drawBranches(painter,rect,index);
}
if(nullptr == m_movie)
{
return;
}
int w = this->indentation();
auto top = rect.topRight();
top.setX(top.x() - w);
QRect _rect(top,rect.bottomRight());
QPixmap _pix = m_movie->currentPixmap();
_pix = _pix.scaled(_rect.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation);
painter->drawPixmap(_rect,_pix);
}
使用
//先构建树
m_tree = new TreeWidget(this);
m_tree->setHeaderHidden(true);
m_tree->setGifPath(":/icon/imgs/loading.gif");
QTreeWidgetItem* item = new QTreeWidgetItem({"root"});
m_tree->addTopLevelItem(item);
QHBoxLayout* _layout = new QHBoxLayout(this);
this->setLayout(_layout);
_layout->addWidget(m_tree);
connect(m_tree,&TreeWidget::itemClicked,this,&CusBranchTreeWidget::slot_itemClicked);
//槽函数及模拟耗时操作
void CusBranchTreeWidget::slot_itemClicked(QTreeWidgetItem *item,int col)
{
if(item->childCount() != 0)
{
return;
}
item->setData(0,Qt::UserRole + 1,true);
m_tree->startMovie();
//模拟加载数据
addChildItems(item);
}
//模拟加载数据
void CusBranchTreeWidget::addChildItems(QTreeWidgetItem *parentItem)
{
QVector<QTreeWidgetItem*> items;
for(int i = 0; i < 5; i++)
{
auto uid = QUuid::createUuid().toString();
auto item = new QTreeWidgetItem({uid.left(5)});
items << item;
//模拟耗时操作
QEventLoop _loop;
QTimer::singleShot(500,&_loop,&QEventLoop::quit);
_loop.exec();
}
for(auto& it : items)
{
parentItem->addChild(it);
}
//停止加载动画
parentItem->setData(0,Qt::UserRole + 1,false);
m_tree->stopMovie();
m_tree->expandItem(parentItem);
}