BayTemplate/source/document.cpp

505 lines
15 KiB
C++
Raw Normal View History

#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_filename = targetFile;
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;
}
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);
// 样式属性
QJsonObject penObj;
penObj.insert("color", item->penColor().name());
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);
// 尺寸
obj.insert("width", item->width());
obj.insert("height", item->height());
// 类型特定属性
obj.insert("properties", serializeItemProperties(item));
// 如果是组,递归序列化子项
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
if (group) {
QJsonArray childrenArray;
QList<QGraphicsItem*> children = group->childItems();
for (QGraphicsItem* child : children) {
// QGraphicsItem 没有 QObject 接口,所以不能用 qobject_cast
// 使用 static_cast 并检查 nullptr
GraphicsBaseItem* childBase = static_cast<GraphicsBaseItem*>(child);
if (childBase) {
childrenArray.append(serializeItem(childBase));
}
}
obj.insert("children", childrenArray);
}
return obj;
}
void Document::deserializeItem(const QJsonObject& obj, QGraphicsScene* scene, QGraphicsItem* parent)
{
QString typeStr = obj["type"].toString();
GraphicsBaseItem* item = nullptr;
// 根据类型创建图元
if (typeStr == "GraphicsRectItem") {
item = deserializeRectItem(obj);
} else if (typeStr == "GraphicsBusSectionItem") {
item = deserializeBusSectionItem(obj);
} else if (typeStr == "GraphicsItemGroup") {
GraphicsItemGroup* group = deserializeItemGroup(obj, scene, parent);
// 对于组,不设置单个图元的属性,只递归处理子项
QJsonArray childrenArray = obj["children"].toArray();
for (auto childIt : childrenArray) {
deserializeItem(childIt.toObject(), scene, group);
}
return;
}
// GraphicsPolygonItem 暂时跳过,因为没有 Q_OBJECT 宏
if (!item) {
return;
}
// 设置基本属性
QJsonObject posObj = obj["pos"].toObject();
item->setPos(posObj["x"].toDouble(), posObj["y"].toDouble());
item->setRotation(obj["rotation"].toDouble());
// Qt 的 setScale() 只接受单个参数,使用 QTransform 来设置非均匀缩放
QJsonObject scaleObj = obj["scale"].toObject();
double scaleX = scaleObj["x"].toDouble();
double scaleY = scaleObj["y"].toDouble();
QTransform transform;
transform.scale(scaleX, scaleY);
item->setTransform(transform);
QJsonObject penObj = obj["pen"].toObject();
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();
QBrush brush;
brush.setColor(QColor(brushObj["color"].toString()));
item->setBrush(brush);
item->setWidth(obj["width"].toDouble());
item->setHeight(obj["height"].toDouble());
// 添加到场景或父项
if (parent) {
// 添加到组中
GraphicsItemGroup* group = static_cast<GraphicsItemGroup*>(parent);
if (group) {
group->addItems(QList<QGraphicsItem*>() << item);
}
} else {
scene->addItem(item);
}
// 递归处理子项
QJsonArray childrenArray = obj["children"].toArray();
for (auto childIt : childrenArray) {
deserializeItem(childIt.toObject(), scene, item);
}
}
// =================================================================
// 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)
QRect rect(-width/2, -height/2, width, height);
return new GraphicsRectItem(rect, false);
}
GraphicsBaseItem* Document::deserializeBusSectionItem(const QJsonObject& obj)
{
// 总线段重建
double width = obj["width"].toDouble();
double height = obj["height"].toDouble();
// GraphicsBusSectionItem 构造函数rect, parent
QRect rect(0, 0, width, height);
return new GraphicsBusSectionItem(rect);
}
GraphicsItemGroup* Document::deserializeItemGroup(const QJsonObject& obj,
QGraphicsScene* scene,
QGraphicsItem* parent)
{
GraphicsItemGroup* group = new GraphicsItemGroup(parent);
group->setWidth(obj["width"].toDouble());
group->setHeight(obj["height"].toDouble());
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);
}