使用 QTreeWidget 实现复杂树控件功能的详细教程

在本教程中,我们将使用 Qt 的 QTreeWidget 控件实现一个复杂的树控件功能。该树控件包含多个层级的项目,并支持右键菜单操作。我们将详细介绍 QTreeWidget 的使用方法,包括控件布局、列设置、项目管理、查询、排序、信号处理等。以下是具体内容:

1. 效果展示

2.QTreeWidget具体接口

2.1 控件布局

在 Qt 中,可以通过 QVBoxLayout 或 QHBoxLayout 等布局管理器来布置 QTreeWidget 控件。

QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(treeWidget);
QWidget *centralWidget = new QWidget(this);
centralWidget->setLayout(layout);
setCentralWidget(centralWidget);
2.2 设置列

可以使用 setColumnCount 方法设置列数,并通过 setHeaderLabels 设置列标题。

treeWidget->setColumnCount(3);
QStringList headers;
headers << QString::fromLocal8Bit("班级")
        << QString::fromLocal8Bit("分配科目")
        << QString::fromLocal8Bit("是否达标");
treeWidget->setHeaderLabels(headers);
2.3 设置宽高

通过 resize 方法设置 QTreeWidget 的宽高。

treeWidget->resize(800, 600);
2.4 设置列表头

使用 setHeaderLabels 方法来设置 QTreeWidget 的列头。

QStringList headers;
headers << QString::fromLocal8Bit("班级")
        << QString::fromLocal8Bit("分配科目")
        << QString::fromLocal8Bit("是否达标");
treeWidget->setHeaderLabels(headers);
2.5 设置复选框

可以通过 setCheckState 方法为项目添加复选框。

QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
item->setText(0, QString::fromLocal8Bit("某班级"));
item->setCheckState(2, Qt::Unchecked);
2.6 添加树
2.6.1 添加顶层项目

使用 addTopLevelItem 方法添加顶层项目。

QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(treeWidget);
topLevelItem->setText(0, QString::fromLocal8Bit("顶层项目"));
treeWidget->addTopLevelItem(topLevelItem);
2.6.2 插入顶层项目

使用 insertTopLevelItem 方法插入顶层项目。

QTreeWidgetItem *newTopLevelItem = new QTreeWidgetItem();
newTopLevelItem->setText(0, QString::fromLocal8Bit("插入顶层项目"));
treeWidget->insertTopLevelItem(0, newTopLevelItem);
2.6.3 添加子项

使用 addChild 方法为顶层项目添加子项。

QTreeWidgetItem *childItem = new QTreeWidgetItem();
childItem->setText(0, QString::fromLocal8Bit("子项目"));
topLevelItem->addChild(childItem);
2.7 删除树
2.7.1 删除子项目

使用 removeChild 方法删除子项目。

topLevelItem->removeChild(childItem);
2.7.2 删除顶级条目

使用 takeTopLevelItem 方法删除顶层项目。

QTreeWidgetItem *removedItem = treeWidget->takeTopLevelItem(0);
delete removedItem;
2.8 查询树
2.8.1 查询对应条目

使用 findItems 方法根据条件查询条目。

QList<QTreeWidgetItem*> items = treeWidget->findItems(QString::fromLocal8Bit("查询内容"), Qt::MatchExactly);
2.8.2 查询对应序号

使用 indexOfTopLevelItem 方法获取顶层项目的索引。

int index = treeWidget->indexOfTopLevelItem(topLevelItem);
2.9 树条目操作
2.9.1 当前选中行列

使用 currentItem 方法获取当前选中的条目,并使用 currentColumn 方法获取当前选中的列。

QTreeWidgetItem *currentItem = treeWidget->currentItem();
int currentColumn = treeWidget->currentColumn();
2.9.2 设置条目选中

使用 setSelected 方法设置条目选中状态。

currentItem->setSelected(true);
2.9.3 设置选中行

使用 setCurrentItem 方法设置当前选中的条目。

treeWidget->setCurrentItem(topLevelItem);
2.9.4 设置选中列

使用 setCurrentItem 方法设置当前选中的条目和列。

treeWidget->setCurrentItem(topLevelItem, 1);
2.9.5 当前选中行 上一个选中行

使用 currentItempreviousItem 方法获取当前选中和上一个选中的条目。

QTreeWidgetItem *currentItem = treeWidget->currentItem(); QTreeWidgetItem *previousItem = treeWidget->itemAbove(currentItem);
2.10 树排序
2.10.1 设置自动排序

使用 setSortingEnabled 方法启用或禁用自动排序。

treeWidget->setSortingEnabled(true);
2.10.2 升序降序

使用 sortItems 方法按列进行升序或降序排序。

treeWidget->sortItems(0, Qt::AscendingOrder); treeWidget->sortItems(1, Qt::DescendingOrder);
2.11 树查找

使用 findItems 方法查找符合条件的项目。

QList<QTreeWidgetItem*> foundItems = treeWidget->findItems(QString::fromLocal8Bit("查找内容"), Qt::MatchExactly);
2.12 清空树

使用 clear 方法清空树控件中的所有项目。

treeWidget->clear();
2.13 折叠树

使用 collapseItem 方法折叠指定项目。

treeWidget->collapseItem(topLevelItem);
2.14 展开树

使用 expandItem 方法展开指定项目。

treeWidget->expandItem(topLevelItem);
2.15 滚动指定条目

使用 scrollToItem 方法滚动到指定项目。

treeWidget->scrollToItem(topLevelItem);
2.16 树单击信号

连接 itemClicked 信号以处理单击事件。

connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(onItemClicked(QTreeWidgetItem*, int)));
2.17 树双击信号

连接 itemDoubleClicked 信号以处理双击事件。

connect(treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
2.18 树展开信号

连接 itemExpanded 信号以处理项目展开事件。

connect(treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(onItemExpanded(QTreeWidgetItem*)));
2.19 树折叠信号

连接 itemCollapsed 信号以处理项目折叠事件。

connect(treeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(onItemCollapsed(QTreeWidgetItem*)));
2.20 列数据变化信号

连接 itemChanged 信号以处理项目数据变化事件。

connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(onItemChanged(QTreeWidgetItem*, int)));

3.程序源码展示

3.1main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MainWindow window;
    window.show();

    return app.exec();
}

3.2 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTreeWidget>
#include <QMenu>
#include <QAction>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onAssignSubject();
    void onPromoteToKeyClass();
    void onMoveToAssignedClass();
    void onMoveToUnassignedKeyClass();
    void onCancelPromotion();
    void onContextMenuRequested(const QPoint &pos);

private:
    void createTreeWidget();
    void createContextMenu();

    QTreeWidget *treeWidget;
    QMenu *contextMenu;
    QMenu *assignMenu;
    QAction *promoteToKeyClassAction;
    QAction *moveToAssignedClassAction;
    QAction *moveToUnassignedKeyClassAction;
    QAction *cancelPromotionAction;

    enum Column { Class = 0, Subject, Standard };
};

#endif // MAINWINDOW_H

3.3 mainwindow.cpp

#include "mainwindow.h"
#include <QVBoxLayout>
#include <QStringList>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    treeWidget(new QTreeWidget(this)),
    contextMenu(new QMenu(this)),
    assignMenu(new QMenu(QString::fromLocal8Bit("分配科目"), this)),
    promoteToKeyClassAction(new QAction(QString::fromLocal8Bit("提升重点班级"), this)),
    moveToAssignedClassAction(new QAction(QString::fromLocal8Bit("提升"), this)),
    moveToUnassignedKeyClassAction(new QAction(QString::fromLocal8Bit("提升"), this)),
    cancelPromotionAction(new QAction(QString::fromLocal8Bit("取消提升"), this))
{
    setWindowTitle(QString::fromLocal8Bit("班级管理"));
    resize(800, 600);

    createTreeWidget();
    createContextMenu();

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(treeWidget);
    QWidget *centralWidget = new QWidget(this);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

	connect(treeWidget, SIGNAL(customContextMenuRequested(const QPoint&)),
		this, SLOT(onContextMenuRequested(const QPoint&)));
}

MainWindow::~MainWindow() {}

void MainWindow::createTreeWidget()
{
    treeWidget->setColumnCount(3);
    QStringList headers;
    headers << QString::fromLocal8Bit("班级")
            << QString::fromLocal8Bit("分配科目")
            << QString::fromLocal8Bit("是否达标");
    treeWidget->setHeaderLabels(headers);
    treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);

    // 创建根节点
	QTreeWidgetItem *assignedClass = new QTreeWidgetItem(treeWidget, QStringList(QString::fromLocal8Bit("已分配科目班级")));
	QTreeWidgetItem *unassignedKeyClass = new QTreeWidgetItem(treeWidget, QStringList(QString::fromLocal8Bit("未分配科目重点班级")));
	QTreeWidgetItem *allClasses = new QTreeWidgetItem(treeWidget, QStringList(QString::fromLocal8Bit("全部班级")));

	QStringList grades;
	grades << QString::fromLocal8Bit("一年级")
		<< QString::fromLocal8Bit("二年级")
		<< QString::fromLocal8Bit("三年级")
		<< QString::fromLocal8Bit("四年级")
		<< QString::fromLocal8Bit("五年级");

	QStringList classes;
	classes << QString::fromLocal8Bit("一班")
		<< QString::fromLocal8Bit("二班")
		<< QString::fromLocal8Bit("三班")
		<< QString::fromLocal8Bit("四班");

	for (int i = 0; i < grades.size(); ++i) {
		QTreeWidgetItem *gradeItem = new QTreeWidgetItem(allClasses, QStringList(grades[i]));
		for (int j = 0; j < classes.size(); ++j) {
			QTreeWidgetItem *classItem = new QTreeWidgetItem(gradeItem, QStringList(classes[j]));
		}
	}
}

void MainWindow::createContextMenu()
{
	assignMenu->addAction(QString::fromLocal8Bit("语文"), this, SLOT(onAssignSubject()));
	assignMenu->addAction(QString::fromLocal8Bit("数学"), this, SLOT(onAssignSubject()));
	assignMenu->addAction(QString::fromLocal8Bit("英语"), this, SLOT(onAssignSubject()));
	assignMenu->addAction(QString::fromLocal8Bit("体育"), this, SLOT(onAssignSubject()));
	assignMenu->addAction(QString::fromLocal8Bit("思政"), this, SLOT(onAssignSubject()));

	contextMenu->addMenu(assignMenu);
	contextMenu->addAction(promoteToKeyClassAction);

	connect(promoteToKeyClassAction, SIGNAL(triggered()), this, SLOT(onPromoteToKeyClass()));
	connect(moveToAssignedClassAction, SIGNAL(triggered()), this, SLOT(onMoveToAssignedClass()));
	connect(moveToUnassignedKeyClassAction, SIGNAL(triggered()), this, SLOT(onMoveToUnassignedKeyClass()));
	connect(cancelPromotionAction, SIGNAL(triggered()), this, SLOT(onCancelPromotion()));
}

void MainWindow::onAssignSubject()
{
    QAction *action = qobject_cast<QAction*>(sender());
    if (!action)
        return;

    QTreeWidgetItem *currentItem = treeWidget->currentItem();
    if (currentItem && currentItem->parent()) {
        QString currentText = currentItem->text(Column::Subject);
        QString newSubject = action->text();

        if (!currentText.contains(newSubject)) {
            if (!currentText.isEmpty()) {
                currentText +=QString::fromLocal8Bit( "、");
            }
            currentText += newSubject;
            currentItem->setText(Column::Subject, currentText);
        }
    }
}

void MainWindow::onPromoteToKeyClass()
{
    QTreeWidgetItem *currentItem = treeWidget->currentItem();
    if (!currentItem || !currentItem->parent())
        return;

    QTreeWidgetItem *itemCopy = currentItem->clone();
    treeWidget->findItems(QString::fromLocal8Bit("未分配科目重点班级"), Qt::MatchExactly).first()->addChild(itemCopy);
}

void MainWindow::onMoveToAssignedClass()
{
    QTreeWidgetItem *currentItem = treeWidget->currentItem();
    if (!currentItem || !currentItem->parent())
        return;

    QTreeWidgetItem *itemCopy = currentItem->clone();
    itemCopy->setCheckState(Column::Standard, Qt::Checked);
    treeWidget->findItems(QString::fromLocal8Bit("已分配科目班级"), Qt::MatchExactly).first()->addChild(itemCopy);

    delete currentItem;
}

void MainWindow::onMoveToUnassignedKeyClass()
{
    QTreeWidgetItem *currentItem = treeWidget->currentItem();
    if (!currentItem || !currentItem->parent())
        return;

    QTreeWidgetItem *itemCopy = currentItem->clone();
    itemCopy->setCheckState(Column::Standard, Qt::Unchecked);
    treeWidget->findItems(QString::fromLocal8Bit("未分配科目重点班级"), Qt::MatchExactly).first()->addChild(itemCopy);

    delete currentItem;
}

void MainWindow::onCancelPromotion()
{
    QTreeWidgetItem *currentItem = treeWidget->currentItem();
    if (!currentItem || !currentItem->parent())
        return;

    QTreeWidgetItem *itemCopy = currentItem->clone();
    itemCopy->setCheckState(Column::Standard, Qt::Unchecked);
    treeWidget->findItems(QString::fromLocal8Bit("未分配科目重点班级"), Qt::MatchExactly).first()->addChild(itemCopy);

    delete currentItem;
}

void MainWindow::onContextMenuRequested(const QPoint &pos)
{
    QTreeWidgetItem *item = treeWidget->itemAt(pos);
    if (!item)
        return;

    if (item->text(0) == QString::fromLocal8Bit("已分配科目班级") || item->text(0) == QString::fromLocal8Bit("未分配科目重点班级") || item->text(0) == QString::fromLocal8Bit("全部班级"))
        return;

    if (item->parent() && item->parent()->text(0) == QString::fromLocal8Bit("已分配科目班级")) {
        contextMenu->clear();
        contextMenu->addAction(cancelPromotionAction);
    } else if (item->parent() && item->parent()->text(0) == QString::fromLocal8Bit("未分配科目重点班级")) {
        contextMenu->clear();
        contextMenu->addAction(moveToAssignedClassAction);
    } else {
        contextMenu->clear();
        contextMenu->addMenu(assignMenu);
        contextMenu->addAction(promoteToKeyClassAction);
    }

    contextMenu->exec(treeWidget->viewport()->mapToGlobal(pos));
}

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值