Skip to content

Commit 604a854

Browse files
DuanYHhevake
authored andcommitted
!46 feat: graphviz_render中新增高清缩放模式,通过ui右上角按钮切换
* feat: graphviz_render中新增高清缩放模式,通过ui右上角按钮切换
1 parent 219aaa4 commit 604a854

8 files changed

Lines changed: 434 additions & 55 deletions

File tree

tools/graphviz_render/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ Pillow==9.0.0
22
Pillow==7.0.0
33
Pillow==11.1.0
44
PyQt5==5.15.11
5+
python3-pyqt5.qtsvg
6+

tools/graphviz_render/src/data_processing.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from PyQt5.QtCore import QThread, pyqtSignal
2929

3030
class DataProcessing(QThread):
31-
data_received = pyqtSignal(QPixmap)
31+
data_received = pyqtSignal(QPixmap, bytes)
3232

3333
def __init__(self):
3434
super().__init__()
@@ -37,9 +37,10 @@ def __init__(self):
3737
self._cond = threading.Condition(self._lock) # 数据变更条件变量
3838

3939
# 共享状态变量
40-
self._last_dot_data = '' # 最后接收的DOT数据
40+
self._last_dot_data = '' # 最后接收的DOT数据
4141
self._running = True # 线程运行标志
4242
self._original_image = None # 原始图像缓存
43+
self._svg_bytes = b'' # SVG字节数据缓存(用于高清光栅化)
4344
self._pending_update = False # 更新标记
4445

4546
def run(self):
@@ -62,28 +63,38 @@ def _process_update(self):
6263

6364
try:
6465
# 生成图像(耗时操作)
65-
image = self._render_dot(current_data)
66+
image, svg_bytes = self._render_dot(current_data)
6667
if image:
6768
self._original_image = image
69+
self._svg_bytes = svg_bytes
6870
self._update_display()
6971
except Exception as e:
7072
print(f"Render error: {e}")
7173

72-
def _render_dot(self, dot_data: str) -> Image.Image:
73-
"""DOT数据渲染方法"""
74-
proc = subprocess.Popen(
74+
def _render_dot(self, dot_data: str):
75+
"""DOT数据渲染方法,同时生成 PNG(预览)和 SVG(高清光栅化用)"""
76+
# 渲染 SVG(矢量格式,用于缩放后高清重绘)
77+
proc_svg = subprocess.Popen(
78+
['dot', '-Tsvg'],
79+
stdin=subprocess.PIPE,
80+
stdout=subprocess.PIPE,
81+
stderr=subprocess.PIPE
82+
)
83+
svg_out, _ = proc_svg.communicate(dot_data.encode())
84+
85+
# 渲染 PNG(用于初始快速预览)
86+
proc_png = subprocess.Popen(
7587
['dot', '-Tpng'],
7688
stdin=subprocess.PIPE,
7789
stdout=subprocess.PIPE,
7890
stderr=subprocess.PIPE
7991
)
80-
stdout, stderr = proc.communicate(dot_data.encode())
92+
png_out, stderr = proc_png.communicate(dot_data.encode())
8193

82-
if proc.returncode != 0:
94+
if proc_png.returncode != 0:
8395
raise RuntimeError(f"Graphviz error: {stderr.decode()}")
8496

85-
return Image.open(BytesIO(stdout))
86-
97+
return Image.open(BytesIO(png_out)), svg_out
8798

8899
def _update_display(self):
89100
"""图像显示更新"""
@@ -95,8 +106,8 @@ def _update_display(self):
95106
qimg = self._pil_to_qimage(self._original_image)
96107
pixmap = QPixmap.fromImage(qimg)
97108

98-
# 发射信号(跨线程安全)
99-
self.data_received.emit(pixmap)
109+
# 发射信号(跨线程安全),同时携带 SVG 数据供高清光栅化
110+
self.data_received.emit(pixmap, self._svg_bytes)
100111
except Exception as e:
101112
print(f"Display error: {e}")
102113

tools/graphviz_render/src/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
import os
2424
import sys
25-
import signal
2625

2726
from ui.viewer import GraphvizViewer
2827
from signal_handler import SignalHandler

tools/graphviz_render/src/signal_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#
2020

2121
import signal
22-
from PyQt5.QtCore import QObject, pyqtSignal, QTimer
22+
from PyQt5.QtCore import QObject, pyqtSignal
2323

2424
class SignalHandler(QObject):
2525
sig_interrupt = pyqtSignal()

tools/graphviz_render/src/transform/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919
#
2020

2121
from .zoomable import ZoomableGraphicsView
22+
from .zoomable_svg import ZoomableSvgGraphicsView

tools/graphviz_render/src/transform/zoomable.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
class ZoomableGraphicsView(QGraphicsView):
2828
def __init__(self, parent=None):
2929
super().__init__(parent)
30-
30+
3131
# 初始化视图设置
3232
self._setup_view()
3333
self._setup_scene()
3434
self._setup_interaction()
3535
self._setup_indicators()
36-
36+
3737
# 初始化参数
3838
self._zoom_factor = 1.0
3939
self.min_zoom = 0.1
@@ -67,7 +67,7 @@ def _setup_scene(self):
6767
self.scene = QGraphicsScene(self)
6868
self.scene.setSceneRect(-1e6, -1e6, 2e6, 2e6) # 超大场景范围
6969
self.setScene(self.scene)
70-
70+
7171
self.pixmap_item = QGraphicsPixmapItem()
7272
self.pixmap_item.setTransformationMode(Qt.SmoothTransformation)
7373
self.pixmap_item.setShapeMode(QGraphicsPixmapItem.BoundingRectShape)
@@ -92,14 +92,14 @@ def _setup_indicators(self):
9292
self.center_indicator.setVisible(False)
9393
self.scene.addItem(self.center_indicator)
9494

95-
def update_image(self, pixmap: QPixmap):
96-
"""更新图像并自适应视图"""
95+
def update_image(self, pixmap: QPixmap, svg_bytes: bytes = b''):
96+
"""更新图像并自适应视图(svg_bytes 参数由方案B使用,此处忽略)"""
9797
self.pixmap_item.setPixmap(pixmap)
9898
self._center_pixmap(pixmap)
9999
if self.fisrt_refresh:
100100
self.fisrt_refresh = False
101101
self.fit_to_view()
102-
102+
103103
def _center_pixmap(self, pixmap: QPixmap):
104104
"""居中放置图元"""
105105
self.pixmap_item.setPos(-pixmap.width()/2, -pixmap.height()/2)
@@ -120,7 +120,7 @@ def wheelEvent(self, event: QWheelEvent):
120120
zoom_in = event.angleDelta().y() > 0
121121
factor = 1.25 if zoom_in else 0.8
122122
new_zoom = self._zoom_factor * factor
123-
123+
124124
# 应用缩放限制
125125
if self.min_zoom <= new_zoom <= self.max_zoom:
126126
self.scale(factor, factor)
@@ -142,7 +142,7 @@ def keyPressEvent(self, event: QKeyEvent):
142142
new_zoom = self._zoom_factor * factor
143143

144144
# 应用缩放限制
145-
if self.min_zoom <= new_zoom <= self.max_zoom:
145+
if self.min_zoom <= new_zoom <= self.max_zoom:
146146
self.scale(factor, factor)
147147
self._zoom_factor = new_zoom
148148
return
@@ -156,9 +156,9 @@ def mousePressEvent(self, event: QMouseEvent):
156156
self.dragging = True
157157
self.last_mouse_pos = event.pos()
158158
self.setCursor(Qt.ClosedHandCursor)
159-
159+
160160
super().mousePressEvent(event)
161-
161+
162162
def mouseDoubleClickEvent(self, event: QMouseEvent):
163163
"""鼠标双击事件处理(增强版)"""
164164
if event.button() == Qt.RightButton:
@@ -167,7 +167,7 @@ def mouseDoubleClickEvent(self, event: QMouseEvent):
167167
return
168168
elif event.button() == Qt.LeftButton:
169169
event.accept()
170-
factor = 2
170+
factor = 1.25
171171
new_zoom = self._zoom_factor * factor
172172

173173
# 应用缩放限制
@@ -183,13 +183,13 @@ def mouseMoveEvent(self, event: QMouseEvent):
183183
if self.dragging:
184184
delta = event.pos() - self.last_mouse_pos
185185
self.last_mouse_pos = event.pos()
186-
186+
187187
# 更新滚动条实现拖拽
188188
self.horizontalScrollBar().setValue(
189189
self.horizontalScrollBar().value() - delta.x())
190190
self.verticalScrollBar().setValue(
191191
self.verticalScrollBar().value() - delta.y())
192-
192+
193193
super().mouseMoveEvent(event)
194194

195195
def mouseReleaseEvent(self, event: QMouseEvent):
@@ -206,10 +206,10 @@ def _fit_and_center_animation(self):
206206

207207
# 先执行自适应调整
208208
self.fit_to_view()
209-
209+
210210
# 获取最终场景中心坐标
211211
final_center = self.pixmap_item.sceneBoundingRect().center()
212-
212+
213213
# 创建组合动画
214214
self._create_center_animation(final_center)
215215

@@ -218,14 +218,14 @@ def _create_center_animation(self, target_center: QPointF):
218218
# 平移动画
219219
anim_h = QPropertyAnimation(self.horizontalScrollBar(), b"value")
220220
anim_v = QPropertyAnimation(self.verticalScrollBar(), b"value")
221-
221+
222222
# 缩放动画
223223
current_zoom = self._zoom_factor
224224
anim_zoom = QPropertyAnimation(self, b"zoom_factor")
225225
anim_zoom.setDuration(400)
226226
anim_zoom.setStartValue(current_zoom)
227227
anim_zoom.setEndValue(1.0) # 自适应后的标准缩放值
228-
228+
229229
# 配置动画参数
230230
for anim in [anim_h, anim_v]:
231231
anim.setDuration(400)
@@ -234,19 +234,19 @@ def _create_center_animation(self, target_center: QPointF):
234234
# 计算目标滚动值
235235
view_center = self.mapToScene(self.viewport().rect().center())
236236
delta = target_center - view_center
237-
237+
238238
# 设置动画参数
239239
anim_h.setStartValue(self.horizontalScrollBar().value())
240240
anim_h.setEndValue(self.horizontalScrollBar().value() + delta.x())
241-
241+
242242
anim_v.setStartValue(self.verticalScrollBar().value())
243243
anim_v.setEndValue(self.verticalScrollBar().value() + delta.y())
244-
244+
245245
# 启动动画
246246
anim_zoom.start()
247247
anim_h.start()
248248
anim_v.start()
249-
249+
250250
# 显示指示器
251251
self.center_indicator.setPos(target_center)
252252
self.center_indicator.setVisible(True)

0 commit comments

Comments
 (0)