646 lines
29 KiB
Markdown
646 lines
29 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## Project Overview
|
||
|
||
BayTemplate is a **Bay Template Designer of Grid Framework DesignTime** tool - a Qt-based application for designing electrical power grid structures. It features a dockable UI with a graphics canvas for placing and manipulating grid elements.
|
||
|
||
## Build Commands
|
||
|
||
### Build the Project
|
||
|
||
使用 CMake presets 进行配置和构建:
|
||
|
||
```bash
|
||
# Configure using preset
|
||
cmake --preset default
|
||
|
||
# Build with Ninja
|
||
ninja -C build/Debug
|
||
```
|
||
|
||
### Output Location
|
||
|
||
- Executable: `build/Debug/BayTemplate.app` (macOS)
|
||
|
||
## Architecture
|
||
|
||
### Core Components
|
||
|
||
#### 1. Docking System (Qt Advanced Docking System 4.3.1)
|
||
- **CDockManager** manages dockable panels
|
||
- **Left Dock**: 图元面板 (GraphicElementsPanel) - library of draggable grid elements
|
||
- **Center**: DrawingPanel - main canvas area with DesignerScene
|
||
- **Right Dock**: 属性编辑器 (PropertyEditor) - QDetailsView for editing selected item properties
|
||
|
||
#### 2. Graphics View Framework
|
||
- **DesignerView** (`QGraphicsView` subclass): Custom view with zoom (0.02-50x), pan (middle-button drag), checkerboard background
|
||
- **DesignerScene** (`QGraphicsScene` subclass): Custom scene with grid overlay (20px spacing), routes mouse events through SelectorManager
|
||
|
||
#### 3. Selector System
|
||
Selector hierarchy in `include/util/`:
|
||
- **BaseSelector**: Abstract base for all selectors
|
||
- **CreatingSelector**: Mode for creating new items from template
|
||
- **MovingSelector**: Mode for moving items
|
||
- **RotationSelector**: Mode for rotating items around origin
|
||
- **ScalingSelector**: Mode for scaling items
|
||
- **EditingSelector**: Mode for editing polygon vertex positions
|
||
- **SelectorManager**: Singleton that manages the active working selector
|
||
|
||
Mouse events flow: `DesignerScene` → `SelectorManager::getWorkingSelector()` → specific selector implementation
|
||
|
||
#### 4. Graphics Items (`include/graphicsItem/`)
|
||
- **GraphicsBaseItem**: Abstract base class for all grid elements
|
||
- **GraphicsRectItem**: Rectangle element
|
||
- **GraphicsPolygonItem**: Polygon element (editable vertices)
|
||
- **GraphicsItemGroup**: Container for grouped items (supports flattening to prevent nesting)
|
||
- **GraphicsBusSectionItem**: Bus section element
|
||
- **ItemControlHandle**: Visual handles for manipulation (rotation, scaling, editing)
|
||
|
||
#### 5. Property Editor (Qt PropertyEditor)
|
||
- **QDetailsView**: Qt Quick-based property editor in right dock
|
||
- **QCustomType**: Custom property type for graphics item properties
|
||
- **PropertyTypeCustomization_CustomType**: Custom property editor integration
|
||
- Property editor observes `QGraphicsScene` and displays properties of selected items
|
||
|
||
#### 6. Command Pattern (Undo/Redo)
|
||
- **QUndoStack** (`m_pUndoStack` in CMainWindow) manages undo/redo
|
||
- **OperationCommand**: Base command class
|
||
- **AddItemCommand**: Add item to scene
|
||
- **DeleteItemCommand**: Remove item from scene
|
||
- **CreateItemGoupCommand**: Group selected items (with flattening)
|
||
- **DestroyItemGoupCommand**: Ungroup items
|
||
|
||
### Signal Flow
|
||
|
||
1. **User drags from 图元面板** → `GraphicElementsPanel` emits signal → `DesignerScene::signalAddItem()` → creates item with `AddItemCommand`
|
||
|
||
2. **User clicks on canvas** → `DesignerView` → `DesignerScene` → `SelectorManager::getWorkingSelector()` → selector processes event
|
||
|
||
3. **User selects items** → `DesignerScene::selectionChanged()` → `CMainWindow::onSignal_selectionChanged()` → updates property editor
|
||
|
||
4. **User modifies property in 属性编辑器** → `QDetailsView` updates item property → scene updates
|
||
|
||
### Key Files and Relationships
|
||
|
||
```
|
||
source/main.cpp # Entry point - creates CMainWindow
|
||
└── source/mainwindow.h/.cpp # Main window with CDockManager, initializes all components
|
||
├── initializeDockUi() # Sets up docks (left, center, right)
|
||
├── initializeAction() # Sets up QUndoStack and menu actions
|
||
├── initializeDocument() # Creates Document, associates with DesignerScene
|
||
├── Event handlers # onSignal_addItem, onSignal_selectionChanged, etc.
|
||
└── File operations # onAction_new(), onAction_open(), onAction_save()
|
||
|
||
source/document.h/.cpp # Document class - serialization and state management
|
||
├── serialize()/deserialize() # Core JSON serialization
|
||
├── saveToFile()/loadFromFile() # File I/O operations
|
||
└── State management # filename, modified, timestamps, meta data
|
||
|
||
source/drawingPanel.h/.cpp # Central dock widget containing DesignerView
|
||
└── DesignerView + DesignerScene
|
||
|
||
source/designerView.h/.cpp # QGraphicsView subclass - zoom, pan, middle-button navigation
|
||
|
||
source/designerScene.h/.cpp # QGraphicsScene subclass - grid background, group operations
|
||
└── Delegates to SelectorManager for mouse events
|
||
|
||
source/util/selectorManager.h/.cpp # Singleton managing active selector
|
||
└── Selector::mousePressEvent/ReleaseEvent/MoveEvent()
|
||
|
||
source/graphicsItem/ # Graphics item implementations
|
||
├── GraphicsBaseItem # Base class with common functionality
|
||
├── GraphicsRectItem # Rectangle
|
||
├── GraphicsPolygonItem # Editable polygon
|
||
├── GraphicsItemGroup # Item grouping
|
||
└── GraphicsBusSectionItem
|
||
|
||
source/propertyType/ # Custom property editor integration
|
||
├── CustomType.h
|
||
├── CustomGadget.h
|
||
└── PropertyTypeCustomization_CustomType.cpp
|
||
```
|
||
|
||
### Third-Party Dependencies
|
||
|
||
1. **QtADS** (`QtADS/` subdirectory, v4.3.1): Advanced docking system - must be present as subdirectory
|
||
2. **PropertyEditor** (`PropertyEditor/` subdirectory): Qt Quick-based property editing system with QDetailsView
|
||
|
||
Both are added via `add_subdirectory()` in CMakeLists.txt and must exist in the repository root.
|
||
|
||
### Platform Support
|
||
|
||
- **Qt Version**: Qt5 or Qt6 (auto-detected, `find_package(QT NAMES Qt6 Qt5 ...)`)
|
||
- **Architectures**: x86, x64, arm64, aarch64 (auto-detected in CMakeLists.txt)
|
||
- **Platforms**: Windows, macOS, Linux, Android (Android uses shared library)
|
||
|
||
### Important Implementation Details
|
||
|
||
1. **CMAKE_AUTOUIC_SEARCH_PATHS**: Set to `"ui"` in CMakeLists.txt because .ui files are in separate directory from header files
|
||
|
||
2. **Group Flattening**: When creating a group that contains existing groups, the scene flattens nested groups first to prevent group nesting (see `DesignerScene::createGroup()`)
|
||
|
||
3. **Zoom Implementation**: Uses `QGraphicsView::zoom()` with smooth factor `qPow(1.0015, angleDelta.y())` per wheel event
|
||
|
||
4. **Wchar String Conversion**: Uses `QString::fromWCharArray(L"中文")` for Chinese UI strings
|
||
|
||
5. **Resource File**: `resource/BayTemplate.qrc` contains checkerboard background and icons, referenced in .ui files and CMakeLists.txt
|
||
|
||
---
|
||
|
||
## Graphics View Architecture Deep Dive
|
||
|
||
### MVC/MVVM Architecture Mapping
|
||
|
||
BayTemplate implements a classic **Model-View-Controller** pattern using Qt's Graphics View Framework:
|
||
|
||
| Layer | Qt Graphics View | BayTemplate Implementation | Responsibility |
|
||
|-------|------------------|---------------------------|----------------|
|
||
| **Model** | QGraphicsItem | GraphicsBaseItem, GraphicsRectItem, GraphicPolygonItem, GraphicsItemGroup | 数据模型:存储图元几何数据、样式属性(pen/brush)、变换信息(位置/旋转/缩放)|
|
||
| **Model Manager** | QGraphicsScene | DesignerScene | 模型管理:管理 item 生命周期、选择状态、碰撞检测、背景绘制(网格)|
|
||
| **View** | QGraphicsView | DesignerView | 视图呈现:坐标系变换、滚动条控制、鼠标事件捕获、缩放/平移 |
|
||
| **Controller** | (Qt 无内置) | SelectorManager + BaseSelector 体系 | 控制器:解释用户输入,转换为模型操作(创建/移动/旋转/缩放/编辑)|
|
||
|
||
**关键点:**
|
||
- Qt 的 Graphics View 框架提供了 MVC 中的 Model 和 View 层
|
||
- Controller 层需要开发者自行实现,BayTemplate 使用 SelectorManager 作为中枢
|
||
- DesignerScene 介于 Model Manager 和 Controller 之间,负责事件分发
|
||
|
||
### View-Scene-Item 三层架构详解
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ DesignerView │
|
||
│ (Viewport / Presentation) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ • 坐标变换:Viewport Coordinates ↔ Scene Coordinates │
|
||
│ • 缩放控制:0.02x - 50x (mouse wheel with smooth zoom) │
|
||
│ • 平移控制:Middle-button drag (translate transform) │
|
||
│ • 事件捕获:mousePressEvent/moveEvent/releaseEvent │
|
||
│ • 无滚动条:setHorizontal/VerticalScrollBarPolicy(Off) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ DesignerScene │
|
||
│ (Scene Graph Manager) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ • Item 管理:addItem(), removeItem(), selectedItems() │
|
||
│ • 背景绘制:drawBackground() 绘制 20px 间距的藏青色虚线网格 │
|
||
│ • 事件分发:将鼠标事件转发给 SelectorManager │
|
||
│ • 组操作:createGroup() 实现扁平化打组 │
|
||
│ • 信号发射:signalAddItem(), selectionChanged() │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ GraphicsItem │
|
||
│ (Data Model) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ GraphicsBaseItem (AbstractShapeType<QGraphicsItem>) │
|
||
│ ├── GraphicsRectItem (rectangle, 圆角矩形) │
|
||
│ ├── GraphicPolygonItem (多点绘制,顶点编辑) │
|
||
│ ├── GraphicsBusSectionItem (总线段) │
|
||
│ └── GraphicsItemGroup (AbstractShapeType<QGroup>) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 坐标系与变换系统
|
||
|
||
BayTemplate 使用多层坐标系,理解它们对添加 Document 类至关重要:
|
||
|
||
```
|
||
1. Viewport Coordinates (像素坐标)
|
||
└── DesignerView 的 viewport 矩形,(0,0) 在左上角
|
||
└── 受窗口大小、缩放影响
|
||
|
||
2. Scene Coordinates (场景坐标)
|
||
└── 全局坐标系,(0,0) 为场景原点
|
||
└── 通过 mapToScene()/mapFromScene() 与 Viewport 互转
|
||
└── 图元的位置 pos() 使用场景坐标
|
||
|
||
3. Item Coordinates (图元局部坐标)
|
||
└── 每个 item 的局部坐标系,原点在 (0,0)
|
||
└── BayTemplate 中,图元中心点通常与 (0,0) 重合
|
||
└── boundingRect() 返回局部坐标中的边界
|
||
|
||
4. Parent Coordinates (父项坐标)
|
||
└── 当 item 加入 group 后,使用 group 的局部坐标
|
||
└── mapToParent()/mapFromParent() 用于转换
|
||
```
|
||
|
||
**变换链(Transform Chain):**
|
||
```
|
||
Item Coordinates → Item Transform → Parent Coordinates → ... → Scene Coordinates → View Transform → Viewport Coordinates
|
||
```
|
||
|
||
- **Item Transform**: 由 `setPos()`, `setRotation()`, `setScale()` 控制的 3×3 变换矩阵
|
||
- **Transform Origin**: `setTransformOriginPoint()` 设置变换中心(BayTemplate 中通常为中心点)
|
||
- **View Transform**: DesignerView 的 zoom/pan 产生的变换,影响所有 item 的显示
|
||
|
||
**关键函数:**
|
||
- `item->mapToScene(point)`: Item 局部坐标 → 场景坐标
|
||
- `item->mapToView(point)`: Item 局部坐标 → Viewport 坐标
|
||
- `view->mapToScene(point)`: Viewport 坐标 → 场景坐标
|
||
- `view->mapSceneToViewport(rect)`: 场景矩形 → Viewport 矩形
|
||
|
||
### 操作模式与状态机(Selector 系统)
|
||
|
||
SelectorManager 实现了一个**状态机**,不同的 Selector 代表不同的操作模式:
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ ST_base │
|
||
│ (空闲状态) │
|
||
└────────┬────────┘
|
||
│
|
||
┌──────────────────┼──────────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ ST_creating │ │ ST_moving │ │ ST_editing │
|
||
│ (创建模式) │ │ (移动模式) │ │ (顶点编辑) │
|
||
└──────────────┘ └──────────────┘ └──────────────┘
|
||
│ │
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────┐ ┌──────────────┐
|
||
│ ST_scaling │ │ ST_rotation │
|
||
│ (缩放模式) │ │ (旋转模式) │
|
||
└──────────────┘ └──────────────┘
|
||
│ │
|
||
└────────┬─────────┘
|
||
│
|
||
▼
|
||
┌────────┐
|
||
│ ST_base│ (操作完成后返回)
|
||
└────────┘
|
||
```
|
||
|
||
**状态转换规则:**
|
||
|
||
| 触发条件 | 从状态 | 到状态 | 说明 |
|
||
|---------|--------|--------|------|
|
||
| 从图元面板拖拽 | ST_base | ST_creating | 设置 CreatingSelector 的 m_pCreatingItem |
|
||
| 点击选中 item(左键) | ST_base | ST_moving/ST_scaling/ST_rotation | 根据是否点击在 handle 上判断 |
|
||
| 点击空白处 | 任何 | ST_base | 取消选择,禁用 RubberBandDrag |
|
||
| mouseReleaseEvent | ST_moving/scaling/rotation | ST_base | 操作完成,返回空闲状态 |
|
||
|
||
**BaseSelector 静态成员(所有 Selector 共享):**
|
||
```cpp
|
||
static QPointF ms_ptMouseDown; // 鼠标按下位置(场景坐标)
|
||
static QPointF ms_ptMouseLast; // 鼠标当前位置(场景坐标)
|
||
static double ms_dAngleMouseDownToItem; // 按下时鼠标与 item 中心的夹角(弧度→角度)
|
||
static int ms_nDragHandle; // 当前拖拽的 handle 类型
|
||
```
|
||
|
||
### Handle 控制系统
|
||
|
||
ItemControlHandle 是围绕在选中 item 周围的视觉控件,用于精细操作:
|
||
|
||
```
|
||
H_leftTop ──── H_top ──── H_rightTop
|
||
│ │ │
|
||
│ │ │
|
||
┌────┴────────────┴────────────┴────┐
|
||
│ │
|
||
│ Item Bounding │
|
||
│ Rect │
|
||
│ │
|
||
└────┬────────────┬────────────┬────┘
|
||
│ │ │
|
||
H_left ────── H_bottom ──── H_rightBottom
|
||
```
|
||
|
||
**Handle 类型枚举:**
|
||
```cpp
|
||
enum HandleTag {
|
||
H_none = 0, // 无 handle
|
||
H_leftTop, // 左上角(缩放)
|
||
H_top, // 上边中点(缩放)
|
||
H_rightTop, // 右上角(缩放)
|
||
H_right, // 右边中点(缩放)
|
||
H_rightBottom, // 右下角(缩放)
|
||
H_bottom, // 下边中点(缩放)
|
||
H_leftBottom, // 左下角(缩放)
|
||
H_left, // 左边中点(缩放)
|
||
H_rotate_leftTop, // 左上角外侧(旋转)
|
||
H_rotate_rightTop, // 右上角外侧(旋转)
|
||
H_rotate_rightBottom, // 右下角外侧(旋转)
|
||
H_rotate_leftBottom, // 左下角外侧(旋转)
|
||
H_edit, // 自定义编辑点(多边形顶点、圆角控制点)
|
||
// ... 更多编辑点
|
||
};
|
||
```
|
||
|
||
**Handle 工作机制:**
|
||
1. `AbstractShapeType::updateHandles()` 根据 boundingRect 计算 handle 位置
|
||
2. Handle 位置在 boundingRect 外扩 5px (nMargin = 5)
|
||
3. `collidesWithHandle()` 检测鼠标点击在哪个 handle 上
|
||
4. 不同操作模式对 handle 的响应不同
|
||
|
||
### 操作副本(Operation Copy)模式
|
||
|
||
移动和旋转操作使用"预览副本"技术,提供视觉反馈:
|
||
|
||
```cpp
|
||
// 1. mousePressEvent: 创建副本
|
||
void createOperationCopy() {
|
||
m_pOperationCopy = new QGraphicsPathItem(this->shape());
|
||
m_pOperationCopy->setPen(Qt::DashLine); // 虚线表示预览
|
||
m_pOperationCopy->setPos(this->pos());
|
||
scene->addItem(m_pOperationCopy);
|
||
m_movingIniPos = this->pos();
|
||
}
|
||
|
||
// 2. mouseMoveEvent: 移动副本(本体不动)
|
||
void moveOperationCopy(const QPointF& delta) {
|
||
m_pOperationCopy->setPos(m_movingIniPos + delta);
|
||
}
|
||
|
||
// 3. mouseReleaseEvent: 副本位置应用到本体
|
||
void removeOperationCopy() {
|
||
this->setPos(m_pOperationCopy->pos()); // 本体移动到副本位置
|
||
scene->removeItem(m_pOperationCopy);
|
||
delete m_pOperationCopy;
|
||
}
|
||
```
|
||
|
||
**优势:**
|
||
- 移动过程中保持原来的 selection state
|
||
- 可以拖出 boundingRect_selected 范围(否则会被误认为取消选择)
|
||
- 旋转/缩放同理
|
||
|
||
### 图元基类架构(AbstractShapeType 模板)
|
||
|
||
```cpp
|
||
// 使用模板继承,允许继承 QGraphicsItem 或 QGraphicsItemGroup
|
||
template <typename BaseType = QGraphicsItem>
|
||
class AbstractShapeType : public BaseType {
|
||
protected:
|
||
ShapeType m_type; // T_undefined/T_item/T_group
|
||
QPen m_pen; // 画笔(边框)
|
||
QBrush m_brush; // 画刷(填充)
|
||
double m_dWidth, m_dHeight; // 宽高
|
||
QRectF m_boundingRect; // 局部坐标中的边界矩形
|
||
QRectF m_boundingRect_selected; // 选中框(外扩 5px)
|
||
double m_dSyncRotationByParent; // 父项旋转同步数据
|
||
QVector<ItemControlHandle*> m_vecHanle;
|
||
QGraphicsPathItem* m_pOperationCopy;
|
||
QPointF m_movingIniPos;
|
||
};
|
||
|
||
// 具体类型定义
|
||
typedef AbstractShapeType<QGraphicsItem> AbstractShape; // 单图元基类
|
||
// GraphicsBaseItem : QObject, AbstractShape
|
||
|
||
// GraphicsItemGroup : QObject, AbstractShapeType<QGraphicsItemGroup> // 组图元基类
|
||
```
|
||
|
||
**关键点:**
|
||
- `m_boundingRect` 的中心点与 item 的 (0,0) 原点重合,简化变换计算
|
||
- `updateCoordinate()` 在 resize/editShape 后重新校准原点
|
||
- `m_dSyncRotationByParent` 用于组内 item 跟踪父组的旋转(因为 item 的 rotation() 不会自动更新)
|
||
|
||
### 命令模式(Command Pattern)
|
||
|
||
所有可撤销操作都通过 QUndoCommand 实现:
|
||
|
||
```cpp
|
||
class AddItemCommand : public QUndoCommand {
|
||
void undo() override { scene->removeItem(m_pItem); }
|
||
void redo() override {
|
||
if(!m_pItem->scene()) scene->addItem(m_pItem);
|
||
}
|
||
};
|
||
|
||
class DeleteItemCommand : public QUndoCommand {
|
||
void undo() override {
|
||
foreach(item, m_listItem) scene->addItem(item);
|
||
}
|
||
void redo() override {
|
||
foreach(item, m_listItem) scene->removeItem(item);
|
||
}
|
||
};
|
||
```
|
||
|
||
**关键点:**
|
||
- `QUndoStack::push(command)` 会自动调用 `command->redo()`
|
||
- `DeleteItemCommand` 使用 `removeItem()` 而非 `delete`,确保 undo 能恢复
|
||
- 命令对象持有 item 的引用(非所有权)
|
||
|
||
### 组(Group)扁平化策略
|
||
|
||
`DesignerScene::createGroup()` 实现组扁平化:
|
||
|
||
```cpp
|
||
// 如果选中的 item 中包含已有的 group,先解散该 group,将子项加入新组
|
||
foreach (item, selectedItems) {
|
||
if (item->getType() == T_group) {
|
||
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
|
||
foreach (child, group->childItems()) {
|
||
listItem.push_back(child); // 将子项加入新列表
|
||
}
|
||
removeItem(group);
|
||
delete group; // 删除旧组
|
||
}
|
||
}
|
||
// 创建新组,避免嵌套
|
||
```
|
||
|
||
**目的:**
|
||
- 简化场景图结构(无嵌套的树)
|
||
- 简化坐标计算和碰撞检测
|
||
- 统一 undo/redo 逻辑
|
||
|
||
---
|
||
|
||
## Document 类架构(持久化层)
|
||
|
||
### Document 类在 MVC 中的位置
|
||
|
||
Document 类作为**持久化层**插入现有架构,位于 Model 层之下:
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ Controller │
|
||
│ (SelectorManager + 各种 Selector) │
|
||
└──────────────┬──────────────────────────┘
|
||
│
|
||
┌──────────────▼──────────────────────────┐
|
||
│ View │
|
||
│ (DesignerView + UI) │
|
||
└──────────────┬──────────────────────────┘
|
||
│
|
||
┌──────────────▼──────────────────────────┐
|
||
│ Model Manager │
|
||
│ (DesignerScene) │
|
||
└──────────────┬──────────────────────────┘
|
||
│
|
||
┌──────────────▼──────────────────────────┐
|
||
│ Model │
|
||
│ (GraphicsItem 层次结构) │
|
||
└──────────────┬──────────────────────────┘
|
||
│
|
||
┌──────────────▼──────────────────────────┐
|
||
│ Document │
|
||
│ (序列化/状态管理) │
|
||
└──────────────────────────────────────────┘
|
||
```
|
||
|
||
**设计原则:**
|
||
- **组合优于继承**:Document 持有 `DesignerScene*` 弱引用,不继承 Scene
|
||
- **单向依赖**:Document 只依赖 DesignerScene 的接口,不依赖 CMainWindow
|
||
- **信号槽通信**:通过信号与外部通信修改状态,避免紧耦合
|
||
|
||
### Document 类职责
|
||
|
||
1. **序列化/反序列化**(JSON 格式)
|
||
- `serialize()` / `deserialize()`: 核心序列化为 QByteArray
|
||
- `saveToFile()` / `loadFromFile()`: 文件 I/O 封装
|
||
- 递归序列化/反序列化图元层次结构(支持组图元嵌套)
|
||
|
||
2. **状态管理**
|
||
- `m_filename`: 文档文件名(空表示未保存的新文档)
|
||
- `m_modified`: 是否被修改(通过 `setModified()` 和 `modifiedChanged` 信号通知)
|
||
- `m_created` / `m_modifiedTime` / `m_lastSavedTime`: 时间戳
|
||
- `m_metaData`: 可扩展的自定义元数据 (`QMap<QString, QVariant>`)
|
||
|
||
3. **与 CMainWindow 的集成**
|
||
- `initializeDocument()`: 创建 Document 并关联 DesignerScene
|
||
- 连接 `modifiedChanged` 信号来更新窗口标题(添加 * 标记)
|
||
- `closeEvent()` 中检查 `isModified()` 提示用户保存
|
||
- 文件操作:New/Open/Save 通过 Document 完成
|
||
|
||
### 序列化格式(v1.0)
|
||
|
||
```json
|
||
{
|
||
"version": "1.0",
|
||
"created": "2024-01-01T12:00:00",
|
||
"modified": "2024-01-01T14:30:00",
|
||
"filename": "/path/to/file.bay",
|
||
"metaData": {
|
||
"customKey": "customValue"
|
||
},
|
||
"items": [
|
||
{
|
||
"type": "GraphicsRectItem",
|
||
"id": "140737488320",
|
||
"pos": {"x": 100.0, "y": 200.0},
|
||
"rotation": 45.0,
|
||
"scale": {"x": 1.0, "y": 1.0},
|
||
"pen": {"color": "#000000", "width": 1.0, "style": 0},
|
||
"brush": {"color": "#FF0000"},
|
||
"width": 100.0,
|
||
"height": 50.0,
|
||
"properties": {},
|
||
"children": []
|
||
},
|
||
{
|
||
"type": "GraphicsItemGroup",
|
||
"pos": {"x": 300.0, "y": 150.0},
|
||
"rotation": 0.0,
|
||
"scale": {"x": 1.0, "y": 1.0},
|
||
"width": 200.0,
|
||
"height": 100.0,
|
||
"children": [
|
||
{ "type": "GraphicsRectItem", ... },
|
||
{ "type": "GraphicsBusSectionItem", ... }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 实现细节
|
||
|
||
**类型识别:** 使用 `qobject_cast<T*>(item)` 配合 `getTypeString()` 函数识别图元类型(要求类型有 `Q_OBJECT` 宏)
|
||
|
||
**QVariant ↔ QJsonValue 转换:** 提供 `toJsonValue()` / `fromJsonValue()` 辅助函数处理元数据序列化
|
||
|
||
**坐标系统:** 图元使用中心原点,`pos()` 存储场景坐标,`boundingRect()` 使用局部坐标
|
||
|
||
**文件扩展名:** `.bay` 作为 BayTemplate 专用格式
|
||
|
||
### 关键文件
|
||
|
||
- `include/document.h`: Document 类声明
|
||
- `source/document.cpp`: 序列化/反序列化实现
|
||
- `source/mainwindow.cpp`: `initializeDocument()`, `onAction_new()`, `onAction_open()`, `onAction_save()`
|
||
|
||
---
|
||
|
||
## Selector 系统设计模式总结
|
||
|
||
### 状态模式(State Pattern)
|
||
|
||
SelectorManager 实现状态模式,不同的 Selector 子类代表不同的状态:
|
||
|
||
```cpp
|
||
// Context
|
||
class SelectorManager {
|
||
BaseSelector* getWorkingSelector(); // 返回当前状态的 Selector
|
||
void setWorkingSelector(SelectorType);
|
||
};
|
||
|
||
// State 接口
|
||
class BaseSelector {
|
||
virtual void mousePressEvent(...) = 0;
|
||
virtual void mouseMoveEvent(...) = 0;
|
||
virtual void mouseReleaseEvent(...) = 0;
|
||
};
|
||
|
||
// Concrete State
|
||
class MovingSelector : public BaseSelector { ... };
|
||
class ScalingSelector : public BaseSelector { ... };
|
||
// ...
|
||
```
|
||
|
||
**静态成员共享的原因:**
|
||
BaseSelector 的静态成员(ms_ptMouseDown 等)在所有 Selector 实例间共享,使得状态切换时无需传递上下文数据。
|
||
|
||
### 操作副本模式(Operation Preview Pattern)
|
||
|
||
移动、旋转、缩放都使用"操作副本"作为视觉预览:
|
||
1. mousePressEvent: 创建虚线副本
|
||
2. mouseMoveEvent: 移动副本(本体不动)
|
||
3. mouseReleaseEvent: 将副本的变换应用到本体,删除副本
|
||
|
||
### Handle 系统(Manipulator Pattern)
|
||
|
||
ItemControlHandle 实现操纵器模式,将抽象的变换操作(缩放、旋转)映射到可视化的交互控件。
|
||
|
||
---
|
||
|
||
## 坐标系转换速查表
|
||
|
||
| 转换 | 函数 | 说明 |
|
||
|------|------|------|
|
||
| Item → Scene | `item->mapToScene(point)` | 局部坐标转场景坐标 |
|
||
| Scene → Item | `item->mapFromScene(point)` | 场景坐标转局部坐标 |
|
||
| Item → Viewport | `item->mapToView(point)` | 局部坐标转屏幕像素 |
|
||
| Item → Parent | `item->mapToParent(point)` | 局部坐标转父项坐标 |
|
||
| Viewport → Scene | `view->mapToScene(point)` | 屏幕像素转场景坐标 |
|
||
| Scene Rect → Viewport Rect | `view->mapSceneToViewport(rect)` | 场景矩形转屏幕矩形 |
|
||
|
||
## Handle 类型速查表
|
||
|
||
| Handle 类型 | 位置 | 功能 |
|
||
|------------|------|------|
|
||
| H_leftTop ~ H_left | boundingRect 八方向 | 缩放 |
|
||
| H_rotate_leftTop ~ H_rotate_leftBottom | boundingRect 外扩 | 旋转 |
|
||
| H_edit + N | 自定义位置 | 形状编辑(多边形顶点、圆角控制点)|
|
||
|
||
## 操作模式速查表
|
||
|
||
| SelectorType | 触发方式 | 用途 |
|
||
|-------------|---------|------|
|
||
| ST_base | 默认 | 空闲状态,允许橡胶框选择 |
|
||
| ST_creating | 从图元面板拖拽 | 创建新图元 |
|
||
| ST_moving | 点击 item 主体 | 移动选中项 |
|
||
| ST_scaling | 点击缩放 handle | 缩放选中项 |
|
||
| ST_rotation | 点击旋转 handle | 旋转选中项 |
|
||
| ST_editing | 点击编辑 handle | 编辑多边形顶点等 |
|