利用Qt的QGraphicsView框架实现思维导图功能
百度网盘体验地址:
链接:https://pan.baidu.com/s/1VyUupBIraqYbnWCzepcJww
提取码:skgk
效果图
1、动态演示效果:
2、静态展示图片:
节点操作
功能 | 描述 |
新建思维导图 | 将在场景中心创建根节点,默认名称:中心主题 |
添加节点 | 若父节点已有孩子节点时,需要调整节点位置。默认名称:新建节点 |
删除孩子节点 | 点击节点下方“-”号按钮,删除该节点的所有孩子节点,折叠/展开节点会被隐藏 |
删除自身及孩子节点 | 点击节点右上方“x”号按钮,删除该节点及其所有孩子节点 |
修改节点文本 | 双击文本节点,修改文本内容 |
折叠/展开节点 | 折叠或展开孩子节点 |
文件操作
功能 | 描述 |
加载文件 | 可以加载指定格式的xml文件,转换为思维导图显示 |
保存文件 | 将思维导图转换为指定格式的xml文件 |
模式与主题
功能 | 描述 |
线型选择 | 提供两种线型:圆角折线、圆滑曲线(三次贝塞尔曲线) |
模式选择 | 提供两种模式:动态编辑、静态展示(可任意拖动节点) |
主题选择 | 提供两种主题:渴望天空的蓝、期待黎明的黑 |
人性化功能
功能 | 描述 |
思维导图居中 | 初始化加载时,根节点位置根据导图位置设计原则进行居中显示 |
复位 | 恢复思维导图居中显示 |
新增节点位置的可视化设计 | 调整视图位置,保证新增节点的人性化可视效果 |
文本节点宽度自适应 | 修改文本内容时,会实时计算节点宽度是否需要改变 |
鼠标滚轮移动视图 | 鼠标滚轮控制视图上下移动,Alt+鼠标滚轮控制视图左右移动 |
鼠标拖拽视图 | 鼠标在空白处按下,变成小手状可以拖拽视图移动 |
鼠标缩放视图 | Ctrl+鼠标滚轮,控制视图缩放 |
方向键控制视图移动 | 上下左右键控制视图移动 |
Ctrl+0 | 思维导图场景显示:显示整个思维导图区域,且居中显示 |
Ctrl+1 | 全场景显示:显示整个场景大小 |
Ctrl+2 | 原画比例显示,即1:1显示 |
Ctrl+E | 复位功能快捷键 |
思维导图初始位置设计的原则
序号 | 描述 |
1 | 若导图整体宽度小于视图宽度的一半,则根节点居中显示 |
2 | 若导图整体宽度大于视图宽度的一半,且小于视图宽度,根节点适度左移,保证导图能够全部显示 |
3 | 若导图整体宽度大于视图宽度,则根节点居左显示,其余节点依次排开,超过视图部分不可见 |
新增节点位置的可视化设计原则
序号 | 描述 |
1 | 若父节点无孩子节点,则在其右侧新增节点。当新增节点超过视图区域时,移动视图,将新增节点位置移动到之前父节点位置,此操作没有闪烁现象,不需要用户寻找新节点位置 |
2 | 若父节点有孩子节点,则在其下侧新增节点,此时会自动调整思维导图的布局。当新增节点超过视图区域时,移动视图,将新增节点位置移动到之前父节点位置,此操作没有闪烁现象,不需要用户寻找新节点位置 |
附属功能节点代码
1、基类
#pragma once
/*
* 思维导图-功能节点基类
*/
#include <QGraphicsItem>
#include "DataStruct/ItemColor.h"
class MindFuncItemBase : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
MindFuncItemBase(QGraphicsItem *parent = nullptr);
virtual ~MindFuncItemBase();
void setItemColor(const ItemColor &color);
public:
virtual QRectF boundingRect() const override;
protected:
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual void initItemColor();
signals:
void clicked();
protected:
int m_width; // 宽度
int m_height; // 高度
int m_borderRadius; // 圆角半径
int m_borderWidth; // 边框宽度
ItemColor m_itemColor; // 颜色
bool m_hover; // 悬浮
bool m_selected; // 选中
};
#include "MindFuncItemBase.h"
#include <QGraphicsSceneMouseEvent>
MindFuncItemBase::MindFuncItemBase(QGraphicsItem *parent /*= nullptr*/)
: QGraphicsItem(parent)
{
m_width = 18; // 宽度
m_height = 18; // 高度
m_borderRadius = 3; // 圆角半径
m_borderWidth = 2; // 边框宽度
m_hover = false; // 悬浮
m_selected = false; // 选中
initItemColor();
setAcceptHoverEvents(true);
setAcceptedMouseButtons(Qt::LeftButton);
setFlags(QGraphicsItem::ItemIsSelectable);
}
MindFuncItemBase::~MindFuncItemBase()
{
}
void MindFuncItemBase::setItemColor(const ItemColor &color)
{
m_itemColor = color;
update();
}
QRectF MindFuncItemBase::boundingRect() const
{
qreal penWidth = m_borderWidth;
QRectF rect = QRectF(-m_width / 2 - penWidth / 2, -m_height / 2 - penWidth / 2, m_width + penWidth, m_height + penWidth);
return rect;
}
void MindFuncItemBase::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
m_hover = true;
update();
}
void MindFuncItemBase::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
m_hover = false;
update();
}
void MindFuncItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
m_selected = true;
update();
}
void MindFuncItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
m_selected = false;
m_hover = false;
if (boundingRect().contains(event->pos()))
emit clicked();
update();
}
void MindFuncItemBase::initItemColor()
{
m_itemColor.m_color = Qt::white;
m_itemColor.m_hoverColor = Qt::white;
m_itemColor.m_selectedColor = Qt::white;
m_itemColor.m_bgColor = QColor(118, 118, 118);
m_itemColor.m_bgHoverColor = QColor(168, 168, 168);
m_itemColor.m_bgSelectedColor = QColor(68, 68, 68);
m_itemColor.m_borderColor = Qt::transparent;
m_itemColor.m_borderHoverColor = Qt::transparent;
m_itemColor.m_borderSelectedColor = Qt::transparent;
}
派生类举例:折叠/展开节点
#pragma once
/*
* 思维导图-折叠/展开节点
*/
#include "MindFuncItemBase.h"
class MindCollapseItem : public MindFuncItemBase
{
Q_OBJECT
public:
enum State { COLLAPSE, EXPAND };
MindCollapseItem(QGraphicsItem *parent = nullptr);
~MindCollapseItem();
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
QRectF boundingRect() const override;
public:
State state();
void setState(State state);
protected:
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
private:
void initItemColor();
signals:
void collpased();
void expanded();
private:
State m_state; // 折叠状态
int m_radius; // 半径
};
#include "MindCollapseItem.h"
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
MindCollapseItem::MindCollapseItem(QGraphicsItem *parent /*= nullptr*/)
: MindFuncItemBase(parent)
{
m_state = EXPAND;
m_radius = 12; // 半径
initItemColor();
}
MindCollapseItem::~MindCollapseItem()
{
}
void MindCollapseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
// 绘制背景
QRectF rect = QRectF(-m_radius, -m_radius, m_radius * 2, m_radius * 2);
QFont font = painter->font();
font.setBold(true);
font.setPixelSize(20);
painter->setFont(font);
painter->setPen(QPen(m_selected ? m_itemColor.m_borderSelectedColor : (m_hover ? m_itemColor.m_borderHoverColor : m_itemColor.m_borderColor) , m_borderWidth));
painter->setBrush(m_selected ? m_itemColor.m_bgSelectedColor : (m_hover ? m_itemColor.m_bgHoverColor : m_itemColor.m_bgColor));
painter->drawEllipse(rect);
// 绘制文本
QString text = (m_state == EXPAND) ? "-" : "+";
painter->setPen(QPen(m_selected ? m_itemColor.m_selectedColor : (m_hover ? m_itemColor.m_hoverColor : m_itemColor.m_color), 4));
painter->drawText(rect, Qt::AlignCenter, text);
}
QRectF MindCollapseItem::boundingRect() const
{
qreal penWidth = m_borderWidth;
QRectF rect = QRectF(-m_radius - penWidth / 2, -m_radius - penWidth / 2, m_radius * 2 + penWidth, m_radius * 2 + penWidth);
return rect;
}
MindCollapseItem::State MindCollapseItem::state()
{
return m_state;
}
void MindCollapseItem::setState(State state)
{
m_state = state;
update();
}
void MindCollapseItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
m_selected = false;
if (boundingRect().contains(event->pos()))
{
if (m_state == EXPAND)
{
m_state = COLLAPSE;
emit collpased();
}
else// m_state == COLLAPSE
{
m_state = EXPAND;
emit expanded();
}
}
update();
}
void MindCollapseItem::initItemColor()
{
m_itemColor.m_color = Qt::black;
m_itemColor.m_hoverColor = Qt::black;
m_itemColor.m_selectedColor = Qt::white;
m_itemColor.m_bgColor = Qt::white;
m_itemColor.m_bgHoverColor = QColor(128, 128, 128);
m_itemColor.m_bgSelectedColor = Qt::black;
m_itemColor.m_borderColor = Qt::black;
m_itemColor.m_borderHoverColor = Qt::white;
m_itemColor.m_borderSelectedColor = Qt::white;
}