Bootstrap

QTreeWidget使用记录(2)

目的:使用QTreeWidget来浏览硬盘目录的文件结构。

功能要求:

1.选择某一磁盘根目录后,显示根目录下的文件和文件夹,且显示对应的图标;

2.单击列表项的箭头区域展开和折叠,展开时加载下一层级的文件和文件夹;

3.单击列表项的图标或者文本区域为选中状态;

4.样式设置,包括边框,表头视图、底色、选中、鼠标Hover,滚动条的样式。

实现方案:

2.单击列表项的箭头区域展开和折叠,展开时加载下一层级的文件和文件夹;

默认情况下单击QTreeWidgetItem可展开折叠,即连接一下信号槽即可:

connect(ui.m_treeWidget, &QTreeWidget::itemClicked, this, &CDemoSettingsView::slotClicked);

但是本文单击需要选中Item,不展开,所以设计为子类化QTreeWidgetItem,通过事件过滤来重发itemClick信号。参考代码实现如下:

bool CDirsTreeWidget::eventFilter(QObject * watched, QEvent * event)
{
    if (watched == viewport())
    {
        if (event->type() == QEvent::MouseButtonPress) 
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                QTreeWidgetItem *item = this->itemAt(mouseEvent->pos());
                int count = this->indexOfTopLevelItem(item);
                QRect rc = this->visualItemRect(item);
                if (item != nullptr) {
                    QPoint pos = mouseEvent->pos();
                    if (pos.x() < rc.x()) {
                        m_arrowPress = true;
                    }
                }
            }
        }
        else if (event->type() == QEvent::MouseButtonRelease)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            if (mouseEvent->button() == Qt::LeftButton) {
                QTreeWidgetItem *item = this->itemAt(mouseEvent->pos());
                if (item != nullptr) {
                    QPoint pos = mouseEvent->pos();
                    QRect rc = this->visualItemRect(item);
                    if (pos.x() < rc.x()) {
                        if (m_arrowPress)
                        {
                            emit itemClicked(item, 0);
                            m_arrowPress = false;
                            return true;
                        }
                    }
                }
            }

        }

    }
    
    return QTreeWidget::eventFilter(watched, event); // 事件未被接受,传递给父类处理
}

其中    QRect rc = this->visualItemRect(item);可获得箭头X的右侧范围,通过判断这个值就可以限定到点击点头才发送itemClick信号,从而实现展开/折叠、选中分开。然后再itemClick的槽函数中进一步获取文件和文件夹。参考代码如下:

  void CDemoSettingsView::slotClicked(QTreeWidgetItem * item, int column)
{
    if (item->data(0, Qt::UserRole + 1).toBool())   // 为true,已经添加过了,不用再添加了
        return;
    item->setData(0, Qt::UserRole + 1, true);

    QString file = item->data(0, Qt::UserRole).toString() + item->text(0);


    QFileInfo* fileInfo = new QFileInfo(file);
    if (fileInfo->isDir()) {
        getFileOnDirectory(item, file);    // 是文件夹就获取里面的文件,添加子节点
    }
    item->setExpanded(true);
}
    
    void CDemoSettingsView::getFileOnDirectory(QTreeWidgetItem* item, QString directoryPath)
{
    QDir dir(directoryPath);
    QFileInfoList fileInfoList = dir.entryInfoList();
    QFileIconProvider iconProvider;

    for (QFileInfo fileInfo : fileInfoList) {
        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;
        QString filepath = fileInfo.path();
        QString filename = fileInfo.fileName();
        QTreeWidgetItem* child = new QTreeWidgetItem();
        child->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
        child->setText(0, filename);
        child->setIcon(0, QIcon(iconProvider.icon(fileInfo)));
        child->setData(0, Qt::UserRole, filepath+"/"); 
        child->setData(0, Qt::UserRole + 1, false); 
        item->addChild(child); 
    }
}

;