基于Qt/C++的节点式图形编辑器
·
Qt/C++实现的节点式图形编辑器,可以添加左侧树上的节点,面板可以放大缩小,节点之间有输入输出接口,使用线进行连接,通过鼠标选中节点或线按住delete键可以进行删除,可以生成节点的图片进行导出的功能。 源码: 使用Qt5.6.1_MinGW、Qt5.13.1_MinGW编译通过,其他版本请自行尝试。
在某个摸鱼的下午突然想搞点可视化编程的东西,于是撸了个能拖拽节点的图形编辑器。核心思路其实挺简单——每个节点都是场景里的可交互元件,接口连线本质上是数学坐标系下的路径计算。
先看节点类的定义:
class NodeItem : public QGraphicsItem {
public:
QRectF boundingRect() const override {
return QRectF(-width/2, -height/2, width, height);
}
void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override {
// 绘制主体
painter->setBrush(QColor(45, 45, 48));
painter->drawRoundedRect(boundingRect(), 5, 5);
// 绘制接口
drawPorts(painter);
}
private:
void drawPorts(QPainter* painter) {
const int portRadius = 6;
QPointF inputPos(-width/2, -portRadius*2);
QPointF outputPos(width/2 - portRadius*2, -portRadius*2);
painter->setBrush(Qt::cyan);
painter->drawEllipse(inputPos, portRadius, portRadius); // 输入口
painter->setBrush(Qt::magenta);
painter->drawEllipse(outputPos, portRadius, portRadius); // 输出口
}
const int width = 120;
const int height = 80;
};
这个节点的妙处在于坐标系处理——所有绘制都以节点中心为原点,移动时直接用setPos()就能准确定位。接口位置计算用简单算术搞定,输入口固定在左侧边缘,输出口在右侧,后续连线时用scene坐标转换就能获取准确位置。

连线功能涉及贝塞尔曲线:
void ConnectionItem::updatePath() {
QPointF start = mapFromScene(startPort->scenePos());
QPointF end = mapFromScene(endPort->scenePos());
QPainterPath path(start);
path.cubicTo(
start + QPointF(50, 0), // 控制点1
end - QPointF(50, 0), // 控制点2
end
);
setPath(path);
}
这里用了三次贝塞尔曲线让连线看起来自然。控制点的水平偏移量固定为50像素,这样无论节点间距多大,连线都能呈现平滑的S形。当节点移动时,只需要调用updatePath()就能实时更新连线轨迹。
删除功能实现得相当暴力:
void Scene::keyPressEvent(QKeyEvent* event) {
if(event->key() == Qt::Key_Delete) {
foreach(auto item, selectedItems()) {
if(auto conn = dynamic_cast<ConnectionItem*>(item)) {
conn->disconnectPorts(); // 先断开端口关联
}
delete item; // 直接删除图形项
}
}
}
这里直接捕获场景的键盘事件,把选中的元素统统干掉。注意连线删除前要先断开端口关联,否则可能出现野指针。这种处理方式虽然简单,但胜在直观有效。
导出图片就调个现成接口:
void exportToImage() {
QImage image(scene->sceneRect().size().toSize(), QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
scene->render(&painter);
image.save("node_graph.png");
}
把整个场景渲染到QImage里,想存什么格式改后缀就行。不过要注意场景坐标系和图像尺寸的对应关系,否则可能导出空白图片。

(注:实测Qt5.6和5.13版本可用,其他版本可能需要微调图形项缓存相关的代码)
更多推荐


所有评论(0)