之前写过一篇动态加载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);
}