@@ -23,10 +23,28 @@ public: | |||
virtual bool saveToFile(const QString &filePath) = 0; | |||
virtual bool loadFromFile(const QString &filePath) = 0; | |||
// 设置变换矩阵 | |||
virtual void setTransform(const QTransform& transform) { | |||
m_transform = transform; | |||
update(); | |||
} | |||
// 获取文档内容尺寸(子类需要重写) | |||
virtual QSize getContentSize() const { return QSize(800, 600); } // 默认尺寸 | |||
// 重写paintEvent应用变换 | |||
// virtual void paintEvent(QPaintEvent *event) override { | |||
// QPainter painter(this); | |||
// painter.setTransform(m_transform); | |||
// // 子类应调用父类的paintEvent? 或者自行绘制 | |||
// } | |||
protected: | |||
DocumentType m_type; | |||
QString m_filePath; | |||
bool m_modified = false; | |||
QTransform m_transform; | |||
}; | |||
#endif // BASEDOCUMENT_H |
@@ -29,22 +29,38 @@ HMIDocument::HMIDocument(QWidget *parent) | |||
m_canDrawRectangle(false), | |||
m_lastPastePos(0, 0) | |||
{ | |||
// 创建绘图场景 | |||
// 创建绘图场景 - 固定大小 | |||
m_scene = new QGraphicsScene(this); | |||
m_scene->setSceneRect(0, 0, 800, 600); | |||
m_scene->setSceneRect(0, 0, 1000, 600); // 固定画布尺寸 | |||
m_scene->setBackgroundBrush(QBrush(Qt::lightGray)); | |||
// 创建视图 | |||
// 创建视图 - 关键修改:使用滚动区域包装 | |||
m_scrollArea = new QScrollArea(this); | |||
m_scrollArea->setWidgetResizable(false); // 禁止内容自动调整大小 | |||
m_scrollArea->setFrameShape(QFrame::NoFrame); // 无边框 | |||
// 创建真正的视图部件 | |||
m_view = new QGraphicsView(m_scene, this); | |||
m_view->setRenderHint(QPainter::Antialiasing); | |||
m_view->setDragMode(QGraphicsView::RubberBandDrag); | |||
m_view->setAcceptDrops(true); | |||
m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 隐藏自身滚动条 | |||
m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | |||
// 关键:视图固定大小与场景匹配 | |||
m_view->setMinimumSize(1000, 600); | |||
m_view->setMaximumSize(1000, 600); | |||
m_view->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); | |||
m_view->viewport()->installEventFilter(this); | |||
// 布局(让视图占满文档区域) | |||
// 添加视图到滚动区域 | |||
m_scrollArea->setWidget(m_view); | |||
m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | |||
// 设置主布局 | |||
auto layout = new QVBoxLayout(this); | |||
layout->setContentsMargins(0, 0, 0, 0); | |||
layout->addWidget(m_view); | |||
layout->addWidget(m_scrollArea); | |||
setLayout(layout); | |||
} | |||
@@ -40,14 +40,12 @@ public: | |||
bool loadFromFile(const QString &filePath) override; | |||
void markModified(); | |||
signals: | |||
void titleChanged(const QString &title); | |||
protected: | |||
bool eventFilter(QObject *obj, QEvent *event) override; | |||
private: | |||
private: | |||
// 辅助方法 | |||
void createShape(const QString& type, const QPointF &pos); | |||
void resetDrawFlags(); | |||
@@ -63,6 +61,7 @@ private: | |||
QString m_title; | |||
QGraphicsScene *m_scene; | |||
QGraphicsView *m_view; | |||
QScrollArea *m_scrollArea; | |||
bool m_canDrawEllipse; | |||
bool m_canDrawRectangle; | |||
@@ -17,7 +17,7 @@ | |||
#include <QFileDialog> | |||
#include <QMessageBox> | |||
#include <QTextEdit> | |||
#include<QDockWidget> | |||
MainWindow::MainWindow(QWidget *parent) | |||
: QMainWindow(parent) | |||
{ | |||
@@ -32,11 +32,64 @@ MainWindow::MainWindow(QWidget *parent) | |||
connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::onCloseTab); | |||
createMenus();// 创建菜单和工具栏 | |||
createToolbars(); | |||
createLogPanel(); // 创建日志面板 | |||
} | |||
MainWindow::~MainWindow() | |||
{ | |||
} | |||
void MainWindow::createLogPanel() | |||
{ | |||
// 创建可停靠窗口 | |||
m_logDock = new QDockWidget("消息输出", this); | |||
m_logDock->setAllowedAreas(Qt::BottomDockWidgetArea); | |||
m_logDock->setFeatures(QDockWidget::DockWidgetClosable | | |||
QDockWidget::DockWidgetMovable | | |||
QDockWidget::DockWidgetFloatable); // 添加垂直标题栏 | |||
// 设置高度范围而非固定高度 | |||
m_logDock->setMinimumHeight(150); | |||
m_logDock->setMaximumHeight(400); | |||
// 创建主内容部件 | |||
QWidget* logWidget = new QWidget; | |||
QVBoxLayout* logLayout = new QVBoxLayout; | |||
logLayout->setContentsMargins(2, 2, 2, 2); | |||
// 创建文本编辑框用于显示日志 | |||
m_logEdit = new QTextEdit; | |||
m_logEdit->setReadOnly(true); | |||
m_logEdit->setFont(QFont("Consolas", 10)); | |||
m_logEdit->setStyleSheet("background-color: #f8f8f8; border: 1px solid #d9d9d9;"); | |||
m_logEdit->setMinimumHeight(100); | |||
// 创建控制按钮 | |||
QHBoxLayout* btnLayout = new QHBoxLayout; | |||
m_clearLogBtn = new QPushButton("清空日志"); | |||
btnLayout->addWidget(m_clearLogBtn); | |||
btnLayout->addStretch(); | |||
// 组合布局 | |||
logLayout->addLayout(btnLayout); | |||
logLayout->addWidget(m_logEdit); | |||
logWidget->setLayout(logLayout); | |||
m_logDock->setWidget(logWidget); | |||
addDockWidget(Qt::BottomDockWidgetArea, m_logDock); | |||
// 初始显示状态 | |||
m_logDock->show(); | |||
// 连接信号 | |||
connect(m_clearLogBtn, &QPushButton::clicked, this, &MainWindow::onClearLogButtonClicked); | |||
} | |||
// 槽函数实现 | |||
void MainWindow::onClearLogButtonClicked() | |||
{ | |||
m_logEdit->clear(); | |||
m_logEdit->append("日志已清空"); | |||
} | |||
// 创建菜单栏 | |||
void MainWindow::createMenus() | |||
@@ -7,7 +7,7 @@ | |||
#include <QAction> | |||
#include<QTextEdit> | |||
#include "basedocument.h" | |||
#include<QPushButton> | |||
QT_BEGIN_NAMESPACE | |||
namespace Ui { class MainWindow; } | |||
QT_END_NAMESPACE | |||
@@ -31,9 +31,14 @@ private slots: | |||
void onSaveAs(); // 另存为文档 | |||
void onOpen(); // 打开文档 | |||
void onCloseTab(int index); // 关闭标签页 | |||
void onClearLogButtonClicked(); | |||
private: | |||
void createMenus(); // 创建菜单栏 | |||
QDockWidget* m_logDock; | |||
QTextEdit* m_logEdit; | |||
QPushButton* m_clearLogBtn; | |||
void createLogPanel(); | |||
void createToolbars(); // 创建工具栏(左侧) | |||
void updateToolBar(BaseDocument *doc); // 根据文档类型更新工具栏 | |||
void saveDocument(BaseDocument *doc); // 保存文档 | |||
@@ -1,13 +1,13 @@ | |||
// plcdocument.h | |||
#ifndef PLCDOCUMENT_H | |||
#define PLCDOCUMENT_H | |||
#include "basedocument.h" | |||
#include "plcitems.h" // 确保包含PLCItem的定义 | |||
#include "plcitems.h" | |||
#include <QGraphicsScene> | |||
#include <QGraphicsView> | |||
#include <QGraphicsLineItem> | |||
#include <QGraphicsRectItem> | |||
#include <QGraphicsEllipseItem> | |||
#include <QMap> | |||
#include <QSet> | |||
#include <QVector> | |||
@@ -17,6 +17,10 @@ | |||
#include <QMimeData> | |||
#include <QPair> | |||
const int DEFAULT_ROWS = 11; | |||
const int DEFAULT_COLUMNS = 15; | |||
const int DEFAULT_CELL_SIZE = 70; | |||
class PLCItem; | |||
class ConnectionLine; | |||
@@ -24,6 +28,19 @@ class PLCDocument : public BaseDocument | |||
{ | |||
Q_OBJECT | |||
public: | |||
enum TerminalType { | |||
NoTerminal = 0, | |||
LeftTerminal, | |||
RightTerminal, | |||
RowTerminal | |||
}; | |||
enum ConnectionSourceType { | |||
ConnectionNone, // 无连接 | |||
ConnectionFromItem, // 起点是元件的端子 | |||
ConnectionFromRowTerminal // 起点是行触点 | |||
}; | |||
explicit PLCDocument(QWidget *parent = nullptr); | |||
~PLCDocument() override; | |||
@@ -34,28 +51,20 @@ public: | |||
bool saveToFile(const QString &filePath) override; | |||
bool loadFromFile(const QString &filePath) override; | |||
// 创建PLC元件 | |||
void createPLCItem(const QString &type, const QPointF &pos); | |||
// 连接管理 | |||
void startConnection(PLCItem *startItem); | |||
void endConnection(PLCItem *endItem); | |||
void startConnection(PLCItem *startItem, TerminalType startTerminal); | |||
void startConnectionFromRowTerminal(int row); | |||
void clearCurrentConnection(); | |||
// 运行状态控制 | |||
bool tryEndConnection(PLCItem *endItem, TerminalType endTerminal); | |||
void setSimulationRunning(bool running); | |||
void resetSimulation(); | |||
void setCurrentTool(const QString &tool) { m_currentTool = tool; } | |||
// 获取表格参数 | |||
int rowCount() const { return m_rows; } | |||
int columnCount() const { return m_columns; } | |||
int cellSize() const { return m_cellSize; } | |||
// 计算单元格中心位置 | |||
QPointF snapToCellCenter(const QPointF &pos) const; | |||
// 检查并修正位置,确保在表格内 | |||
QPointF constrainToTable(const QPointF &pos) const; | |||
void setTitle(const QString &title) { m_title = title; } | |||
@@ -65,63 +74,79 @@ protected: | |||
void resizeEvent(QResizeEvent *event) override; | |||
private slots: | |||
// 处理场景变化(用于更新连接线) | |||
void handleSceneChanged(); | |||
void onRowTerminalConnection(); | |||
private: | |||
// 表格管理 | |||
void createRealTable(); | |||
void clearTable(); | |||
void adjustTableToFitView(); // 调整表格以适应视图大小 | |||
// 项目位置改变处理 | |||
//void clearTable(); | |||
TerminalType whichTerminal(const QPointF& scenePos, PLCItem* item) const; | |||
bool isTerminalUsed(PLCItem* item, TerminalType terminal) const; | |||
void handleItemPositionChange(PLCItem *item); | |||
// 仿真与连接管理 | |||
bool isCellOccupied(int col, int row, PLCItem* excludeItem) const; | |||
void updateConnections(); | |||
void createContextMenu(QPoint globalPos); | |||
// 根据类型创建PLC项 - 新增的辅助函数 | |||
PLCItem* createItemByType(PLCItem::ItemType type); | |||
void removeInvalidConnectionsForItem(PLCItem* movedItem); | |||
QList<PLCItem*> allPLCItems() const; | |||
QGraphicsScene *m_scene; | |||
QGraphicsView *m_view; | |||
QString m_currentTool; // 当前选择的工具 | |||
// 连接管理 | |||
PLCItem *m_connectionStart = nullptr; | |||
QString m_currentTool; | |||
ConnectionSourceType m_connectionSourceType = ConnectionNone; | |||
PLCItem *m_connectionStartItem = nullptr; | |||
TerminalType m_startTerminal = NoTerminal; | |||
int m_connectionStartRow = -1; | |||
QGraphicsLineItem *m_tempLine = nullptr; | |||
QList<ConnectionLine*> m_connections; | |||
// 运行状态 | |||
QMap<PLCItem*, QMap<TerminalType, bool>> terminalConnections; | |||
bool m_simulationRunning = false; | |||
QSet<PLCItem*> m_activeItems; | |||
// 表格参数 - 使用常量定义默认值 | |||
int m_rows = 15; // 表格行数 | |||
int m_columns = 20; // 表格列数 | |||
int m_cellSize = 50; // 单元格大小(像素) | |||
// 表格图形项 | |||
int m_rows = DEFAULT_ROWS; | |||
int m_columns = DEFAULT_COLUMNS; | |||
int m_cellSize = DEFAULT_CELL_SIZE; | |||
QGraphicsRectItem* m_tableFrame = nullptr; | |||
QVector<QGraphicsLineItem*> m_horizontalLines; | |||
QVector<QGraphicsLineItem*> m_verticalLines; | |||
QString m_title; // 添加标题成员变量 | |||
QString m_title; | |||
QVector<QGraphicsEllipseItem*> m_rowTerminals; | |||
QMap<QGraphicsEllipseItem*, int> m_rowTerminalMap; | |||
QMap<PLCItem*, QPointF> m_lastValidPositions; | |||
QSet<PLCItem*> m_processingItems; | |||
bool m_loadingFile = false; | |||
}; | |||
// 连接线类 | |||
class ConnectionLine : public QGraphicsLineItem | |||
{ | |||
public: | |||
ConnectionLine(PLCItem *start, PLCItem *end, QGraphicsItem *parent = nullptr); | |||
ConnectionLine( | |||
PLCItem *startItem, PLCDocument::TerminalType startTerminal, | |||
PLCItem *endItem, PLCDocument::TerminalType endTerminal, | |||
QGraphicsItem *parent = nullptr | |||
); | |||
void updatePosition(); | |||
PLCItem* startItem() const { return m_startItem; } | |||
PLCItem* endItem() const { return m_endItem; } | |||
PLCDocument::TerminalType startTerminal() const { return m_startTerminal; } | |||
PLCDocument::TerminalType endTerminal() const { return m_endTerminal; } | |||
void setRowTerminalTargetRow(int row) { m_rowTerminalTargetRow = row; } | |||
int rowTerminalTargetRow() const { return m_rowTerminalTargetRow; } | |||
void setRowTerminalSourceRow(int row) { m_rowTerminalSourceRow = row; } | |||
int rowTerminalSourceRow() const { return m_rowTerminalSourceRow; } | |||
void setStartTerminalPoint(const QPointF& point) { m_startTerminalPoint = point; } | |||
void setEndTerminalPoint(const QPointF& point) { m_endTerminalPoint = point; } | |||
private: | |||
PLCItem *m_startItem; | |||
PLCDocument::TerminalType m_startTerminal; | |||
PLCItem *m_endItem; | |||
PLCDocument::TerminalType m_endTerminal; | |||
QPointF m_startTerminalPoint; | |||
QPointF m_endTerminalPoint; | |||
int m_rowTerminalTargetRow = -1; | |||
int m_rowTerminalSourceRow = -1; | |||
}; | |||
#endif // PLCDOCUMENT_H |
@@ -11,7 +11,7 @@ PLCItem::PLCItem(ItemType type, QGraphicsItem *parent) | |||
QRectF PLCItem::boundingRect() const | |||
{ | |||
return m_boundingRect; // 返回我们设置的边界矩形 | |||
return m_boundingRect; //返回我们设置的边界矩形 | |||
} | |||
void PLCItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) | |||
@@ -104,7 +104,7 @@ void PLCItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q | |||
painter->drawLine(QPointF(-25, 0), QPointF(-15, 0)); // 左侧横线 | |||
painter->drawArc(QRectF(-15, -15, 15, 30), 90 * 16, 180 * 16);// 绘制左括号 "(" | |||
painter->drawArc(QRectF(0, -15, 15, 30), 270 * 16, 180 * 16);// 绘制右括号 ")" | |||
painter->drawLine(QPointF(15, 0), QPointF(25, 0));// 右侧横线 | |||
//painter->drawLine(QPointF(15, 0), QPointF(25, 0));// 右侧横线 | |||
painter->setPen(QPen(Qt::black, 4));// 调整线条宽度使括号更清晰 | |||
break; | |||
} | |||
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-09T17:07:02. --> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-11T21:25:16. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||