BayTemplate/source/document.cpp

634 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "document.h"
#include "designerScene.h"
#include "graphicsItem/graphicsBaseItem.h"
#include "graphicsItem/graphicsItemGroup.h"
#include "graphicsItem/graphicsRectItem.h"
#include "graphicsItem/graphicsBusSectionItem.h"
class GraphicsPolygonItem; // 前向声明,暂不使用多边形
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>
#include <QDataStream>
#include <QSet>
#include <QJsonArray>
#include <QJsonDocument>
#include <QFile>
#include <QDataStream>
// 文档格式版本号
static const QString DOCUMENT_VERSION = "1.0";
Document::Document(QObject *parent)
: QObject(parent)
, m_filename()
, m_modified(false)
, m_version(DOCUMENT_VERSION)
, m_created(QDateTime::currentDateTime())
, m_modifiedTime(QDateTime::currentDateTime())
, m_lastSavedTime()
, m_pScene(nullptr)
{
}
Document::~Document()
{
// Document 不拥有 scene 的所有权,不需要删除
m_pScene = nullptr;
}
// =================================================================
// 场景关联
// =================================================================
void Document::setScene(DesignerScene* scene)
{
m_pScene = scene;
// 如果关联了新的 scene连接 selectionChanged 信号来追踪修改
if (m_pScene) {
connect(m_pScene, &QGraphicsScene::selectionChanged,
this, [this]() {
// 选择改变本身不代表文档修改,但可以作为用户活动的信号
// 实际的修改由具体的操作(添加、删除、移动等)触发
});
}
}
DesignerScene* Document::scene() const
{
return m_pScene;
}
// =================================================================
// 文件操作
// =================================================================
bool Document::saveToFile(const QString& filename)
{
QString targetFile = filename.isEmpty() ? m_filename : filename;
if (targetFile.isEmpty()) {
// 没有目标文件名,无法保存
emit saveStatusChanged(false, tr("未指定文件路径,请使用另存为功能"));
return false;
}
bool success = saveInternal(targetFile);
if (success) {
m_lastSavedTime = QDateTime::currentDateTime();
m_modified = false;
// 如果传入的文件名与当前文件名不同,更新 m_filename
if (!filename.isEmpty() && filename != m_filename) {
m_filename = filename;
emit filenameChanged(m_filename);
}
emit modifiedChanged(false);
emit saveStatusChanged(true, tr("保存成功"));
} else {
emit saveStatusChanged(false, tr("保存失败"));
}
return success;
}
bool Document::saveAsToFile(const QString& filename)
{
if (filename.isEmpty()) {
emit saveStatusChanged(false, tr("未指定文件路径"));
return false;
}
bool success = saveInternal(filename);
if (success) {
QString oldFilename = m_filename;
m_filename = filename;
m_lastSavedTime = QDateTime::currentDateTime();
m_modified = false;
// 另存为总是会更新文件名
emit filenameChanged(m_filename);
emit modifiedChanged(false);
emit saveStatusChanged(true, tr("另存为成功"));
} else {
emit saveStatusChanged(false, tr("另存为失败"));
}
return success;
}
bool Document::loadFromFile(const QString& filename)
{
bool success = loadInternal(filename);
if (success) {
m_filename = filename;
m_modified = false;
m_lastSavedTime = QDateTime::currentDateTime();
emit filenameChanged(m_filename);
emit modifiedChanged(false);
emit saveStatusChanged(true, tr("加载成功"));
} else {
emit saveStatusChanged(false, tr("加载失败"));
}
return success;
}
// =================================================================
// 序列化核心实现
// =================================================================
QByteArray Document::serialize() const
{
if (!m_pScene) {
return QByteArray();
}
QJsonObject root;
root.insert("version", m_version);
root.insert("created", m_created.toString(Qt::ISODate));
root.insert("modified", m_modifiedTime.toString(Qt::ISODate));
root.insert("filename", m_filename);
// 序列化元数据
if (!m_metaData.isEmpty()) {
QJsonObject metaObj;
for (auto it = m_metaData.begin(); it != m_metaData.end(); ++it) {
// QVariant 需要转换为 QJsonValue
metaObj.insert(it.key(), toJsonValue(it.value()));
}
root.insert("metaData", metaObj);
}
// 序列化图元
QJsonArray itemsArray;
QList<QGraphicsItem*> items = m_pScene->items();
for (QGraphicsItem* item : items) {
// 跳过 handle 等辅助元素
// QGraphicsItem 不是 QObject所以不能用 qobject_cast
// GraphicsBaseItem 继承 QObject 和 AbstractShape (QGraphicsItem),可以用 qgraphicsitem_cast
GraphicsBaseItem* baseItem = qgraphicsitem_cast<GraphicsBaseItem*>(item);
if (!baseItem) {
continue;
}
// 双重检查:通过 isValidItem() 确保不是辅助元素(如 ItemControlHandle
// ItemControlHandle 是 QGraphicsRectItem 子类,不是 GraphicsBaseItem 子类
// 即使 qgraphicsitem_cast 错误返回非 nullisValidItem() 也能过滤掉m_type 为 T_undefined
if (!baseItem->isValidItem()) {
continue;
}
QJsonObject itemObj = serializeItem(baseItem);
itemsArray.append(itemObj);
}
root.insert("items", itemsArray);
QJsonDocument doc(root);
return doc.toJson(QJsonDocument::Indented);
}
bool Document::deserialize(const QByteArray& data)
{
if (data.isEmpty() || !m_pScene) {
return false;
}
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
return false;
}
QJsonObject root = doc.object();
// 解析版本信息
QString version = root["version"].toString();
if (version.isEmpty()) {
return false;
}
// 解析时间信息
if (root.contains("created")) {
m_created = QDateTime::fromString(root["created"].toString(), Qt::ISODate);
}
if (root.contains("modified")) {
m_modifiedTime = QDateTime::fromString(root["modified"].toString(), Qt::ISODate);
}
// 解析元数据
if (root.contains("metaData")) {
QJsonObject metaObj = root["metaData"].toObject();
for (auto it = metaObj.begin(); it != metaObj.end(); ++it) {
// QJsonValue 需要转换为 QVariant
m_metaData.insert(it.key(), fromJsonValue(it.value()));
}
}
// 清空当前场景
if (m_pScene) {
m_pScene->clear();
}
// 反序列化图元
QJsonArray itemsArray = root["items"].toArray();
for (auto itemIt : itemsArray) {
QJsonObject itemObj = itemIt.toObject();
deserializeItem(itemObj, m_pScene, nullptr);
}
return true;
}
// =================================================================
// 内部实现
// =================================================================
bool Document::saveInternal(const QString& filename)
{
QByteArray data = serialize();
if (data.isEmpty()) {
return false;
}
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
file.write(data);
file.close();
return true;
}
bool Document::loadInternal(const QString& filename)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
return false;
}
QByteArray data = file.readAll();
file.close();
return deserialize(data);
}
// =================================================================
// 图元序列化/反序列化
// =================================================================
QJsonObject Document::serializeItem(GraphicsBaseItem* item) const
{
QJsonObject obj;
// 基本属性
obj.insert("type", getTypeString(item));
obj.insert("id", QString::number(reinterpret_cast<quintptr>(item)));
// 位置和变换
QPointF pos = item->pos();
QJsonObject posObj;
posObj.insert("x", pos.x());
posObj.insert("y", pos.y());
obj.insert("pos", posObj);
obj.insert("rotation", item->rotation());
QJsonObject scaleObj;
// Qt 的 scale() 返回 qreal不是 QPointF
scaleObj.insert("x", item->scale());
scaleObj.insert("y", item->scale());
obj.insert("scale", scaleObj);
// 样式属性只用于非组项GraphicsItemGroup 没有 pen/brush
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
if (!group) {
// 普通图元才有 pen/brush
QJsonObject penObj;
QColor penColor = item->penColor();
if (penColor.isValid()) {
penObj.insert("color", penColor.name());
} else {
penObj.insert("color", QString());
}
penObj.insert("width", item->pen().widthF());
penObj.insert("style", static_cast<int>(item->pen().style()));
obj.insert("pen", penObj);
QJsonObject brushObj;
brushObj.insert("color", item->brushColor().name());
obj.insert("brush", brushObj);
} else {
// 组没有 pen/brush插入空对象
obj.insert("pen", QJsonObject());
obj.insert("brush", QJsonObject());
}
// 尺寸
obj.insert("width", item->width());
obj.insert("height", item->height());
// 类型特定属性
obj.insert("properties", serializeItemProperties(item));
// 如果是组,递归序列化子项
if (group) {
QJsonArray childrenArray;
QList<QGraphicsItem*> children = group->childItems();
for (QGraphicsItem* child : children) {
// 使用 qgraphicsitem_cast 安全转换,只序列化 GraphicsBaseItem 子类的项
GraphicsBaseItem* childBase = qgraphicsitem_cast<GraphicsBaseItem*>(child);
if (childBase) {
childrenArray.append(serializeItem(childBase));
}
// 跳过 ItemControlHandle 等非 GraphicsBaseItem 的子项
}
obj.insert("children", childrenArray);
}
return obj;
}
void Document::deserializeItem(const QJsonObject& obj, QGraphicsScene* scene, QGraphicsItem* parent)
{
QString typeStr = obj["type"].toString();
GraphicsBaseItem* item = nullptr;
GraphicsItemGroup* group = nullptr;
// 根据类型创建图元(属性在工厂函数中已设置)
if (typeStr == "GraphicsRectItem") {
item = deserializeRectItem(obj);
} else if (typeStr == "GraphicsBusSectionItem") {
item = deserializeBusSectionItem(obj);
} else if (typeStr == "GraphicsItemGroup") {
group = deserializeItemGroup(obj, scene, parent);
// 先添加到 scene 或父组,再递归处理子项
if (parent) {
GraphicsItemGroup* parentGroup = qgraphicsitem_cast<GraphicsItemGroup*>(parent);
if (parentGroup) {
parentGroup->addToGroup(group);
}
} else {
scene->addItem(group);
}
// 递归处理子项(此时 group 已在 scene 中)
QJsonArray childrenArray = obj["children"].toArray();
for (auto childIt : childrenArray) {
deserializeItem(childIt.toObject(), scene, group);
}
// 添加完子项后,更新 group 的 coordinate会自动计算 boundingRect
group->updateCoordinate();
// 更新 handle 位置
group->updateHandles();
return;
}
// GraphicsPolygonItem 暂时跳过
if (!item) {
return;
}
// 添加到场景或父项
if (parent) {
GraphicsItemGroup* parentGroup = qgraphicsitem_cast<GraphicsItemGroup*>(parent);
if (parentGroup) {
parentGroup->addToGroup(item);
}
} else {
scene->addItem(item);
}
// 普通图元没有子项
// 添加后更新 handle 位置(如果有)
item->setHandleVisible(false); // 默认不显示 handle
}
// =================================================================
// QVariant <-> QJsonValue 转换辅助函数
// =================================================================
QJsonValue Document::toJsonValue(const QVariant& value) const
{
if (value.typeId() == qMetaTypeId<bool>()) {
return value.toBool();
} else if (value.typeId() == qMetaTypeId<int>()) {
return value.toInt();
} else if (value.typeId() == qMetaTypeId<double>()) {
return value.toDouble();
} else if (value.typeId() == QMetaType::Type::QString) {
return value.toString();
} else {
return value.toString();
}
}
QVariant Document::fromJsonValue(const QJsonValue& value) const
{
if (value.isDouble()) {
return value.toDouble();
} else if (value.isBool()) {
return value.toBool();
} else if (value.isArray()) {
QList<QVariant> list;
for (const QJsonValue& v : value.toArray()) {
list.append(fromJsonValue(v));
}
return list;
} else if (value.isObject()) {
QMap<QString, QVariant> map;
for (auto it = value.toObject().begin(); it != value.toObject().end(); ++it) {
map.insert(it.key(), fromJsonValue(it.value()));
}
return map;
} else {
return value.toString();
}
}
// =================================================================
// 辅助函数
// =================================================================
QString Document::getTypeString(GraphicsBaseItem* item) const
{
// 使用动态类型信息获取类型字符串qobject_cast 要求类型有 Q_OBJECT 宏)
// GraphicsRectItem, GraphicsBusSectionItem, GraphicsItemGroup 都有 Q_OBJECT 宏
if (qobject_cast<GraphicsRectItem*>(item)) {
return "GraphicsRectItem";
} else if (qobject_cast<GraphicsBusSectionItem*>(item)) {
return "GraphicsBusSectionItem";
} else if (qobject_cast<GraphicsItemGroup*>(item)) {
return "GraphicsItemGroup";
}
// GraphicsPolygonItem 没有 Q_OBJECT 宏,暂时不支持序列化
return "GraphicsBaseItem";
}
QJsonObject Document::serializeItemProperties(GraphicsBaseItem* item) const
{
QJsonObject props;
// 类型特定的属性序列化
GraphicsRectItem* rectItem = qobject_cast<GraphicsRectItem*>(item);
if (rectItem) {
// 这里可以添加圆角矩形等特殊属性
// props.insert("isRound", rectItem->m_bIsRound);
}
// 多边形等特殊属性可以在这里扩展
return props;
}
GraphicsBaseItem* Document::deserializeRectItem(const QJsonObject& obj)
{
// 根据保存的属性重建矩形
double width = obj["width"].toDouble();
double height = obj["height"].toDouble();
// 图元的原点在中心,因此 rect 的左上角为 (-width/2, -height/2)
// 在构造函数中已经设置了 boundingRect所以不需要再调用 setWidth/setHeight
QRect rect(-width/2, -height/2, width, height);
GraphicsRectItem* item = new GraphicsRectItem(rect, false);
// 从 JSON 恢复属性
QJsonObject posObj = obj["pos"].toObject();
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
item->setRotation(obj["rotation"].toDouble());
// 使用 setScale 而不是 setTransform避免与 item 的 transform 冲突)
QJsonObject scaleObj = obj["scale"].toObject();
double scaleX = scaleObj["x"].toDouble();
double scaleY = scaleObj["y"].toDouble();
if (scaleX != 1.0 || scaleY != 1.0) {
item->setScale(qMax(scaleX, scaleY)); // 使用统一缩放
}
QJsonObject penObj = obj["pen"].toObject();
if (penObj.isEmpty()) {
// 如果 pen 对象为空(来自组的序列化),使用默认值
QPen pen(Qt::black);
pen.setWidthF(1.0);
pen.setStyle(Qt::SolidLine);
item->setPen(pen);
} else {
QPen pen;
pen.setColor(QColor(penObj["color"].toString()));
pen.setWidthF(penObj["width"].toDouble());
pen.setStyle(static_cast<Qt::PenStyle>(penObj["style"].toInt()));
item->setPen(pen);
}
QJsonObject brushObj = obj["brush"].toObject();
if (brushObj.isEmpty()) {
item->setBrush(QBrush(Qt::NoBrush));
} else {
QBrush brush;
brush.setColor(QColor(brushObj["color"].toString()));
item->setBrush(brush);
}
return item;
}
GraphicsBaseItem* Document::deserializeBusSectionItem(const QJsonObject& obj)
{
// 总线段重建
double width = obj["width"].toDouble();
double height = obj["height"].toDouble();
QRect rect(-width/2, -height/2, width, height);
GraphicsBusSectionItem* item = new GraphicsBusSectionItem(rect);
// 从 JSON 恢复属性
QJsonObject posObj = obj["pos"].toObject();
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
item->setRotation(obj["rotation"].toDouble());
QJsonObject scaleObj = obj["scale"].toObject();
double scaleX = scaleObj["x"].toDouble();
double scaleY = scaleObj["y"].toDouble();
if (scaleX != 1.0 || scaleY != 1.0) {
item->setScale(qMax(scaleX, scaleY));
}
QJsonObject penObj = obj["pen"].toObject();
if (penObj.isEmpty()) {
QPen pen(Qt::black);
pen.setWidthF(1.0);
pen.setStyle(Qt::SolidLine);
item->setPen(pen);
} else {
QPen pen;
pen.setColor(QColor(penObj["color"].toString()));
pen.setWidthF(penObj["width"].toDouble());
pen.setStyle(static_cast<Qt::PenStyle>(penObj["style"].toInt()));
item->setPen(pen);
}
QJsonObject brushObj = obj["brush"].toObject();
if (brushObj.isEmpty()) {
item->setBrush(QBrush(Qt::NoBrush));
} else {
QBrush brush;
brush.setColor(QColor(brushObj["color"].toString()));
item->setBrush(brush);
}
return item;
}
GraphicsItemGroup* Document::deserializeItemGroup(const QJsonObject& obj,
QGraphicsScene* scene,
QGraphicsItem* parent)
{
GraphicsItemGroup* group = new GraphicsItemGroup(parent);
// 先设置位置、旋转等变换属性
QJsonObject posObj = obj["pos"].toObject();
group->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
group->setRotation(obj["rotation"].toDouble());
QJsonObject scaleObj = obj["scale"].toObject();
double scaleX = scaleObj["x"].toDouble();
double scaleY = scaleObj["y"].toDouble();
if (scaleX != 1.0 || scaleY != 1.0) {
group->setScale(qMax(scaleX, scaleY));
}
// 获取宽高,但不立即调用 setWidth/setHeight会触发 updateCoordinate
double width = obj["width"].toDouble();
double height = obj["height"].toDouble();
// 注意:对于 GraphicsItemGroup其 boundingRect 是由子项自动计算的
// 我们创建一个临时的空矩形,待子项添加后会自动更新
// 暂时不设置宽高,因为 updateCoordinate 需要 parentItem 为空才能正常工作
// 子项添加后group 的 boundingRect 会自动重新计算
return group;
}
void Document::setMetaData(const QString& key, const QVariant& value)
{
m_metaData.insert(key, value);
m_modifiedTime = QDateTime::currentDateTime();
if (m_pScene) {
setModified(true);
}
}
QVariant Document::metaData(const QString& key) const
{
return m_metaData.value(key);
}