treeitem.h
#pragma once
#include <QVariant>
class TreeItem
{
public:
explicit TreeItem(const QString &folderName, const QString &folderPath, bool newFolder, TreeItem *parent = nullptr);
~TreeItem();
void appendChild(TreeItem *child);
TreeItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
int row() const;
TreeItem *parentItem();
QList<TreeItem *> m_childItems;
QString m_folderName;
QString m_folderPath;
bool m_newFolder;
TreeItem *m_parentItem;
};
treeitem.cpp
#include "treeitem.h"
TreeItem::TreeItem(const QString &folderName, const QString &folderPath, bool newFolder, TreeItem *parent)
: m_folderName{folderName}, m_folderPath{folderPath}, m_newFolder{newFolder}, m_parentItem{parent}
{
}
TreeItem::~TreeItem()
{
qDeleteAll(m_childItems);
}
void TreeItem::appendChild(TreeItem *child)
{
m_childItems.append(child);
}
TreeItem *TreeItem::child(int row)
{
if (row < 0 || row >= m_childItems.count()) {
return nullptr;
}
return m_childItems.at(row);
}
int TreeItem::childCount() const
{
return m_childItems.count();
}
int TreeItem::columnCount() const
{
return 1;
}
QVariant TreeItem::data(int column) const
{
switch (column) {
case 0:
return m_folderName;
case 1:
return m_folderPath;
case 3:
return m_newFolder;
default:
return QVariant();
}
}
int TreeItem::row() const
{
if (m_parentItem) {
return m_parentItem->m_childItems.indexOf(const_cast<TreeItem *>(this));
}
return 0;
}
TreeItem *TreeItem::parentItem()
{
return m_parentItem;
}
treemodel.h
#pragma once
#include <QAbstractItemModel>
#include "treeitem.h"
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TreeModel(QObject *parent = nullptr);
~TreeModel();
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
public slots:
Q_INVOKABLE void showQModelIndex(const QModelIndex &index) const;
Q_INVOKABLE void createFolder(const QModelIndex &index);
Q_INVOKABLE void removeFolder(const QModelIndex &index);
Q_INVOKABLE void saveFolder(const QModelIndex &index, const QString folderName);
Q_INVOKABLE void delAllChild(const QModelIndex &index);
Q_INVOKABLE void delAllChildAndSelf(const QModelIndex &index);
Q_INVOKABLE void queryAllChild(const QModelIndex &index);
private:
TreeItem *m_rootItem;
enum Roles {
FolderNameRole = Qt::UserRole + 1,
FolderPathRole,
NewFolderRole,
ChildCountRole,
};
};
treemodel.cpp
#include "treemodel.h"
TreeModel::TreeModel(QObject *parent)
: QAbstractItemModel(parent)
{
m_rootItem = new TreeItem("", "", false);
for (int i = 0; i < 10; i++) {
QString fileName = QString("Home222333333%1=").arg(i);
m_rootItem->appendChild(new TreeItem(fileName, m_rootItem->m_folderPath + "/" + fileName, false, m_rootItem));
}
TreeItem *item_4 = m_rootItem->child(4);
for (int i = 0; i < 5; i++) {
QString fileName = QString("Item6666666666%1=").arg(i);
item_4->appendChild(new TreeItem(fileName, item_4->m_folderPath + "/" + fileName, false, item_4));
}
item_4->m_childItems.at(0)->appendChild(new TreeItem("====================", item_4->m_childItems.at(0)->m_folderPath + "/" , false, item_4->m_childItems.at(0)));
}
TreeModel::~TreeModel()
{
delete m_rootItem;
}
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
TreeItem *parentItem;
if (!parent.isValid()) {
parentItem = m_rootItem;
} else {
parentItem = static_cast<TreeItem *>(parent.internalPointer());
}
TreeItem *childItem = parentItem->child(row);
if (childItem) {
return createIndex(row, column, childItem);
}
return QModelIndex();
}
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem *>(index.internalPointer());
TreeItem *parentItem = childItem->parentItem();
if (parentItem == m_rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int TreeModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return m_rootItem->m_childItems.count();
}
TreeItem *parentItem = static_cast<TreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
int TreeModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QHash<int, QByteArray> TreeModel::roleNames() const {
QHash<int, QByteArray> roleNames;
roleNames.insert(TreeModel::FolderNameRole, QByteArray("folderName"));
roleNames.insert(TreeModel::FolderPathRole, QByteArray("folderPath"));
roleNames.insert(TreeModel::NewFolderRole, QByteArray("newFolder"));
roleNames.insert(TreeModel::ChildCountRole, QByteArray("childCount"));
return roleNames;
}
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
switch (role) {
case FolderNameRole:
return item->m_folderName;
case FolderPathRole:
return item->m_folderPath;
case NewFolderRole:
return item->m_newFolder;
case ChildCountRole:
return item->childCount();
default:
return QVariant();
}
}
void TreeModel::showQModelIndex(const QModelIndex &index) const
{
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
qDebug() << "Show QModelIndex " << index << " internal pointer " << index.internalPointer()
<< " name = " << item->m_folderName << ", path = " + item->m_folderPath;
}
void TreeModel::createFolder(const QModelIndex &index)
{
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
beginInsertRows(index, 0, 0);
item->appendChild(new TreeItem("New Folder", item->m_folderPath + "/" + "New Folder", true, item));
endInsertRows();
dataChanged(index, index);
}
void TreeModel::removeFolder(const QModelIndex &index) {
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
beginRemoveRows(index, 0, 0);
item->m_childItems.remove(0);
endRemoveRows();
dataChanged(index, index);
}
void TreeModel::saveFolder(const QModelIndex &index, const QString folderName) {
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
item->m_folderName = folderName;
item->m_newFolder = false;
dataChanged(index, index);
}
void TreeModel::delAllChild(const QModelIndex &index) {
qDebug() << "index.row() = " << index.row() << ", index.column() = " << index.column();
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
if (item->childCount() > 0) {
beginRemoveRows(index, 0, item->childCount() - 1);
item->m_childItems.clear();
endRemoveRows();
dataChanged(index, index);
}
}
void TreeModel::delAllChildAndSelf(const QModelIndex &index) {
qDebug() << "index.row() = " << index.row() << ", index.column() = " << index.column();
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
if (item->m_parentItem) {
qDebug() << "parent delete row = " << item->row();
beginRemoveRows(index.parent(), item->row(), item->row());
item->m_parentItem->m_childItems.removeAt(item->row());
endRemoveRows();
}
}
void TreeModel::queryAllChild(const QModelIndex &parentIndex) {
TreeItem *item = static_cast<TreeItem *>(parentIndex.internalPointer());
int count = rowCount(parentIndex);
qDebug() << "parentName = " << item->m_folderName << ", child count = " << count;
for (int i = 0; i < count; i++) {
QModelIndex childIndex = index(i, 0, parentIndex);
TreeItem *childItem = static_cast<TreeItem *>(childIndex.internalPointer());
qDebug() << "i = " << i << ".name = " << childItem->m_folderName << ",path = " << childItem->m_folderPath;
childItem->m_folderName = "hello";
dataChanged(childIndex, childIndex);
}
}
main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "treemodel.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
TreeModel *model = new TreeModel();
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("_model", model);
engine.load(QUrl(QStringLiteral("../main.qml")));
return QApplication::exec();
}
main.qml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
Window {
width: 600
height: 400
visible: true
Rectangle {
width: childrenRect.width
height: childrenRect.height
border.color: "red"
TreeView {
width: 400
height: 400
id: _view
property string chooseFolder
property int chooseRow
property int maxWidth: 0
boundsBehavior: Flickable.StopAtBounds
// The model needs to be a QAbstractItemModel
model: _model
delegate: Item {
onImplicitWidthChanged: {
// console.log("width ===========" + implicitWidth)
_view.maxWidth = Math.max(_view.maxWidth, implicitWidth)
}
id: treeDelegate
implicitWidth: (arrow.width + fileIcon.width + 4 + 20 * treeDelegate.depth) + aaa.implicitWidth
implicitHeight: 32
readonly property real indent: 20
readonly property real padding: 5
// Assigned to by TreeView:
required property TreeView treeView
required property bool isTreeNode
required property bool expanded
required property int hasChildren
required property int depth
Rectangle {
y: 10
width: parent.implicitWidth
height: 1
color: "red"
}
Rectangle {
y: 30
width: aaa.width
height: 1
color: "green"
Text {
text: aaa.width
}
}
Rectangle {
y: 40
width: bbb.width
height: 1
color: "blue"
Text {
text: bbb.width
}
}
//
// Rectangle {
// y: 40
// width: 20 * treeDelegate.depth
// height: 1
// color: "black"
// Text {
// text: 20 * treeDelegate.depth
// }
// }
Rectangle {
// width: arrow.width + fileIcon.width + 4 + aaa.width + bbb.width + 20 * treeDelegate.depth + 600
width: _view.maxWidth <= 400 ? 400 : _view.maxWidth
// width: _view.maxWidth
height: childrenRect.height
color: _view.chooseFolder === model.folderPath ? "yellow" : "transparent"
RowLayout {
spacing: 0
height: 32
Image {
id: arrow
source: "arrow_right.png"
opacity: treeDelegate.isTreeNode && treeDelegate.hasChildren ? 1 : 0
rotation: treeDelegate.expanded ? 90 : 0
Layout.leftMargin: 20 * treeDelegate.depth
MouseArea {
anchors.fill: parent
onClicked: treeView.toggleExpanded(row)
}
}
Image {
id: fileIcon
source: "file.png"
Layout.leftMargin: 4
MouseArea {
anchors.fill: parent
onClicked: {
// _view.model.delAllChildAndSelf(_view.index(row, 0)) //删除当前节点数据
// console.log(_view.model.rowCount(_view.index(row, 0))) //查询当前节点孩子数量
/*
遍历当前index下的所有孩子index
var parentIndex = _view.index(row, 0)
var childCount = _view.model.rowCount(parentIndex)
console.log("childCount = " + childCount)
for (var i = 0; i < childCount; i++) {
var childIndex = _view.model.index(i, 0, parentIndex);
_view.model.showQModelIndex(childIndex)
}
*/
console.log("cur r = " + row);
var parentIndex = _view.index(row, 0) //注意是_view.index, 不是_view.model.index
_view.model.queryAllChild(parentIndex)
}
}
}
Text {
id: aaa
text: model.folderName + "(" + model.childCount + ")" + ",r = " + row + ".column" + column + ",index = " + index
visible: model.newFolder === false
Layout.rightMargin: 100
MouseArea {
anchors.fill: parent
onClicked: {
_view.chooseFolder = model.folderPath
_view.chooseRow = row
}
onDoubleClicked: {
var item = _view.index(row, 0)
_view.model.createFolder(item)
if (!expanded) {
treeView.expand(row);
}
}
}
}
TextInput {
id: bbb
text: model.folderName
color: "#222222";
// font.pixelSize: 14
visible: model.newFolder === true
width: model.newFolder === true ? implicitWidth : 0
onAccepted: {
console.log("User pressed Enter key.");
_view.model.saveFolder(_view.index(row, 0), text)
treeView.toggleExpanded(row - 1)
treeView.toggleExpanded(row - 1)
}
}
}
}
// Rectangle {
// width: rowLayout.width
// height: 1
// color: "red"
// }
// property int childrenCount: _view.model.queryChildrenCount(_view.index(row, column))
// property bool isChoose: _view.model.getChooseFolderPath() === model.folderPath
// RowLayout {
// id: rowLayout
// spacing: 0
// height: parent.height
//
// Image {
// id: indicator
// source: "arrow_right.png"
// opacity: treeDelegate.isTreeNode && treeDelegate.hasChildren ? 1 : 0
// rotation: treeDelegate.expanded ? 90 : 0
// Layout.leftMargin: 20 * treeDelegate.depth
// MouseArea {
// anchors.fill: parent
// onClicked: {
// //1111
// var item = _view.index(row, 0)
// _view.model.showQModelIndex(item)
// //222
// console.log("onClicked, row = " + row + ",column = " + column + ",count = " + model.childCount)
// // console.log(_view.model.data(item, 0) + ", " + _view.model.data(item, 1) + ", " + _view.model.data(item, 2))
// // _view.model.updateChooseFolderPath(item)
//
//
// treeView.toggleExpanded(row) //展开或者收起
// }
// }
// }
//
// Image {
// source: "file.png"
// Layout.leftMargin: 4
// }
//
// Text {
// text: model.folderName + "(" + model.childCount + ")" /* + ",r = " + row + ".column" + column + ",index = " + index*/
// color: _view.chooseFolder === model.folderPath ? "red" : "#222222";
// font.pixelSize: 14
// Layout.leftMargin: 4
// visible: model.newFolder === false
// MouseArea {
// anchors.fill: parent
// onClicked: {
// _view.chooseFolder = model.folderPath
// _view.chooseRow = row
// }
// onDoubleClicked: {
// var item = _view.index(_view.chooseRow, 0)
// _view.model.createFolder(item)
// if (!expanded) {
// treeView.expand(row);
// }
// }
// }
// }
//
// Item {
// implicitWidth: model.newFolder === true ? edit.width + 8 : 0
// implicitHeight: model.newFolder === true? edit.height + 8 : 0
// visible: model.newFolder === true
// Layout.leftMargin: 4
// Rectangle {
// anchors.fill: parent
// border.color: "#0473FF"
// TextInput {
// id: edit
// anchors.centerIn: parent
// text: model.folderName
// color: "#222222";
// font.pixelSize: 14
// onAccepted: {
// console.log("User pressed Enter key.");
// _view.model.saveFolder(_view.index(row, 0), text)
// }
// }
// }
// }
//
// // Row {
// // visible: model.newFolder === false
// // Button {
// // text: "add"
// // onClicked: {
// // var item = _view.index(row, 0)
// // _view.model.createFolder(item)
// // // _view.model.showQModelIndex(item)
// // if (!expanded) {
// // treeView.expand(row);
// // }
// // // treeView.toggleExpanded(row)
// // }
// // }
// //
// // Button {
// // text: "remove"
// // onClicked: {
// // var item = _view.index(row, 0)
// // _view.model.removeFolder(item)
// // // treeView.collapse(row)
// // // if (model.childCount === 0) {
// // //
// // // }
// // // _view.model.showQModelIndex(item)
// // }
// // }
// // }
// }
}
}
}
}
运行效果
