背景:
近期要用Qt写一个 加载渲染激光点云的小工具软件, 对于这种软件来说,首先要有一个图层树来方便控制.因而做了一个小demo来记录一下;
效果展示:
要完成的功能: 增,删,改,查,收起,以及展开的操作效果.以及对自己定义
实现过程:
1.先定义自己的数据结构:
#include <iostream>
#include <list>
/// <summary>
/// Tree 基础图元
/// </summary>
class IXPloteTreeItem
{
public:
IXPloteTreeItem();
~IXPloteTreeItem();
static long XPloteNumsID;//全局的ID
void SetData(const std::string& name);
int GUID;
std::string gName{ "" };
std::shared_ptr<std::list<IXPloteTreeItem>> gChilds{ nullptr };
std::string toString() const;
};
2.定义TreeStandItem,来包裹自己的结构
XPloteStandardItem.h头文件.
#include <qstandarditemmodel.h>
#include "IXPloteTreeItem.h"
/// <summary>
/// 创建自己的QstandItem 子项目.
/// </summary>
class XPloteStandardItem :public QStandardItem
{
public:
using QStandardItem::QStandardItem;
XPloteStandardItem() :QStandardItem() {
InitData();
}
XPloteStandardItem(const QString& str) :QStandardItem(str)
{
InitData();
gTreeData->gName = str.toStdString().c_str();
}
~XPloteStandardItem();
std::shared_ptr<IXPloteTreeItem> gTreeData{ nullptr };
private:
void InitData();
};
XPloteStandardItem.cpp文件
#include "XPloteStandardItem.h"
#include <memory>
XPloteStandardItem::~XPloteStandardItem()
{
}
void XPloteStandardItem::InitData()
{
if(gTreeData==nullptr) gTreeData = std::make_shared<IXPloteTreeItem>();
this->setCheckable(true);
this->setCheckState(Qt::Checked);
this->setEditable(false);
}
3.UI上面我就不写了,随便拖一个TreeView控件上去,主要是下面的关键代码以及实现过程:
QtTree.h 类头文件
/// <summary>
/// 主窗体函数.
/// </summary>
class QtTree : public QMainWindow
{
Q_OBJECT
public:
QtTree(QWidget *parent = nullptr);
~QtTree();
void InitSource();
public slots:
void on_treeView_customContextMenuRequested(const QPoint &pos);
private:
Ui::QtTreeClass ui;
QMenu* menu;
QStandardItemModel* model;
QStandardItem* rootItem1;
QStandardItem* rootItem2;
};
QtTree.cpp实现部分:
#include "QtTree.h"
#include <QMessageBox>
QtTree::QtTree(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
InitSource();
}
QtTree::~QtTree()
{
}
void PrintLog(const std::string& str)
{
QMessageBox::information(nullptr,"",str.c_str());
}
void PrintLog(QString str)
{
QMessageBox::information(nullptr, "", str);
}
void QtTree::InitSource()
{
ui.treeView->setContextMenuPolicy(Qt::CustomContextMenu);
//添加右键菜单...
QString qss = "QMenu{color:#E8E8E8;background:#4D4D4D;margin:2px;}\
QMenu::item{padding:3px 20px 3px 20px;}\
QMenu::indicator{width:13px;height:13px;}\
QMenu::item:selected{color:#E8E8E8;border:0px solid #575757;background:#1E90FF;}\
QMenu::separator{height:1px;background:#757575;}"; //设置样式表
menu = new QMenu(ui.treeView);
menu->setStyleSheet(qss); //给菜单设置样式
QAction* a1 = new QAction(QStringLiteral("增加"));
menu->addAction(a1);
connect(a1, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
#if 0
auto parentIndex = curIndex.parent();
if (parentIndex.isValid())
{
auto parentItem = model->itemFromIndex(parentIndex);
if (parentItem != nullptr)
{
parentItem->appendRow(new QStandardItem(QStringLiteral("城市_%1").arg(model->rowCount())));
}
}
else
{
//model
model->appendRow(new QStandardItem(QStringLiteral("城市_%1").arg(model->rowCount())));
}
#else
auto curItem = model->itemFromIndex(curIndex);
if (curItem != nullptr)
{
auto item = new XPloteStandardItem(QStringLiteral("城市_%1").arg(model->rowCount()));
item->setCheckable(true);
item->setCheckState(Qt::Checked);
curItem->appendRow(item);
}
#endif // 0
}
});
QAction* a2 = new QAction(QStringLiteral("查看"));
menu->addAction(a2);
connect(a2, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
auto curItem = static_cast<XPloteStandardItem*>((model->itemFromIndex(curIndex)));
if (curItem != nullptr)
{
QString info;
info.append(curItem->text());//获取当前文本.
info.append(QStringLiteral("\r\n状态: %1\r\n").arg(curItem->checkState()));
info.append(QStringLiteral("自己的数据(IXPloteTreeItem): %1").arg(curItem->gTreeData->toString().c_str()));
PrintLog(info);
}
}
});
QAction* a5 = new QAction(QStringLiteral("展开"));
menu->addAction(a5);
connect(a5, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
ui.treeView->expand(curIndex);
}
});
QAction* a6 = new QAction(QStringLiteral("收起"));
menu->addAction(a6);
connect(a6, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
ui.treeView->collapse(curIndex);
}
});
QAction* a3 = new QAction(QStringLiteral("修改"));
menu->addAction(a3);
connect(a3, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
auto curItem = static_cast<XPloteStandardItem*>((model->itemFromIndex(curIndex)));
if (curItem != nullptr)
{
QString str = QStringLiteral("天朝");
curItem->gTreeData->gName = str.toStdString();
curItem->setText(str);
PrintLog(QStringLiteral("修改完成"));
}
}
});
QAction* a4 = new QAction(QStringLiteral("删除"));
menu->addAction(a4);
connect(a4, &QAction::triggered, this, [&]() {
QModelIndex curIndex = ui.treeView->currentIndex();
if (curIndex.isValid())
{
auto parentIndex = curIndex.parent();
if (parentIndex.isValid())
{
auto parentItem = model->itemFromIndex(parentIndex);
parentItem->removeRow(curIndex.row());
}
else
{
model->removeRow(curIndex.row());
}
}
});
//给菜单添加项
//添加一些Tree数据.
{
model = new QStandardItemModel(ui.treeView); //指定父对象,便于内存管理
model->setHorizontalHeaderLabels(QStringList() << QStringLiteral("名称") << QStringLiteral("说明"));
ui.treeView->setModel(model);//设置数据Model,关键.
rootItem1 = new XPloteStandardItem(QStringLiteral("北京"));
model->appendRow(rootItem1);
XPloteStandardItem* childItem1_1 = new XPloteStandardItem(QStringLiteral("故宫"));
rootItem1->appendRow(childItem1_1);
XPloteStandardItem* childItem1_2 = new XPloteStandardItem(QStringLiteral("子午庙"));
rootItem1->appendRow(childItem1_2);
rootItem1->appendRow(new XPloteStandardItem(QStringLiteral("日月潭")));
rootItem1->appendRow(new XPloteStandardItem(QStringLiteral("中南海")));
rootItem2 = new XPloteStandardItem(QStringLiteral("湖北"));
model->appendRow(rootItem2);
//model->setItem(model->indexFromItem(rootItem2).row(), 1, new XPloteStandardItem(QStringLiteral("黄鹤楼")));//设置第二列内容.
XPloteStandardItem* childItem2_1 = new XPloteStandardItem(QStringLiteral("武汉"));
rootItem2->appendRow(childItem2_1);
//rootItem2->setChild(childItem2_1->index().row(), 1, new XPloteStandardItem(QStringLiteral("步行街")));//在第二列添加数据.
XPloteStandardItem* childItem2_2 = new XPloteStandardItem(QStringLiteral("园博园")); //注意这里->rootItem2,通过父类设置其所在的行.
rootItem2->appendRow(childItem2_2);
//rootItem2->setChild(childItem2_2->index().row(),1,new XPloteStandardItem(QStringLiteral("揽月楼")));
rootItem2->appendRow(new XPloteStandardItem(QStringLiteral("白云寺")));
}
}
/// <summary>
/// 右键菜单...
/// </summary>
/// <param name="pos"></param>
void QtTree::on_treeView_customContextMenuRequested(const QPoint& pos)
{
if (ui.treeView->hasFocus()) {
menu->exec(this->mapToGlobal(pos));
}
}
完整的资源下载链接:
或者关注公众号 我有一座编码屋 回复 QtTree 即可获取链接