QTreeWidget
阅读数:27 评论数:0
跳转到新版页面分类
C/C++
正文
一、概述
QTreeWidget
是 Qt 框架中的一个控件,用于以树形结构展示数据。它是 QTreeView
的一个高级版本,提供了一个基于项目(item-based)的接口来管理和显示层次数据。使用 QTreeWidget
,你可以添加、移除和修改树中的项目(QTreeWidgetItem
对象),每个项目可以有一个或多个列(column)来显示不同的数据。
二、创建和设置QTreeWidget
QTreeWidget *treeWidget = new QTreeWidget(parent);
// 设置列数
treeWidget->setColumnCount(3);
// 设置列标题
QStringList headers;
headers << "Column 1" << "Column 2" << "Column 3";
treeWidget->setHeaderLabels(headers);
三、添加项目
// 创建一个树项目
QTreeWidgetItem *treeItem = new QTreeWidgetItem(treeWidget);
// 设置项目的列数据
treeItem->setText(0, "Item 1 Column 1");
treeItem->setText(1, "Item 1 Column 2");
treeItem->setText(2, "Item 1 Column 3");
// 将项目添加到树中
treeWidget->addTopLevelItem(treeItem);
四、添加子项目
// 创建子项目
QTreeWidgetItem *childItem = new QTreeWidgetItem();
// 设置子项目的列数据
childItem->setText(0, "Child Item Column 1");
childItem->setText(1, "Child Item Column 2");
childItem->setText(2, "Child Item Column 3");
// 将子项目添加到父项目中
treeItem->addChild(childItem);
五、选择和迭代
// 获取当前选中的项目
QTreeWidgetItem *selectedItem = treeWidget->currentItem();
// 遍历所有顶级项目
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = treeWidget->topLevelItem(i);
// 对每个顶级项目做一些处理
}
// 遍历子项目
for (int i = 0; i < treeItem->childCount(); ++i) {
QTreeWidgetItem *child = treeItem->child(i);
// 对每个子项目做一些处理
}
六、信号和槽
// 当项目被选中时发出
connect(treeWidget, &QTreeWidget::itemSelectionChanged, this, &YourClass::onItemSelectionChanged);
// 当项目被双击时发出
connect(treeWidget, &QTreeWidget::itemDoubleClicked, this, &YourClass::onItemDoubleClicked);
// ...在 YourClass 中实现相应的槽函数
七、自定义item
你可以通过继承 QTreeWidgetItem
类来创建自定义的树项目,并添加自定义的行为和属性。
class CustomTreeWidgetItem : public QTreeWidgetItem {
public:
CustomTreeWidgetItem(QTreeWidget *parent) : QTreeWidgetItem(parent) {
// 自定义初始化代码
}
// ... 自定义方法和属性
};
八、树的外观和行为
// 设置选择模式
treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
// 启用拖放
treeWidget->setDragEnabled(true);
treeWidget->setAcceptDrops(true);
treeWidget->setDropIndicatorShown(true);
设置勾选及选择的样式
setObjectName("motorTree");
setStyleSheet("#motorTree {"
"border: none;"
"font-weight: 400;"
"font-size: 20px;"
"color: #18181A;"
"}"
"#motorTree::item {"
"border: none;"
"}"
"#motorTree::item:selected {"
"color: #2961B2;"
"background-color: rgba(41,91,178,0.12);"
"}"
"#motorTree::indicator {"
"width: 16px;"
"height: 16px;"
"}"
"#motorTree::indicator:checked {"
" image: url(:/images/checked.png);"
"}"
"#motorTree::indicator:unchecked {"
"image: url(:/images/unchecked.png);"
"}"
"");
去掉列标题
QTreeWidget *treeWidget = new QTreeWidget(this);
treeWidget->header()->setVisible(false); // 隐藏列标题
去掉边框
treeWidget->setStyleSheet("QTreeWidget { border: none; }");
设置item上现边距
class TreeItemStyledDelegate:public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
QSize size = QStyledItemDelegate::sizeHint(option, index);
// 调整高度以包含边距
size.setHeight(size.height() + 20); // 增加20像素的边距
return size;
}
};
#endif
// 设置委托到你的treeWidget
treeWidget->setItemDelegate(new ItemDelegate(treeWidget));
增加可点击的图标
setColumnCount(2);
// 创建一个复选框图标
QIcon checkIcon(":/icons/check_icon.png");
// 创建一个项并设置复选框图标
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setText(0, "Item 1");
item->setCheckState(0, Qt::Unchecked); // 设置为未选中状态
item->setIcon(1, checkIcon); // 在第二列设置复选框图标
// 为复选框图标的点击事件连接一个槽函数
connect(treeWidget, &QTreeWidget::itemClicked, [=](QTreeWidgetItem *item, int column) {
if (column == 1) { // 第二列的复选框图标被点击
if (item->checkState(0) == Qt::Unchecked) {
item->setCheckState(0, Qt::Checked);
} else {
item->setCheckState(0, Qt::Unchecked);
}
}
});
九、滚动条
int MotorTree::calTotalHeight(QTreeWidgetItem *item){
int totalHeight = visualItemRect(item).height();
if(item->isExpanded()){
for(int i=0;i<item->childCount();++i){
totalHeight += calTotalHeight(item->child(i));
}
}
return totalHeight;
}
void MotorTree::updateScrollBars(){
int contentHeight = 0;
for(int i=0;i<topLevelItemCount();++i){
contentHeight += calTotalHeight(topLevelItem(i));
}
int contentWidth = header()->length();
setVerticalScrollBarPolicy(contentHeight>maximumHeight()? Qt::ScrollBarAlwaysOn: Qt::ScrollBarAsNeeded);
setHorizontalScrollBarPolicy(contentWidth>maximumWidth()? Qt::ScrollBarAlwaysOn: Qt::ScrollBarAsNeeded);
}
...
connect(this,&MotorTree::itemExpanded,this,&MotorTree::updateScrollBars);
connect(this,&MotorTree::itemCollapsed,this,&MotorTree::updateScrollBars);
QHeaderView
类的 length()
方法返回的是 header 的总长度。这个长度是 header 中所有列的长度之和,不管它们是否可见。因此,如果你的 QTreeWidget
有多列,header()->length()
就会返回所有这些列宽度的总和。
十、使用json文件读写
1、json文件
[
{
"type": "folder",
"id": "1",
"parentId": "",
"title": "Root"
},
{
"type": "file",
"id": "2",
"parentId": "1",
"title": "Child 1"
},
{
"type": "file",
"id": "3",
"parentId": "1",
"title": "Child 2"
},
{
"type": "file",
"id": "4",
"parentId": "2",
"title": "Grandchild of Child 1"
}
]
(2)从json文件读取数据到QTreeWidget
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QFile>
#include <QTreeWidget>
void loadTreeWidgetFromJson(QTreeWidget* treeWidget, const QString& fileName) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly) {
qWarning("Cannot open file for reading");
return;
}
QByteArray jsonData = file.readAll();
file.close();
QJsonDocument document = QJsonDocument::fromJson(jsonData);
QJsonArray jsonArray = document.array();
QMap<QString, QTreeWidgetItem*> itemMap;
for (const QJsonValue& value : jsonArray) {
QJsonObject obj = value.toObject();
QString id = obj["id"].toString();
QString parentId = obj["parentId"].toString();
QString title = obj["title"].toString();
QTreeWidgetItem* parentItem = itemMap.value(parentId, nullptr);
QTreeWidgetItem* item = new QTreeWidgetItem(parentItem ? parentItem : treeWidget);
item->setText(0, title);
item->setData(0, Qt::UserRole, QVariant(id));
item->setData(0, Qt::UserRole + 1, QVariant(obj["type"].toString()));
if (!parentItem) {
treeWidget->addTopLevelItem(item);
}
itemMap[id] = item;
}
}
在Qt中,当你尝试以QIODevice::WriteOnly
或QIODevice::WriteOnly | QIODevice::Truncate
模式打开一个文件时,如果文件不存在,QFile::open()
方法会自动创建这个文件。
(3)将QTreeWidget写回到json文件
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QFile>
#include <QTreeWidget>
void saveTreeWidgetToJson(QTreeWidget* treeWidget, const QString& fileName) {
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning("Cannot open file for writing");
return;
}
QJsonArray jsonArray;
QTreeWidgetItemIterator it(treeWidget);
while (*it) {
QTreeWidgetItem* item = *it;
QJsonObject obj;
obj["title"] = item->text(0);
obj["id"] = item->data(0, Qt::UserRole).toString();
obj["type"] = item->data(0, Qt::UserRole + 1).toString();
obj["parentId"] = item->parent() ? item->parent()->data(0, Qt::UserRole).toString() : "";
jsonArray.append(obj);
++it;
}
QJsonDocument document(jsonArray);
file.write(document.toJson());
file.close();
}
在Qt的模型/视图编程中,每个QTreeWidgetItem
可以存储多个与之关联的数据项。每个数据项都与一个角色相关联。角色是一个整数值,用于指定数据项的用途。例如,Qt::DisplayRole
用于存储显示给用户的文本,Qt::DecorationRole
用于存储图标等。
Qt::UserRole
是一个在Qt中预定义的角色,其值比所有预定义角色的值都要大。它的目的是允许开发者存储自定义的数据。从Qt::UserRole
开始的角色值被保留用于用户自定义的数据。这意味着你可以使用Qt::UserRole
和比它大的值来存储任何你需要的额外信息,而不会与Qt的标准角色冲突。
十一、实现项的拖拽
// 启用拖放
treeWidget->setDragEnabled(true);
treeWidget->setAcceptDrops(true);
treeWidget->setDropIndicatorShown(true);
treeWidget->setDragDropMode(QAbstractItemView::InternalMove);
// 对于每个QTreeWidgetItem,设置拖放标志
QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setText(0, "Item Title");
item->setData(0, Qt::UserRole, QVariant(itemId)); // itemId为该项目的ID
item->setData(0, Qt::UserRole + 1, QVariant(parentId)); // parentId为父项目的ID
item->setFlags(item->flags() | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
当你使用QAbstractItemView::InternalMove
作为参数调用setDragDropMode
时,你告诉QTreeWidget
启用内部项的拖放,并且只允许在同一控件内移动项(而不是从其他控件拖入或拖出)。这意味着用户可以通过拖放来重新排列项,但不能将它们拖到QTreeWidget
外部,也不能从外部拖入项。
void MyTreeWidget::dropEvent(QDropEvent *event)
{
// 处理拖放事件,确定放置位置,移动项目等
QTreeWidget::dropEvent(event);
// 这里可以添加代码来更新项目的parentId
}
void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
// 处理拖动事件,决定是否允许移动到某个位置
QTreeWidget::dragMoveEvent(event);
// 这里可以添加代码来提供更多自定义行为
}
十二、获取所有勾选项
void MotorTree::getCheckedItemRecursive(QTreeWidgetItem* item){
if(item->checkState(0)==Qt::Checked){
QString id = item->data(0,Qt::UserRole).toString();
checkItemIdList.append(id);
}
for(int i=0;i<item->childCount();++i){
getCheckedItemRecursive(item->child(i));
}
}
QString MotorTree::getCheckedItemIds(){
checkItemIdList.clear();
for(int i=0;i<topLevelItemCount();++i){
getCheckedItemRecursive(topLevelItem(i));
}
return checkItemIdList.join(",");
}