From 69ac4f3239a34f90960a3b031ee226490c4b9b71 Mon Sep 17 00:00:00 2001 From: duanyh <19517952495@139.com> Date: Sat, 22 Mar 2025 22:02:40 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat=E7=94=A8=E5=9C=BA=E6=99=AF=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0grphvize=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/graphviz_render/README.md | 13 ++ tools/graphviz_render/iamges/image-1.png | Bin 0 -> 15804 bytes tools/graphviz_render/iamges/image.png | Bin 0 -> 53627 bytes tools/graphviz_render/requirements.txt | 3 + tools/graphviz_render/src/main.py | 37 ++++ .../src/pip_operiton/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 225 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 216 bytes .../__pycache__/pip_reader.cpython-312.pyc | Bin 0 -> 1749 bytes .../__pycache__/pip_reader.cpython-38.pyc | Bin 0 -> 1145 bytes .../src/pip_operiton/pip_reader.py | 28 +++ .../graphviz_render/src/transform/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 223 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 221 bytes .../__pycache__/zoomable.cpython-312.pyc | Bin 0 -> 11001 bytes .../__pycache__/zoomable.cpython-38.pyc | Bin 0 -> 7241 bytes .../graphviz_render/src/transform/zoomable.py | 201 ++++++++++++++++++ .../src/transform/zoomable_cpu.py | 81 +++++++ tools/graphviz_render/src/ui/__init__.py | 1 + .../ui/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 215 bytes .../ui/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 206 bytes .../src/ui/__pycache__/viewer.cpython-312.pyc | Bin 0 -> 6945 bytes .../src/ui/__pycache__/viewer.cpython-38.pyc | Bin 0 -> 3526 bytes tools/graphviz_render/src/ui/viewer.py | 138 ++++++++++++ 24 files changed, 504 insertions(+) create mode 100644 tools/graphviz_render/README.md create mode 100644 tools/graphviz_render/iamges/image-1.png create mode 100644 tools/graphviz_render/iamges/image.png create mode 100644 tools/graphviz_render/requirements.txt create mode 100755 tools/graphviz_render/src/main.py create mode 100644 tools/graphviz_render/src/pip_operiton/__init__.py create mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc create mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-38.pyc create mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc create mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc create mode 100644 tools/graphviz_render/src/pip_operiton/pip_reader.py create mode 100644 tools/graphviz_render/src/transform/__init__.py create mode 100644 tools/graphviz_render/src/transform/__pycache__/__init__.cpython-312.pyc create mode 100644 tools/graphviz_render/src/transform/__pycache__/__init__.cpython-38.pyc create mode 100644 tools/graphviz_render/src/transform/__pycache__/zoomable.cpython-312.pyc create mode 100644 tools/graphviz_render/src/transform/__pycache__/zoomable.cpython-38.pyc create mode 100755 tools/graphviz_render/src/transform/zoomable.py create mode 100644 tools/graphviz_render/src/transform/zoomable_cpu.py create mode 100644 tools/graphviz_render/src/ui/__init__.py create mode 100644 tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc create mode 100644 tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc create mode 100644 tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc create mode 100644 tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc create mode 100644 tools/graphviz_render/src/ui/viewer.py diff --git a/tools/graphviz_render/README.md b/tools/graphviz_render/README.md new file mode 100644 index 0000000..5a72dcb --- /dev/null +++ b/tools/graphviz_render/README.md @@ -0,0 +1,13 @@ +第一步:安装python3.12及以上 +第二步:安装依赖 + pip3.12 install -r requirements.txt +第三步:运行工具graphviz_render/ + python3.12 graphviz_render.py /tmp/gviz (备注: /tmp/gviz是管道名,可自定义) + 如果运行报以下错误 + ![alt text](iamges/image.png) + + 请安装: + sudo apt-get install libxcb-cursor0 libxcb-xinerama0 libxcb-randr0 libxcb-util0-dev +第四步:执行监听shell脚本 + ![alt text](iamges/image-1.png) + 示例:while true; do echo '/cc/brain/graph_work; quit' | nc 10.8.11.62 3000 > /tmp/gviz; sleep 1; done \ No newline at end of file diff --git a/tools/graphviz_render/iamges/image-1.png b/tools/graphviz_render/iamges/image-1.png new file mode 100644 index 0000000000000000000000000000000000000000..014867aaf1281bee4b1e510b53dc30cf15dba913 GIT binary patch literal 15804 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 Gp_-qa~ueMp8vK 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 Hu0mjf3L)4p literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/requirements.txt b/tools/graphviz_render/requirements.txt new file mode 100644 index 0000000..56d6afc --- /dev/null +++ b/tools/graphviz_render/requirements.txt @@ -0,0 +1,3 @@ +Pillow==11.1.0 +PyQt6==6.8.1 +PyQt6_sip==13.10.0 diff --git a/tools/graphviz_render/src/main.py b/tools/graphviz_render/src/main.py new file mode 100755 index 0000000..665f081 --- /dev/null +++ b/tools/graphviz_render/src/main.py @@ -0,0 +1,37 @@ +import os +import sys + +from ui.viewer import GraphvizViewer +from PyQt5.QtWidgets import (QApplication) + +def create_pipe(pipe_name): + if os.path.exists(pipe_name) : + os.remove(pipe_name) + os.mkfifo(pipe_name) + +def print_usage(proc_name): + print("This is a real-time rendering tool for Graphviz graphics.") + print("Author: Hevake Lee\n") + print(f"Usage: {proc_name} /some/where/you_pipe_file\n") + +def main(): + if '-h' in sys.argv or '--help' in sys.argv: + print_usage(sys.argv[0]) + sys.exit(0) + + if len(sys.argv) != 2: + print_usage(sys.argv[0]) + sys.exit(1) + + pipe_name = sys.argv[1] + create_pipe(pipe_name) + + app = QApplication(sys.argv) + viewer = GraphvizViewer(pipe_name) + viewer.show() + sys.exit(app.exec()) + + os.remove(pipe_name) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/graphviz_render/src/pip_operiton/__init__.py b/tools/graphviz_render/src/pip_operiton/__init__.py new file mode 100644 index 0000000..8c72da5 --- /dev/null +++ b/tools/graphviz_render/src/pip_operiton/__init__.py @@ -0,0 +1 @@ +from .pip_reader import PipeReader \ No newline at end of file diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b406d0d3ad81f611d3d4aece723352dca1b7e2f GIT binary patch literal 225 zcmX@j%ge<81UFOer7HpH#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r6;%!kUb? zxB@Z@QiD?@89sx|`4ym_k)NBYpHiBbSDB$-P?VpQ znp~ovTu`7}l9XSeUy`4nQ>>p}lvt2amRSWfAP;DsesNK*d(Lo_xt8)P@I26iXQV#FJHBq?mHtnI8EsQk(Q zKYxmsImU6sNDuie7u2tD{41cbCAV`5$t15?!!ypA3=S*_-<-|~AEH#F+N)eDw@ZR7 zLXE;r(~X2Xb0O6f-xMm_D3D!l`U=q5vno6kh)aL6SMb)>EtD?D>Y{H%F$9Iy6)-|A cDyO|QAcQvB3$em}n5`QtyIO6=Z$4!90~c61!2kdN literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51511c503b10163a16fdef54d5c995a100aaffa6 GIT binary patch literal 1749 zcmZt{%T60t^xS#iH;?cRCN>YLaYbxJQ6e>pq85_2QdAleHt}ZUdIm5WdnR{=fT^ii zP?c2Bw5lqgw4#eF60zYIN|)UP6$xh)Rh7DFc2*!IBAcEwwrL2Qk?%d{-t)TW<=;ag zKOnpu`*U;dm*KupfJJ=c zdWB|CU#rg_f(%$Q2phM`-D zQJyl#?q#MGuI2Du35%x_XiG8Be#SO(Edy4c=5FD=v-TRY~Z#o#X9L%I@h!l4F#Zs;E2=@JxR+&wk?m;H?8B3qrMcFs(2iI?kaU z;P<@=VT-Nj&JPA=QpVvJTTMs)Hy3r-wTtHpX=A8?}ivz0rD%%PbUYPd(7T)=Ppv z)cvfveYtlnz7l`b^G9>M1T_t3H+qYw&#d+R*jJ47tmRkotDi4RPdg&TaC9de-wMZf z!WXu}7dEbKhu{0FExZ<33H;LkQ(Y+l9g(%Z)xLcZb(CbNYkZ(D>iZH=r1te*Ei`q$ z00E8GH$Q1RztOeP_@w3HcKxMdebZ9lyTJ0bo%*h=`mWa{8JP~O1bX{TOMe6&e$d+A zDE@lIi}+@v+#mLAws{DT_y!up&9jk#p!i!*M*K)iQ5(1|bqW!js5D^^D}y^>K8!Ip zE4WcqEo#V03?<7p%;~x}f=CA)#6_)-QEUxUC-XX_Vt$%XG?Z{c`4uH=s1rKjK}ESc zkH6hz58@aN-pmi#m*PYAfWb9RL2;dN?;k^%mrCcr kd!_9ARsIf=WZY`Or6e-O_G^Hh+ml*Z=eMmd0XJ9fKOoF>`~Uy| literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ac476a4710fe7040b107368817d9d0a2838be1e GIT binary patch literal 1145 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 literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/transform/zoomable.py b/tools/graphviz_render/src/transform/zoomable.py new file mode 100755 index 0000000..e2a6669 --- /dev/null +++ b/tools/graphviz_render/src/transform/zoomable.py @@ -0,0 +1,201 @@ +from PyQt5.QtCore import pyqtProperty +from PyQt5.QtGui import QPixmap, QWheelEvent, QMouseEvent, QPainter, QColor +from PyQt5.QtCore import Qt, QPointF, QPropertyAnimation, QTimer, QEasingCurve +from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QOpenGLWidget, QGraphicsEllipseItem) + + +class ZoomableGraphicsView(QGraphicsView): + def __init__(self, parent=None): + super().__init__(parent) + + # 初始化视图设置 + self._setup_view() + self._setup_scene() + self._setup_interaction() + self._setup_indicators() + + # 初始化参数 + self._zoom_factor = 1.0 + self.min_zoom = 0.1 + self.max_zoom = 20.0 + self.dragging = False + self.last_mouse_pos = QPointF() + + # 添加属性声明 + @pyqtProperty(float) + def zoom_factor(self): + return self._zoom_factor + + @zoom_factor.setter + def zoom_factor(self, value): + # 计算缩放比例差异 + ratio = value / self._zoom_factor + self._zoom_factor = value + # 应用缩放变换 + self.scale(ratio, ratio) + + def _setup_view(self): + """配置视图参数""" + self.setViewport(QOpenGLWidget()) # OpenGL加速 + self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing) + self.setCacheMode(QGraphicsView.CacheBackground) + self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate) + + def _setup_scene(self): + """配置场景和图像项""" + self.scene = QGraphicsScene(self) + self.scene.setSceneRect(-1e6, -1e6, 2e6, 2e6) # 超大场景范围 + self.setScene(self.scene) + + self.pixmap_item = QGraphicsPixmapItem() + self.pixmap_item.setTransformationMode(Qt.SmoothTransformation) + self.pixmap_item.setShapeMode(QGraphicsPixmapItem.BoundingRectShape) + self.scene.addItem(self.pixmap_item) + + def _setup_interaction(self): + """配置交互参数""" + self.setDragMode(QGraphicsView.NoDrag) + self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) + self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setMouseTracking(True) + + def _setup_indicators(self): + """配置视觉指示器""" + # 居中指示器 + self.center_indicator = QGraphicsEllipseItem(-8, -8, 16, 16) + self.center_indicator.setPen(QColor(255, 0, 0, 150)) + self.center_indicator.setBrush(QColor(255, 0, 0, 50)) + self.center_indicator.setZValue(100) + self.center_indicator.setVisible(False) + self.scene.addItem(self.center_indicator) + + def update_image(self, pixmap: QPixmap): + """更新图像并自适应视图""" + self.pixmap_item.setPixmap(pixmap) + self._center_pixmap(pixmap) + self.fit_to_view() + + def _center_pixmap(self, pixmap: QPixmap): + """居中放置图元""" + self.pixmap_item.setPos(-pixmap.width()/2, -pixmap.height()/2) + self.scene.setSceneRect(-1e6, -1e6, 2e6, 2e6) # 重置场景范围 + + def fit_to_view(self): + """自适应窗口显示""" + if self.pixmap_item.pixmap().isNull(): + return + + # 自动计算场景中的图元边界 + rect = self.pixmap_item.sceneBoundingRect() + self.fitInView(rect, Qt.KeepAspectRatio) + self._zoom_factor = 1.0 + + def wheelEvent(self, event: QWheelEvent): + """滚轮缩放处理""" + zoom_in = event.angleDelta().y() > 0 + factor = 1.25 if zoom_in else 0.8 + new_zoom = self._zoom_factor * factor + + # 应用缩放限制 + if self.min_zoom <= new_zoom <= self.max_zoom: + self.scale(factor, factor) + self._zoom_factor = new_zoom + + def mousePressEvent(self, event: QMouseEvent): + """鼠标按下事件处理""" + # 左键拖拽 + if event.button() == Qt.LeftButton: + self.dragging = True + self.last_mouse_pos = event.pos() + self.setCursor(Qt.ClosedHandCursor) + + super().mousePressEvent(event) + + def mouseDoubleClickEvent(self, event: QMouseEvent): + """鼠标双击事件处理(增强版)""" + if event.button() == Qt.RightButton: + event.accept() + self._fit_and_center_animation() # 改为新的组合动画方法 + return + super().mouseDoubleClickEvent(event) + + def mouseMoveEvent(self, event: QMouseEvent): + """鼠标移动事件处理""" + if self.dragging: + delta = event.pos() - self.last_mouse_pos + self.last_mouse_pos = event.pos() + + # 更新滚动条实现拖拽 + self.horizontalScrollBar().setValue( + self.horizontalScrollBar().value() - delta.x()) + self.verticalScrollBar().setValue( + self.verticalScrollBar().value() - delta.y()) + + super().mouseMoveEvent(event) + + def mouseReleaseEvent(self, event: QMouseEvent): + """鼠标释放事件处理""" + if event.button() == Qt.LeftButton: + self.dragging = False + self.setCursor(Qt.ArrowCursor) + super().mouseReleaseEvent(event) + + def _fit_and_center_animation(self): + """自适应并居中动画组合""" + if self.pixmap_item.pixmap().isNull(): + return + + # 先执行自适应调整 + self.fit_to_view() + + # 获取最终场景中心坐标 + final_center = self.pixmap_item.sceneBoundingRect().center() + + # 创建组合动画 + self._create_center_animation(final_center) + + def _create_center_animation(self, target_center: QPointF): + """创建居中动画序列""" + # 平移动画 + anim_h = QPropertyAnimation(self.horizontalScrollBar(), b"value") + anim_v = QPropertyAnimation(self.verticalScrollBar(), b"value") + + # 缩放动画 + current_zoom = self._zoom_factor + anim_zoom = QPropertyAnimation(self, b"zoom_factor") + anim_zoom.setDuration(5000) + anim_zoom.setStartValue(current_zoom) + anim_zoom.setEndValue(1.0) # 自适应后的标准缩放值 + + # 配置动画参数 + for anim in [anim_h, anim_v]: + anim.setDuration(5000) + anim.setEasingCurve(QEasingCurve.OutQuad) + + # 计算目标滚动值 + view_center = self.mapToScene(self.viewport().rect().center()) + delta = target_center - view_center + + # 设置动画参数 + anim_h.setStartValue(self.horizontalScrollBar().value()) + anim_h.setEndValue(self.horizontalScrollBar().value() + delta.x()) + + anim_v.setStartValue(self.verticalScrollBar().value()) + anim_v.setEndValue(self.verticalScrollBar().value() + delta.y()) + + # 启动动画 + anim_zoom.start() + anim_h.start() + anim_v.start() + + # 显示指示器 + self.center_indicator.setPos(target_center) + self.center_indicator.setVisible(True) + QTimer.singleShot(800, lambda: self.center_indicator.setVisible(False)) + + def resizeEvent(self, event): + """窗口大小变化时保持自适应""" + self.fit_to_view() + super().resizeEvent(event) \ No newline at end of file diff --git a/tools/graphviz_render/src/transform/zoomable_cpu.py b/tools/graphviz_render/src/transform/zoomable_cpu.py new file mode 100644 index 0000000..7f4e214 --- /dev/null +++ b/tools/graphviz_render/src/transform/zoomable_cpu.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +from PyQt5.QtWidgets import QLabel, QApplication, QOpenGLWidget +from PyQt5.QtCore import Qt, QPoint, QTime, QTimer +from PyQt5.QtGui import QPixmap, QPainter, QMouseEvent, QWheelEvent, QImage + +class ZoomableLabel(QLabel): + def __init__(self, parent=None): + super().__init__(parent) + self.zoom_factor = 1.0 + self.min_zoom = 0.1 + self.max_zoom = 10.0 + self.zoom_step = 1.2 + self.panning = False + self.last_pos = None + self.offset = QPoint(0, 0) + self.current_pixmap = None + self.setMouseTracking(True) + self.initial_fit = True + self.last_click_time = 0 + self.last_click_pos = None + self.zoom_cache = {} + self.current_cache_key = None + self.redraw_timer = QTimer(self) + self.redraw_timer.setSingleShot(True) + self.redraw_timer.timeout.connect(self.force_redraw) + self.redraw_pending = False + self.zoom_timer = QTimer(self) + self.zoom_timer.setSingleShot(True) + self.zoom_timer.timeout.connect(self.high_quality_redraw) + + def wheelEvent(self, event: QWheelEvent): + if self.current_pixmap is None: + return + cursor_pos = event.pos() + if event.angleDelta().y() > 0: + self.zoom_at_position(cursor_pos, self.zoom_step) + else: + self.zoom_at_position(cursor_pos, 1.0 / self.zoom_step) + self.zoom_timer.start(500) # 延迟高质量重绘 + + def update_image(self, pixmap: QPixmap, use_fast=False): + if pixmap is None: + return + cache_key = (round(self.zoom_factor, 2), pixmap.size().width(), pixmap.size().height(), use_fast) + if cache_key in self.zoom_cache: + scaled_pixmap = self.zoom_cache[cache_key] + else: + new_width = int(pixmap.width() * self.zoom_factor) + new_height = int(pixmap.height() * self.zoom_factor) + transformation = Qt.FastTransformation if use_fast else Qt.SmoothTransformation + scaled_pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, transformation) + self.zoom_cache[cache_key] = scaled_pixmap + if len(self.zoom_cache) > 10: + self.zoom_cache.pop(next(iter(self.zoom_cache))) + result_pixmap = QPixmap(self.size()) + result_pixmap.fill(Qt.transparent) + painter = QPainter(result_pixmap) + x = (self.width() - scaled_pixmap.width()) // 2 + self.offset.x() + y = (self.height() - scaled_pixmap.height()) // 2 + self.offset.y() + painter.drawPixmap(x, y, scaled_pixmap) + painter.end() + super().setPixmap(result_pixmap) + + def high_quality_redraw(self): + if self.current_pixmap: + self.update_image(self.current_pixmap, use_fast=False) + + def mouseMoveEvent(self, event: QMouseEvent): + if self.panning and self.last_pos is not None: + delta = event.pos() - self.last_pos + self.offset += delta + self.last_pos = event.pos() + if not self.redraw_pending: + self.redraw_pending = True + self.redraw_timer.start(30) + + def force_redraw(self): + if self.redraw_pending and self.current_pixmap: + self.update_image(self.current_pixmap, use_fast=True) + self.redraw_pending = False \ No newline at end of file diff --git a/tools/graphviz_render/src/ui/__init__.py b/tools/graphviz_render/src/ui/__init__.py new file mode 100644 index 0000000..650d985 --- /dev/null +++ b/tools/graphviz_render/src/ui/__init__.py @@ -0,0 +1 @@ +from .viewer import GraphvizViewer \ No newline at end of file diff --git a/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc b/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e782f7e30bb6cc7e99a1e1d25b76ffebb8d55baa GIT binary patch literal 215 zcmX@j%ge<81l=k3(v^VpV-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9i2>VNJ$c zeC|bw1sP?TRbiQ_<*7w}noPIY%D~JbW}u3d44*;f{BqOJ$j?pHPbp2ztIW_ZD9X=D zO)k+-E-26~Ny@L#FUil(Db`Pi8WmrZnwOGVq+eW=tY4a`A0MBYmst`YuUAm{i^C>2 fKczG$)vkyGXe`JL#URE9W=2NFdkjiNY(NeGD9Aah literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc b/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aec73111fb4abe61a5a099efca2bfde3f2f44301 GIT binary patch literal 206 zcmWIL<>g`kg6@=i=}JKQF^Gc(44TX@8G*u@ zjJNpQixLYm$}+3MGE>V_i~Ka1Zn2etnMEKIRx%W^04Xr>%S}HcKQ~oBr8F_GGDE+h zC_gJTxkNv?pg^}IDZfI$BtJi=SU(+VUVKq%UP@|_esNKt>Mu3~ literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc b/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b598ff49c143169705ba1a2b9ed33a71be48100c GIT binary patch literal 6945 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 literal 0 HcmV?d00001 diff --git a/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc b/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e70ba420ad34a2aa2da1bf8e880fae3215370b7 GIT binary patch literal 3526 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 u<5 Date: Sat, 22 Mar 2025 22:06:42 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/graphviz_render/giv.txt | 401 ++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 tools/graphviz_render/giv.txt diff --git a/tools/graphviz_render/giv.txt b/tools/graphviz_render/giv.txt new file mode 100644 index 0000000..7ce047b --- /dev/null +++ b/tools/graphviz_render/giv.txt @@ -0,0 +1,401 @@ +digraph { +rankdir=LR; +node [shape="rect",style="filled"]; +action_24 [fillcolor="orange",label="24.Clean\nclean_time = 0\ncollect_enable = true\ndidnot_continue_work_after_leave_ground = false\ndrying_enable = true\nhas_it_been_first_weeting_fill_water = false\nis_do_stop_clean = false\nis_fault_pause = false\nis_need_back_wash = false\nis_need_drying = false\nis_need_sync_task_desc = false\nis_only_discharge_dust = false\nis_pause_task_timeout = false\nis_quiet_mode = false\nis_relocation_fail = false\nis_timeout_recharge = false\nis_user_click_recharge = false\nlast_collect_dust_clean_timepoint = 0\npause_task_timeout_times = 3\nremain_clean_area = 0.0"]; +action_25 [fillcolor="green",label="25.BackupMapBag"]; +action_24->action_25 [label="0.prework"]; +action_26 [fillcolor="white",label="26.FirstWettingAndFillWaterAction\nis_allow_interrupt = false"]; +action_27 [fillcolor="white",label="27.BackWashOnly\nis_allow_interrupt = false\nis_self_clean_finish = false\nis_succ_all_action = true\nis_water_empty_during_self_clean = false\nself_clean_time = 10"]; +action_28 [fillcolor="white",label="28.SetPowerToWork"]; +action_27->action_28 [label="0.conncet_bt_action"]; +action_69 [fillcolor="white",label="69.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; +action_71 [fillcolor="white",label="71.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_72 [fillcolor="white",label="72.Prepare"]; +action_78 [fillcolor="white",label="78.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_79 [fillcolor="white",label="79.Function\n[clean fault]"]; +action_78->action_79 [label="[0]"]; +action_73 [fillcolor="white",label="73.Check"]; +action_74 [fillcolor="white",label="74.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_75 [fillcolor="white",label="75.Function"]; +action_74->action_75 [label="[0]"]; +action_76 [fillcolor="white",label="76.SetPowerToWork"]; +action_74->action_76 [label="[1]"]; +action_77 [fillcolor="white",label="77.Function"]; +action_74->action_77 [label="[2]"]; +action_73->action_74; +action_78->action_73 [label="[1]"]; +action_81 [fillcolor="white",label="81.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_80 [fillcolor="white",label="80.WaitLidarReady"]; +action_81->action_80 [label="[0]"]; +action_82 [fillcolor="white",label="82.Sleep"]; +action_81->action_82 [label="[1]"]; +action_78->action_81 [label="[2]"]; +action_83 [fillcolor="white",label="83.Function"]; +action_78->action_83 [label="[3]"]; +action_84 [fillcolor="white",label="84.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_78->action_84 [label="[4]"]; +action_72->action_78; +action_71->action_72 [label="[0]"]; +action_69->action_71 [label="1.exit_base_seq"]; +action_70 [fillcolor="white",label="70.SetPowerToWork"]; +action_69->action_70 [label="3.set_power_mode"]; +action_125 [fillcolor="white",label="125.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_126 [fillcolor="white",label="126.SetMucPowerManageMode"]; +action_125->action_126 [label="[0]"]; +action_127 [fillcolor="white",label="127.Open22VoltChannel"]; +action_125->action_127 [label="[1]"]; +action_69->action_125 [label="4.open_22v_channel"]; +action_129 [fillcolor="white",label="129.WaitBtUploadOnBase"]; +action_69->action_129 [label="5.pending_bt_upload_on_base"]; +action_128 [fillcolor="white",label="128.SetMucPowerManageMode"]; +action_69->action_128 [label="6.close_mcu_charge_mode"]; +action_131 [fillcolor="white",label="131.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; +action_69->action_131 [label="7.do_dill_water"]; +action_130 [fillcolor="white",label="130.DischargeSewage"]; +action_69->action_130 [label="8.discharge_sewage"]; +action_27->action_69 [label="3.change_water"]; +action_132 [fillcolor="white",label="132.DoSelfClean"]; +action_133 [fillcolor="white",label="133.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_134 [fillcolor="white",label="134.LoopIf"]; +action_135 [fillcolor="white",label="135.Function"]; +action_134->action_135 [label="0.if"]; +action_136 [fillcolor="white",label="136.Sleep"]; +action_134->action_136 [label="1.exec"]; +action_133->action_134 [label="[0]"]; +action_132->action_133; +action_27->action_132 [label="4.do_self_clean"]; +action_137 [fillcolor="white",label="137.RinseFilter"]; +action_138 [fillcolor="white",label="138.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_139 [fillcolor="white",label="139.Function"]; +action_138->action_139 [label="[0]"]; +action_140 [fillcolor="white",label="140.SendOpenBaseValveCmdAction"]; +action_138->action_140 [label="[1]"]; +action_141 [fillcolor="white",label="141.Sleep"]; +action_138->action_141 [label="[2]"]; +action_142 [fillcolor="white",label="142.SendDischargeSewageCmd"]; +action_138->action_142 [label="[3]"]; +action_143 [fillcolor="white",label="143.Sleep"]; +action_138->action_143 [label="[4]"]; +action_137->action_138; +action_27->action_137 [label="5.rinse_filter"]; +action_26->action_27 [label="1.fully_wetting_action"]; +action_144 [fillcolor="white",label="144.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; +action_26->action_144 [label="2.host_fill_water_action"]; +action_24->action_26 [label="1.prework"]; +action_145 [fillcolor="lightblue",label="145.Clean\nhas_it_been_wetted = false\nis_clean_with_explore = false\nlast_state_before_pause = 1"]; +action_191 [fillcolor="white",label="191.IfElse"]; +action_190 [fillcolor="white",label="190.Function\n[is_switch_to_clean_with_explore]"]; +action_191->action_190 [label="0.if"]; +action_176 [fillcolor="white",label="176.Prepare"]; +action_182 [fillcolor="white",label="182.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_183 [fillcolor="white",label="183.Function\n[clean fault]"]; +action_182->action_183 [label="[0]"]; +action_177 [fillcolor="white",label="177.Check"]; +action_178 [fillcolor="white",label="178.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_179 [fillcolor="white",label="179.Function"]; +action_178->action_179 [label="[0]"]; +action_180 [fillcolor="white",label="180.SetPowerToWork"]; +action_178->action_180 [label="[1]"]; +action_181 [fillcolor="white",label="181.Function"]; +action_178->action_181 [label="[2]"]; +action_177->action_178; +action_182->action_177 [label="[1]"]; +action_185 [fillcolor="white",label="185.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_184 [fillcolor="white",label="184.WaitLidarReady"]; +action_185->action_184 [label="[0]"]; +action_186 [fillcolor="white",label="186.Sleep"]; +action_185->action_186 [label="[1]"]; +action_182->action_185 [label="[2]"]; +action_187 [fillcolor="white",label="187.Function"]; +action_182->action_187 [label="[3]"]; +action_188 [fillcolor="white",label="188.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_182->action_188 [label="[4]"]; +action_189 [fillcolor="white",label="189.StartSlam\nmode = 2\nrelocate_state = 0"]; +action_182->action_189 [label="[5]"]; +action_176->action_182; +action_191->action_176 [label="1.then"]; +action_146 [fillcolor="white",label="146.IfElse\n[prepare_clean_by_is_need_mop]"]; +action_147 [fillcolor="white",label="147.Function"]; +action_146->action_147 [label="0.if"]; +action_162 [fillcolor="white",label="162.Prepare"]; +action_168 [fillcolor="white",label="168.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_169 [fillcolor="white",label="169.Function\n[clean fault]"]; +action_168->action_169 [label="[0]"]; +action_163 [fillcolor="white",label="163.Check"]; +action_164 [fillcolor="white",label="164.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_165 [fillcolor="white",label="165.Function"]; +action_164->action_165 [label="[0]"]; +action_166 [fillcolor="white",label="166.SetPowerToWork"]; +action_164->action_166 [label="[1]"]; +action_167 [fillcolor="white",label="167.Function"]; +action_164->action_167 [label="[2]"]; +action_163->action_164; +action_168->action_163 [label="[1]"]; +action_171 [fillcolor="white",label="171.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_170 [fillcolor="white",label="170.WaitLidarReady"]; +action_171->action_170 [label="[0]"]; +action_172 [fillcolor="white",label="172.Sleep"]; +action_171->action_172 [label="[1]"]; +action_168->action_171 [label="[2]"]; +action_173 [fillcolor="white",label="173.Function"]; +action_168->action_173 [label="[3]"]; +action_174 [fillcolor="white",label="174.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_168->action_174 [label="[4]"]; +action_175 [fillcolor="white",label="175.StartSlam\nmode = 2\nrelocate_state = 0"]; +action_168->action_175 [label="[5]"]; +action_162->action_168; +action_146->action_162 [label="1.then"]; +action_148 [fillcolor="white",label="148.Prepare"]; +action_154 [fillcolor="white",label="154.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_155 [fillcolor="white",label="155.Function\n[clean fault]"]; +action_154->action_155 [label="[0]"]; +action_149 [fillcolor="white",label="149.Check"]; +action_150 [fillcolor="white",label="150.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_151 [fillcolor="white",label="151.Function"]; +action_150->action_151 [label="[0]"]; +action_152 [fillcolor="white",label="152.SetPowerToWork"]; +action_150->action_152 [label="[1]"]; +action_153 [fillcolor="white",label="153.Function"]; +action_150->action_153 [label="[2]"]; +action_149->action_150; +action_154->action_149 [label="[1]"]; +action_157 [fillcolor="white",label="157.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_156 [fillcolor="white",label="156.WaitLidarReady"]; +action_157->action_156 [label="[0]"]; +action_158 [fillcolor="white",label="158.Sleep"]; +action_157->action_158 [label="[1]"]; +action_154->action_157 [label="[2]"]; +action_159 [fillcolor="white",label="159.Function"]; +action_154->action_159 [label="[3]"]; +action_160 [fillcolor="white",label="160.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_154->action_160 [label="[4]"]; +action_161 [fillcolor="white",label="161.StartSlam\nmode = 2\nrelocate_state = 0"]; +action_154->action_161 [label="[5]"]; +action_148->action_154; +action_146->action_148 [label="2.else"]; +action_191->action_146 [label="2.else"]; +action_145->action_191 [label="1.prepare"]; +action_200 [fillcolor="white",label="200.RollerWetting"]; +action_145->action_200 [label="2.wetting"]; +action_198 [fillcolor="white",label="198.DoClean"]; +action_199 [fillcolor="white",label="199.WaitTeFinish"]; +action_198->action_199; +action_145->action_198 [label="3.do_clean"]; +action_192 [fillcolor="white",label="192.CleanTaskRelocateFail"]; +action_145->action_192 [label="4.relocate_fail"]; +action_24->action_145 [label="2.clean"]; +action_241 [fillcolor="orange",label="241.BackBaseChangeWater"]; +action_282 [fillcolor="orange",label="282.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; +action_284 [fillcolor="white",label="284.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_285 [fillcolor="white",label="285.Prepare"]; +action_291 [fillcolor="white",label="291.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_292 [fillcolor="white",label="292.Function\n[clean fault]"]; +action_291->action_292 [label="[0]"]; +action_286 [fillcolor="white",label="286.Check"]; +action_287 [fillcolor="white",label="287.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_288 [fillcolor="white",label="288.Function"]; +action_287->action_288 [label="[0]"]; +action_289 [fillcolor="white",label="289.SetPowerToWork"]; +action_287->action_289 [label="[1]"]; +action_290 [fillcolor="white",label="290.Function"]; +action_287->action_290 [label="[2]"]; +action_286->action_287; +action_291->action_286 [label="[1]"]; +action_294 [fillcolor="white",label="294.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_293 [fillcolor="white",label="293.WaitLidarReady"]; +action_294->action_293 [label="[0]"]; +action_295 [fillcolor="white",label="295.Sleep"]; +action_294->action_295 [label="[1]"]; +action_291->action_294 [label="[2]"]; +action_296 [fillcolor="white",label="296.Function"]; +action_291->action_296 [label="[3]"]; +action_297 [fillcolor="white",label="297.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_291->action_297 [label="[4]"]; +action_285->action_291; +action_284->action_285 [label="[0]"]; +action_282->action_284 [label="1.exit_base_seq"]; +action_283 [fillcolor="green",label="283.SetPowerToWork"]; +action_282->action_283 [label="3.set_power_mode"]; +action_338 [fillcolor="white",label="338.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_339 [fillcolor="white",label="339.SetMucPowerManageMode"]; +action_338->action_339 [label="[0]"]; +action_340 [fillcolor="white",label="340.Open22VoltChannel"]; +action_338->action_340 [label="[1]"]; +action_282->action_338 [label="4.open_22v_channel"]; +action_342 [fillcolor="white",label="342.WaitBtUploadOnBase"]; +action_282->action_342 [label="5.pending_bt_upload_on_base"]; +action_341 [fillcolor="green",label="341.SetMucPowerManageMode"]; +action_282->action_341 [label="6.close_mcu_charge_mode"]; +action_344 [fillcolor="orange",label="344.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 12"]; +action_491 [fillcolor="orange",label="491.Sequence\nindex = 2\nmode = \"AnyFail\""]; +action_515 [fillcolor="green",label="515.ConnectBt"]; +action_491->action_515 [label="[0]"]; +action_516 [fillcolor="green",label="516.WaitCombinedBaseStatusSync"]; +action_491->action_516 [label="[1]"]; +action_494 [fillcolor="orange",label="494.IfElse"]; +action_495 [fillcolor="green",label="495.Function\n[check_is_need_send_water_base_fill_water_cmd]"]; +action_494->action_495 [label="0.if"]; +action_497 [fillcolor="orange",label="497.Sequence\nindex = 3\nmode = \"AnyFail\""]; +action_493 [fillcolor="green",label="493.Function\n[update_water_base_fill_water_seconds_action]"]; +action_497->action_493 [label="[0]"]; +action_492 [fillcolor="green",label="492.SendOpenBaseValveCmdAction"]; +action_497->action_492 [label="[1]"]; +action_496 [fillcolor="green",label="496.Sleep"]; +action_497->action_496 [label="[2]"]; +action_498 [fillcolor="orange",label="498.Sequence\nindex = 2\nmode = \"AnyFail\""]; +action_499 [fillcolor="green",label="499.SetMucPowerManageMode"]; +action_498->action_499 [label="[0]"]; +action_500 [fillcolor="green",label="500.SendFillWaterWithCleaningCiquidCmd"]; +action_498->action_500 [label="[1]"]; +action_501 [fillcolor="orange",label="501.Sleep"]; +action_498->action_501 [label="[2]"]; +action_502 [fillcolor="white",label="502.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_503 [fillcolor="white",label="503.Function"]; +action_502->action_503 [label="[0]"]; +action_504 [fillcolor="white",label="504.Sleep"]; +action_502->action_504 [label="[1]"]; +action_505 [fillcolor="white",label="505.Function"]; +action_502->action_505 [label="[2]"]; +action_498->action_502 [label="[3]"]; +action_497->action_498 [label="[3]"]; +action_494->action_497 [label="1.then"]; +action_506 [fillcolor="white",label="506.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_507 [fillcolor="white",label="507.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_508 [fillcolor="white",label="508.SetMucPowerManageMode"]; +action_507->action_508 [label="[0]"]; +action_509 [fillcolor="white",label="509.SendFillWaterWithCleaningCiquidCmd"]; +action_507->action_509 [label="[1]"]; +action_510 [fillcolor="white",label="510.Sleep"]; +action_507->action_510 [label="[2]"]; +action_511 [fillcolor="white",label="511.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_512 [fillcolor="white",label="512.Function"]; +action_511->action_512 [label="[0]"]; +action_513 [fillcolor="white",label="513.Sleep"]; +action_511->action_513 [label="[1]"]; +action_514 [fillcolor="white",label="514.Function"]; +action_511->action_514 [label="[2]"]; +action_507->action_511 [label="[3]"]; +action_506->action_507 [label="[0]"]; +action_494->action_506 [label="2.else"]; +action_491->action_494 [label="[2]"]; +action_344->action_491 [label="1.filling_water"]; +action_485 [fillcolor="white",label="485.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_486 [fillcolor="white",label="486.SetMucPowerManageMode"]; +action_485->action_486 [label="[0]"]; +action_487 [fillcolor="white",label="487.SendCloseBaseValveCmd"]; +action_485->action_487 [label="[1]"]; +action_488 [fillcolor="white",label="488.SendCloseBaseValveCmd"]; +action_485->action_488 [label="[2]"]; +action_489 [fillcolor="white",label="489.Function"]; +action_485->action_489 [label="[3]"]; +action_490 [fillcolor="white",label="490.Sleep"]; +action_485->action_490 [label="[4]"]; +action_344->action_485 [label="2.fill_water_fulled"]; +action_282->action_344 [label="7.do_dill_water"]; +action_343 [fillcolor="green",label="343.DischargeSewage"]; +action_282->action_343 [label="8.discharge_sewage"]; +action_241->action_282 [label="2.change_water"]; +action_24->action_241 [label="3.change_water"]; +action_345 [fillcolor="white",label="345.BackWashExtend\nis_allow_interrupt = false\nis_succ_all_action = true"]; +action_346 [fillcolor="white",label="346.BackWashOnly\nis_allow_interrupt = false\nis_self_clean_finish = false\nis_succ_all_action = true\nis_water_empty_during_self_clean = false\nself_clean_time = 60"]; +action_347 [fillcolor="white",label="347.SetPowerToWork"]; +action_346->action_347 [label="0.conncet_bt_action"]; +action_388 [fillcolor="white",label="388.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; +action_390 [fillcolor="white",label="390.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_391 [fillcolor="white",label="391.Prepare"]; +action_397 [fillcolor="white",label="397.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_398 [fillcolor="white",label="398.Function\n[clean fault]"]; +action_397->action_398 [label="[0]"]; +action_392 [fillcolor="white",label="392.Check"]; +action_393 [fillcolor="white",label="393.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_394 [fillcolor="white",label="394.Function"]; +action_393->action_394 [label="[0]"]; +action_395 [fillcolor="white",label="395.SetPowerToWork"]; +action_393->action_395 [label="[1]"]; +action_396 [fillcolor="white",label="396.Function"]; +action_393->action_396 [label="[2]"]; +action_392->action_393; +action_397->action_392 [label="[1]"]; +action_400 [fillcolor="white",label="400.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; +action_399 [fillcolor="white",label="399.WaitLidarReady"]; +action_400->action_399 [label="[0]"]; +action_401 [fillcolor="white",label="401.Sleep"]; +action_400->action_401 [label="[1]"]; +action_397->action_400 [label="[2]"]; +action_402 [fillcolor="white",label="402.Function"]; +action_397->action_402 [label="[3]"]; +action_403 [fillcolor="white",label="403.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; +action_397->action_403 [label="[4]"]; +action_391->action_397; +action_390->action_391 [label="[0]"]; +action_388->action_390 [label="1.exit_base_seq"]; +action_389 [fillcolor="white",label="389.SetPowerToWork"]; +action_388->action_389 [label="3.set_power_mode"]; +action_444 [fillcolor="white",label="444.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_445 [fillcolor="white",label="445.SetMucPowerManageMode"]; +action_444->action_445 [label="[0]"]; +action_446 [fillcolor="white",label="446.Open22VoltChannel"]; +action_444->action_446 [label="[1]"]; +action_388->action_444 [label="4.open_22v_channel"]; +action_448 [fillcolor="white",label="448.WaitBtUploadOnBase"]; +action_388->action_448 [label="5.pending_bt_upload_on_base"]; +action_447 [fillcolor="white",label="447.SetMucPowerManageMode"]; +action_388->action_447 [label="6.close_mcu_charge_mode"]; +action_450 [fillcolor="white",label="450.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; +action_388->action_450 [label="7.do_dill_water"]; +action_449 [fillcolor="white",label="449.DischargeSewage"]; +action_388->action_449 [label="8.discharge_sewage"]; +action_346->action_388 [label="3.change_water"]; +action_451 [fillcolor="white",label="451.DoSelfClean"]; +action_452 [fillcolor="white",label="452.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_453 [fillcolor="white",label="453.LoopIf"]; +action_454 [fillcolor="white",label="454.Function"]; +action_453->action_454 [label="0.if"]; +action_455 [fillcolor="white",label="455.Sleep"]; +action_453->action_455 [label="1.exec"]; +action_452->action_453 [label="[0]"]; +action_451->action_452; +action_346->action_451 [label="4.do_self_clean"]; +action_456 [fillcolor="white",label="456.RinseFilter"]; +action_457 [fillcolor="white",label="457.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_458 [fillcolor="white",label="458.Function"]; +action_457->action_458 [label="[0]"]; +action_459 [fillcolor="white",label="459.SendOpenBaseValveCmdAction"]; +action_457->action_459 [label="[1]"]; +action_460 [fillcolor="white",label="460.Sleep"]; +action_457->action_460 [label="[2]"]; +action_461 [fillcolor="white",label="461.SendDischargeSewageCmd"]; +action_457->action_461 [label="[3]"]; +action_462 [fillcolor="white",label="462.Sleep"]; +action_457->action_462 [label="[4]"]; +action_456->action_457; +action_346->action_456 [label="5.rinse_filter"]; +action_345->action_346 [label="1.back_wash_only_"]; +action_463 [fillcolor="white",label="463.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_464 [fillcolor="white",label="464.SetMucPowerManageMode"]; +action_463->action_464 [label="[0]"]; +action_465 [fillcolor="white",label="465.Open22VoltChannel"]; +action_463->action_465 [label="[1]"]; +action_466 [fillcolor="white",label="466.Sleep"]; +action_463->action_466 [label="[2]"]; +action_345->action_463 [label="2.discharge_to_water_base"]; +action_24->action_345 [label="4.back_wash"]; +action_467 [fillcolor="white",label="467.SaveMapWaitLabelChange\nis_auto_save = false"]; +action_468 [fillcolor="white",label="468.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_469 [fillcolor="white",label="469.SaveMap"]; +action_468->action_469 [label="[0]"]; +action_474 [fillcolor="white",label="474.IfElse"]; +action_470 [fillcolor="white",label="470.Function"]; +action_474->action_470 [label="0.if"]; +action_471 [fillcolor="white",label="471.Sequence\nindex = 0\nmode = \"AnyFail\""]; +action_472 [fillcolor="white",label="472.Function"]; +action_471->action_472 [label="[0]"]; +action_473 [fillcolor="white",label="473.WaitLabelChange"]; +action_471->action_473 [label="[1]"]; +action_474->action_471 [label="1.then"]; +action_468->action_474 [label="[1]"]; +action_467->action_468; +action_24->action_467 [label="6.save_map"]; +} + + -- Gitee From 3f3f2496293265a76aeade4bc1f6933498aa7938 Mon Sep 17 00:00:00 2001 From: duanyh <19517952495@139.com> Date: Mon, 24 Mar 2025 11:28:12 +0800 Subject: [PATCH 3/8] =?UTF-8?q?opt:=E4=BC=98=E5=8C=96=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/graphviz_render/.gitignore | 1 + tools/graphviz_render/README.md | 17 +++++++++------- .../{iamges => images}/image-1.png | Bin .../{iamges => images}/image.png | Bin tools/graphviz_render/requirements.txt | 3 --- .../__pycache__/__init__.cpython-312.pyc | Bin 225 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 216 -> 0 bytes .../__pycache__/pip_reader.cpython-312.pyc | Bin 1749 -> 0 bytes .../__pycache__/pip_reader.cpython-38.pyc | Bin 1145 -> 0 bytes tools/graphviz_render/src/requirements.txt | 4 ++++ .../__pycache__/__init__.cpython-312.pyc | Bin 223 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 221 -> 0 bytes .../__pycache__/zoomable.cpython-312.pyc | Bin 11001 -> 0 bytes .../__pycache__/zoomable.cpython-38.pyc | Bin 7241 -> 0 bytes .../graphviz_render/src/transform/zoomable.py | 19 ++++++++++++++---- .../ui/__pycache__/__init__.cpython-312.pyc | Bin 215 -> 0 bytes .../ui/__pycache__/__init__.cpython-38.pyc | Bin 206 -> 0 bytes .../src/ui/__pycache__/viewer.cpython-312.pyc | Bin 6945 -> 0 bytes .../src/ui/__pycache__/viewer.cpython-38.pyc | Bin 3526 -> 0 bytes 19 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 tools/graphviz_render/.gitignore rename tools/graphviz_render/{iamges => images}/image-1.png (100%) rename tools/graphviz_render/{iamges => images}/image.png (100%) delete mode 100644 tools/graphviz_render/requirements.txt delete mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-38.pyc delete mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc create mode 100644 tools/graphviz_render/src/requirements.txt delete mode 100644 tools/graphviz_render/src/transform/__pycache__/__init__.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/transform/__pycache__/__init__.cpython-38.pyc delete mode 100644 tools/graphviz_render/src/transform/__pycache__/zoomable.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/transform/__pycache__/zoomable.cpython-38.pyc delete mode 100644 tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc delete mode 100644 tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc delete mode 100644 tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc diff --git a/tools/graphviz_render/.gitignore b/tools/graphviz_render/.gitignore new file mode 100644 index 0000000..bee8a64 --- /dev/null +++ b/tools/graphviz_render/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/tools/graphviz_render/README.md b/tools/graphviz_render/README.md index 5a72dcb..f3453cf 100644 --- a/tools/graphviz_render/README.md +++ b/tools/graphviz_render/README.md @@ -1,13 +1,16 @@ 第一步:安装python3.12及以上 + 第二步:安装依赖 - pip3.12 install -r requirements.txt -第三步:运行工具graphviz_render/ - python3.12 graphviz_render.py /tmp/gviz (备注: /tmp/gviz是管道名,可自定义) + `pip3 install -r requirements.txt` + +第三步:运行工具`graphviz_render/src/` + `python3 main.py /tmp/gviz` (备注: /tmp/gviz是管道名,可自定义) 如果运行报以下错误 - ![alt text](iamges/image.png) + ![alt text](images/image.png) 请安装: - sudo apt-get install libxcb-cursor0 libxcb-xinerama0 libxcb-randr0 libxcb-util0-dev + `sudo apt-get install libxcb-cursor0 libxcb-xinerama0 libxcb-randr0 libxcb-util0-dev` + 第四步:执行监听shell脚本 - ![alt text](iamges/image-1.png) - 示例:while true; do echo '/cc/brain/graph_work; quit' | nc 10.8.11.62 3000 > /tmp/gviz; sleep 1; done \ No newline at end of file + ![alt text](images/image-1.png) + 示例:`while true; do echo '/cc/brain/graph_work; quit' | nc 10.8.11.62 3000 > /tmp/gviz; sleep 1; done` diff --git a/tools/graphviz_render/iamges/image-1.png b/tools/graphviz_render/images/image-1.png similarity index 100% rename from tools/graphviz_render/iamges/image-1.png rename to tools/graphviz_render/images/image-1.png diff --git a/tools/graphviz_render/iamges/image.png b/tools/graphviz_render/images/image.png similarity index 100% rename from tools/graphviz_render/iamges/image.png rename to tools/graphviz_render/images/image.png diff --git a/tools/graphviz_render/requirements.txt b/tools/graphviz_render/requirements.txt deleted file mode 100644 index 56d6afc..0000000 --- a/tools/graphviz_render/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -Pillow==11.1.0 -PyQt6==6.8.1 -PyQt6_sip==13.10.0 diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 3b406d0d3ad81f611d3d4aece723352dca1b7e2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmX@j%ge<81UFOer7HpH#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r6;%!kUb? zxB@Z@QiD?@89sx|`4ym_k)NBYpHiBbSDB$-P?VpQ znp~ovTu`7}l9XSeUy`4nQ>>p}lvt2amRSWfAP;DsesNK*d(Lo_xt8)P@I26iXQV#FJHBq?mHtnI8EsQk(Q zKYxmsImU6sNDuie7u2tD{41cbCAV`5$t15?!!ypA3=S*_-<-|~AEH#F+N)eDw@ZR7 zLXE;r(~X2Xb0O6f-xMm_D3D!l`U=q5vno6kh)aL6SMb)>EtD?D>Y{H%F$9Iy6)-|A cDyO|QAcQvB3$em}n5`QtyIO6=Z$4!90~c61!2kdN diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-312.pyc deleted file mode 100644 index 51511c503b10163a16fdef54d5c995a100aaffa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1749 zcmZt{%T60t^xS#iH;?cRCN>YLaYbxJQ6e>pq85_2QdAleHt}ZUdIm5WdnR{=fT^ii zP?c2Bw5lqgw4#eF60zYIN|)UP6$xh)Rh7DFc2*!IBAcEwwrL2Qk?%d{-t)TW<=;ag zKOnpu`*U;dm*KupfJJ=c zdWB|CU#rg_f(%$Q2phM`-D zQJyl#?q#MGuI2Du35%x_XiG8Be#SO(Edy4c=5FD=v-TRY~Z#o#X9L%I@h!l4F#Zs;E2=@JxR+&wk?m;H?8B3qrMcFs(2iI?kaU z;P<@=VT-Nj&JPA=QpVvJTTMs)Hy3r-wTtHpX=A8?}ivz0rD%%PbUYPd(7T)=Ppv z)cvfveYtlnz7l`b^G9>M1T_t3H+qYw&#d+R*jJ47tmRkotDi4RPdg&TaC9de-wMZf z!WXu}7dEbKhu{0FExZ<33H;LkQ(Y+l9g(%Z)xLcZb(CbNYkZ(D>iZH=r1te*Ei`q$ z00E8GH$Q1RztOeP_@w3HcKxMdebZ9lyTJ0bo%*h=`mWa{8JP~O1bX{TOMe6&e$d+A zDE@lIi}+@v+#mLAws{DT_y!up&9jk#p!i!*M*K)iQ5(1|bqW!js5D^^D}y^>K8!Ip zE4WcqEo#V03?<7p%;~x}f=CA)#6_)-QEUxUC-XX_Vt$%XG?Z{c`4uH=s1rKjK}ESc zkH6hz58@aN-pmi#m*PYAfWb9RL2;dN?;k^%mrCcr kd!_9ARsIf=WZY`Or6e-O_G^Hh+ml*Z=eMmd0XJ9fKOoF>`~Uy| diff --git a/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc b/tools/graphviz_render/src/pip_operiton/__pycache__/pip_reader.cpython-38.pyc deleted file mode 100644 index 5ac476a4710fe7040b107368817d9d0a2838be1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1145 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 diff --git a/tools/graphviz_render/src/transform/zoomable.py b/tools/graphviz_render/src/transform/zoomable.py index e2a6669..769252c 100755 --- a/tools/graphviz_render/src/transform/zoomable.py +++ b/tools/graphviz_render/src/transform/zoomable.py @@ -106,7 +106,7 @@ class ZoomableGraphicsView(QGraphicsView): def mousePressEvent(self, event: QMouseEvent): """鼠标按下事件处理""" # 左键拖拽 - if event.button() == Qt.LeftButton: + if event.button() in (Qt.RightButton, Qt.LeftButton): self.dragging = True self.last_mouse_pos = event.pos() self.setCursor(Qt.ClosedHandCursor) @@ -119,6 +119,17 @@ class ZoomableGraphicsView(QGraphicsView): event.accept() self._fit_and_center_animation() # 改为新的组合动画方法 return + elif event.button() == Qt.LeftButton: + event.accept() + factor = 2 + new_zoom = self._zoom_factor * factor + + # 应用缩放限制 + if self.min_zoom <= new_zoom <= self.max_zoom: + self.scale(factor, factor) + self._zoom_factor = new_zoom + return + super().mouseDoubleClickEvent(event) def mouseMoveEvent(self, event: QMouseEvent): @@ -137,7 +148,7 @@ class ZoomableGraphicsView(QGraphicsView): def mouseReleaseEvent(self, event: QMouseEvent): """鼠标释放事件处理""" - if event.button() == Qt.LeftButton: + if event.button() in (Qt.RightButton, Qt.LeftButton): self.dragging = False self.setCursor(Qt.ArrowCursor) super().mouseReleaseEvent(event) @@ -165,13 +176,13 @@ class ZoomableGraphicsView(QGraphicsView): # 缩放动画 current_zoom = self._zoom_factor anim_zoom = QPropertyAnimation(self, b"zoom_factor") - anim_zoom.setDuration(5000) + anim_zoom.setDuration(400) anim_zoom.setStartValue(current_zoom) anim_zoom.setEndValue(1.0) # 自适应后的标准缩放值 # 配置动画参数 for anim in [anim_h, anim_v]: - anim.setDuration(5000) + anim.setDuration(400) anim.setEasingCurve(QEasingCurve.OutQuad) # 计算目标滚动值 diff --git a/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc b/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index e782f7e30bb6cc7e99a1e1d25b76ffebb8d55baa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215 zcmX@j%ge<81l=k3(v^VpV-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9i2>VNJ$c zeC|bw1sP?TRbiQ_<*7w}noPIY%D~JbW}u3d44*;f{BqOJ$j?pHPbp2ztIW_ZD9X=D zO)k+-E-26~Ny@L#FUil(Db`Pi8WmrZnwOGVq+eW=tY4a`A0MBYmst`YuUAm{i^C>2 fKczG$)vkyGXe`JL#URE9W=2NFdkjiNY(NeGD9Aah diff --git a/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc b/tools/graphviz_render/src/ui/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index aec73111fb4abe61a5a099efca2bfde3f2f44301..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmWIL<>g`kg6@=i=}JKQF^Gc(44TX@8G*u@ zjJNpQixLYm$}+3MGE>V_i~Ka1Zn2etnMEKIRx%W^04Xr>%S}HcKQ~oBr8F_GGDE+h zC_gJTxkNv?pg^}IDZfI$BtJi=SU(+VUVKq%UP@|_esNKt>Mu3~ diff --git a/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc b/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-312.pyc deleted file mode 100644 index b598ff49c143169705ba1a2b9ed33a71be48100c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6945 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 diff --git a/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc b/tools/graphviz_render/src/ui/__pycache__/viewer.cpython-38.pyc deleted file mode 100644 index 0e70ba420ad34a2aa2da1bf8e880fae3215370b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3526 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 u<5 Date: Mon, 24 Mar 2025 19:31:00 +0800 Subject: [PATCH 4/8] =?UTF-8?q?opt:=E4=BC=98=E5=8C=96=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/data_processing/__init__.py | 1 + .../src/data_processing/data_processing.py | 114 ++++++++++++++++++ tools/graphviz_render/src/main.py | 4 + .../src/pip_operiton/pip_reader.py | 10 +- tools/graphviz_render/src/signal_handler.py | 14 +++ .../graphviz_render/src/transform/zoomable.py | 30 ++++- tools/graphviz_render/src/ui/viewer.py | 78 ++++-------- 7 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 tools/graphviz_render/src/data_processing/__init__.py create mode 100644 tools/graphviz_render/src/data_processing/data_processing.py create mode 100644 tools/graphviz_render/src/signal_handler.py diff --git a/tools/graphviz_render/src/data_processing/__init__.py b/tools/graphviz_render/src/data_processing/__init__.py new file mode 100644 index 0000000..b735aaa --- /dev/null +++ b/tools/graphviz_render/src/data_processing/__init__.py @@ -0,0 +1 @@ +from .data_processing import DataProcessing \ No newline at end of file diff --git a/tools/graphviz_render/src/data_processing/data_processing.py b/tools/graphviz_render/src/data_processing/data_processing.py new file mode 100644 index 0000000..a03ccb3 --- /dev/null +++ b/tools/graphviz_render/src/data_processing/data_processing.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import threading +from io import BytesIO + +from PIL import Image +from PyQt5.QtGui import QPixmap, QImage +from PyQt5.QtCore import QThread, pyqtSignal + +class DataProcessing(QThread): + data_received = pyqtSignal(QPixmap) + + def __init__(self): + super().__init__() + # 共享数据保护 + self._lock = threading.Lock() + self._cond = threading.Condition(self._lock) # 数据变更条件变量 + + # 共享状态变量 + self._last_dot_data = '' # 最后接收的DOT数据 + self._running = True # 线程运行标志 + self._original_image = None # 原始图像缓存 + self._pending_update = False # 更新标记 + + def run(self): + """线程主循环(条件变量优化版)""" + while True: + with self._cond: + # 等待数据变更 + self._cond.wait_for(lambda: self._pending_update or (not self._running)) + + if not self._running: + break + + # 处理待更新数据 + self._process_update() + self._pending_update = False + + def _process_update(self): + """处理数据更新(受保护方法)""" + current_data = self._last_dot_data + + try: + # 生成图像(耗时操作) + image = self._render_dot(current_data) + if image: + self._original_image = image + self._update_display() + except Exception as e: + print(f"Render error: {e}") + + def _render_dot(self, dot_data: str) -> Image.Image: + """DOT数据渲染方法""" + proc = subprocess.Popen( + ['dot', '-Tpng'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + stdout, stderr = proc.communicate(dot_data.encode()) + + if proc.returncode != 0: + raise RuntimeError(f"Graphviz error: {stderr.decode()}") + + return Image.open(BytesIO(stdout)) + + + def _update_display(self): + """图像显示更新""" + if self._original_image is None: + return + + try: + # 转换为Qt兼容格式 + qimg = self._pil_to_qimage(self._original_image) + pixmap = QPixmap.fromImage(qimg) + + # 发射信号(跨线程安全) + self.data_received.emit(pixmap) + except Exception as e: + print(f"Display error: {e}") + + @staticmethod + def _pil_to_qimage(pil_img: Image.Image) -> QImage: + """PIL图像转QImage(优化版)""" + if pil_img.mode == 'RGBA': + fmt = QImage.Format_RGBA8888 + else: + pil_img = pil_img.convert('RGBA') + fmt = QImage.Format_RGBA8888 + + return QImage( + pil_img.tobytes(), + pil_img.width, + pil_img.height, + pil_img.width * 4, + fmt + ) + + def receive_data(self, dot_data: str): + """接收新数据(线程安全入口)""" + with self._cond: + if dot_data != self._last_dot_data: + self._last_dot_data = dot_data + self._pending_update = True + self._cond.notify() + + def stop(self): + """安全停止线程""" + with self._cond: + self._running = False + self._cond.notify_all() # 唤醒可能处于等待的线程 \ No newline at end of file diff --git a/tools/graphviz_render/src/main.py b/tools/graphviz_render/src/main.py index 665f081..14e092c 100755 --- a/tools/graphviz_render/src/main.py +++ b/tools/graphviz_render/src/main.py @@ -1,7 +1,9 @@ import os import sys +import signal from ui.viewer import GraphvizViewer +from signal_handler import SignalHandler from PyQt5.QtWidgets import (QApplication) def create_pipe(pipe_name): @@ -28,6 +30,8 @@ def main(): app = QApplication(sys.argv) viewer = GraphvizViewer(pipe_name) + handler = SignalHandler() + handler.sig_interrupt.connect(app.quit) viewer.show() sys.exit(app.exec()) diff --git a/tools/graphviz_render/src/pip_operiton/pip_reader.py b/tools/graphviz_render/src/pip_operiton/pip_reader.py index d9b7db5..c22d03b 100644 --- a/tools/graphviz_render/src/pip_operiton/pip_reader.py +++ b/tools/graphviz_render/src/pip_operiton/pip_reader.py @@ -2,6 +2,8 @@ from PyQt5.QtCore import QThread, pyqtSignal +exit_str = "pipe_read_exit" + class PipeReader(QThread): data_received = pyqtSignal(str) @@ -18,11 +20,13 @@ class PipeReader(QThread): data = pipe.read() if data: self.data_received.emit(data) - # Small sleep to prevent CPU overuse self.msleep(10) + except Exception as e: print(f"Error reading from pipe: {e}") - self.msleep(1000) # Wait before retrying + self.msleep(1000) def stop(self): - self.running = False \ No newline at end of file + self.running = False + with open(self.pipe_name, 'w') as pipe: + pipe.write(exit_str) \ No newline at end of file diff --git a/tools/graphviz_render/src/signal_handler.py b/tools/graphviz_render/src/signal_handler.py new file mode 100644 index 0000000..eb97aa5 --- /dev/null +++ b/tools/graphviz_render/src/signal_handler.py @@ -0,0 +1,14 @@ +import signal +from PyQt5.QtCore import QObject, pyqtSignal, QTimer + +class SignalHandler(QObject): + sig_interrupt = pyqtSignal() + + def __init__(self): + super().__init__() + # 注册系统信号到Qt信号转发 + signal.signal(signal.SIGINT, self.handle_signal) + + def handle_signal(self, signum, _): + print(f"捕获到信号 {signum}") + self.sig_interrupt.emit() # 通过Qt信号触发主线程退出 \ No newline at end of file diff --git a/tools/graphviz_render/src/transform/zoomable.py b/tools/graphviz_render/src/transform/zoomable.py index 769252c..087bdbe 100755 --- a/tools/graphviz_render/src/transform/zoomable.py +++ b/tools/graphviz_render/src/transform/zoomable.py @@ -1,5 +1,5 @@ from PyQt5.QtCore import pyqtProperty -from PyQt5.QtGui import QPixmap, QWheelEvent, QMouseEvent, QPainter, QColor +from PyQt5.QtGui import QPixmap, QWheelEvent, QMouseEvent, QKeyEvent, QPainter, QColor from PyQt5.QtCore import Qt, QPointF, QPropertyAnimation, QTimer, QEasingCurve from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QOpenGLWidget, QGraphicsEllipseItem) @@ -19,6 +19,7 @@ class ZoomableGraphicsView(QGraphicsView): self.min_zoom = 0.1 self.max_zoom = 20.0 self.dragging = False + self.fisrt_refresh = True self.last_mouse_pos = QPointF() # 添加属性声明 @@ -75,7 +76,9 @@ class ZoomableGraphicsView(QGraphicsView): """更新图像并自适应视图""" self.pixmap_item.setPixmap(pixmap) self._center_pixmap(pixmap) - self.fit_to_view() + if self.fisrt_refresh: + self.fisrt_refresh = False + self.fit_to_view() def _center_pixmap(self, pixmap: QPixmap): """居中放置图元""" @@ -103,6 +106,29 @@ class ZoomableGraphicsView(QGraphicsView): self.scale(factor, factor) self._zoom_factor = new_zoom + def keyPressEvent(self, event: QKeyEvent): + """增加键盘快捷键支持""" + new_zoom = 0.0 + factor = 0.0 + if event.modifiers() == Qt.ControlModifier: + if event.key() == Qt.Key_Left: # Ctrl + Left + event.accept() + factor = 0.8 + new_zoom = self._zoom_factor * factor + + elif event.key() == Qt.Key_Right: # Ctrl + right + event.accept() + factor = 1.25 + new_zoom = self._zoom_factor * factor + + # 应用缩放限制 + if self.min_zoom <= new_zoom <= self.max_zoom: + self.scale(factor, factor) + self._zoom_factor = new_zoom + return + + super().keyPressEvent(event) + def mousePressEvent(self, event: QMouseEvent): """鼠标按下事件处理""" # 左键拖拽 diff --git a/tools/graphviz_render/src/ui/viewer.py b/tools/graphviz_render/src/ui/viewer.py index 0920b03..8111609 100644 --- a/tools/graphviz_render/src/ui/viewer.py +++ b/tools/graphviz_render/src/ui/viewer.py @@ -5,6 +5,7 @@ import subprocess from io import BytesIO from PIL import Image +from data_processing.data_processing import DataProcessing from pip_operiton.pip_reader import PipeReader from transform.zoomable import ZoomableGraphicsView @@ -19,9 +20,7 @@ class GraphvizViewer(QMainWindow): def __init__(self, pipe_name): super().__init__() self.pipe_name = pipe_name - self.original_image = None self.current_pixmap = None - self.last_dot_data = '' self.setup_ui() def setup_ui(self): @@ -51,9 +50,13 @@ class GraphvizViewer(QMainWindow): layout.addWidget(self.timestamp_label) layout.addWidget(self.image_label) + self.data_processing = DataProcessing() + self.data_processing.data_received.connect(self.update_graph) + self.data_processing.start() + # Setup pipe reader self.pipe_reader = PipeReader(self.pipe_name) - self.pipe_reader.data_received.connect(self.update_graph) + self.pipe_reader.data_received.connect(self.receive_graph_data) self.pipe_reader.start() # Setup resize timer for debouncing @@ -67,72 +70,33 @@ class GraphvizViewer(QMainWindow): def on_resize(self, event): super().resizeEvent(event) # Debounce resize events - self.resize_timer.start(100) - - def update_graph(self, dot_data): - if dot_data != self.last_dot_data: - self.last_dot_data = dot_data - - try: - # Render DOT data using Graphviz - proc = subprocess.Popen( - ['dot', '-Tpng'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE - ) - output, _ = proc.communicate(dot_data.encode()) + # self.resize_timer.start(100) - # Convert to PIL Image - self.original_image = Image.open(BytesIO(output)) - - # Update image display - self.update_image() - - except Exception as e: - print(f"Error rendering graph: {e}") + def update_graph(self, pixmap): + if pixmap != self.current_pixmap: + self.current_pixmap = pixmap + self.image_label.update_image(self.current_pixmap) + def updateTime(self): # Update timestamp current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.timestamp_label.setText(current_time) + + def receive_graph_data(self, data): + self.updateTime() + self.data_processing.receive_data(data) def update_image(self): - if self.original_image is None: + if self.update_image is None: return - - try: - # Convert PIL image to QImage directly - if self.original_image.mode == 'RGBA': - # For RGBA images, use direct conversion - qimg = QImage( - self.original_image.tobytes(), - self.original_image.width, - self.original_image.height, - self.original_image.width * 4, - QImage.Format.Format_RGBA8888 - ) - else: - # For other formats, convert to RGBA first - rgba_image = self.original_image.convert('RGBA') - qimg = QImage( - rgba_image.tobytes(), - rgba_image.width, - rgba_image.height, - rgba_image.width * 4, - QImage.Format.Format_RGBA8888 - ) - - # Create pixmap from QImage - pixmap = QPixmap.fromImage(qimg) - - # Update the zoomable label - self.image_label.update_image(pixmap) - - except Exception as e: - print(f"Error updating image: {e}") + self.image_label.update_image(self.current_pixmap) def closeEvent(self, event): + print(f'closeEvent') self.pipe_reader.stop() + self.data_processing.stop() self.pipe_reader.wait() + self.data_processing.wait() if os.path.exists(self.pipe_name): os.remove(self.pipe_name) event.accept() \ No newline at end of file -- Gitee From 49f2f1368e2b92f7df0b3274a7baa20128ae95a3 Mon Sep 17 00:00:00 2001 From: duanyh <19517952495@139.com> Date: Mon, 24 Mar 2025 19:44:01 +0800 Subject: [PATCH 5/8] =?UTF-8?q?opt:=E4=BC=98=E5=8C=96=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=BB=84=E7=BB=87=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/{data_processing => }/data_processing.py | 0 .../src/data_processing/__init__.py | 1 - .../pip_reader.py => data_source.py} | 2 +- .../graphviz_render/src/pip_operiton/__init__.py | 1 - tools/graphviz_render/src/ui/viewer.py | 15 +++++++-------- 5 files changed, 8 insertions(+), 11 deletions(-) rename tools/graphviz_render/src/{data_processing => }/data_processing.py (100%) delete mode 100644 tools/graphviz_render/src/data_processing/__init__.py rename tools/graphviz_render/src/{pip_operiton/pip_reader.py => data_source.py} (96%) delete mode 100644 tools/graphviz_render/src/pip_operiton/__init__.py diff --git a/tools/graphviz_render/src/data_processing/data_processing.py b/tools/graphviz_render/src/data_processing.py similarity index 100% rename from tools/graphviz_render/src/data_processing/data_processing.py rename to tools/graphviz_render/src/data_processing.py diff --git a/tools/graphviz_render/src/data_processing/__init__.py b/tools/graphviz_render/src/data_processing/__init__.py deleted file mode 100644 index b735aaa..0000000 --- a/tools/graphviz_render/src/data_processing/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .data_processing import DataProcessing \ No newline at end of file diff --git a/tools/graphviz_render/src/pip_operiton/pip_reader.py b/tools/graphviz_render/src/data_source.py similarity index 96% rename from tools/graphviz_render/src/pip_operiton/pip_reader.py rename to tools/graphviz_render/src/data_source.py index c22d03b..386a806 100644 --- a/tools/graphviz_render/src/pip_operiton/pip_reader.py +++ b/tools/graphviz_render/src/data_source.py @@ -4,7 +4,7 @@ from PyQt5.QtCore import QThread, pyqtSignal exit_str = "pipe_read_exit" -class PipeReader(QThread): +class DataSource(QThread): data_received = pyqtSignal(str) def __init__(self, pipe_name): diff --git a/tools/graphviz_render/src/pip_operiton/__init__.py b/tools/graphviz_render/src/pip_operiton/__init__.py deleted file mode 100644 index 8c72da5..0000000 --- a/tools/graphviz_render/src/pip_operiton/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .pip_reader import PipeReader \ No newline at end of file diff --git a/tools/graphviz_render/src/ui/viewer.py b/tools/graphviz_render/src/ui/viewer.py index 8111609..24e7b16 100644 --- a/tools/graphviz_render/src/ui/viewer.py +++ b/tools/graphviz_render/src/ui/viewer.py @@ -5,8 +5,8 @@ import subprocess from io import BytesIO from PIL import Image -from data_processing.data_processing import DataProcessing -from pip_operiton.pip_reader import PipeReader +from data_processing import DataProcessing +from data_source import DataSource from transform.zoomable import ZoomableGraphicsView @@ -55,9 +55,9 @@ class GraphvizViewer(QMainWindow): self.data_processing.start() # Setup pipe reader - self.pipe_reader = PipeReader(self.pipe_name) - self.pipe_reader.data_received.connect(self.receive_graph_data) - self.pipe_reader.start() + self.data_source = DataSource(self.pipe_name) + self.data_source.data_received.connect(self.receive_graph_data) + self.data_source.start() # Setup resize timer for debouncing self.resize_timer = QTimer() @@ -92,10 +92,9 @@ class GraphvizViewer(QMainWindow): self.image_label.update_image(self.current_pixmap) def closeEvent(self, event): - print(f'closeEvent') - self.pipe_reader.stop() + self.data_source.stop() self.data_processing.stop() - self.pipe_reader.wait() + self.data_source.wait() self.data_processing.wait() if os.path.exists(self.pipe_name): os.remove(self.pipe_name) -- Gitee From 4fbe1348f7db111048c44ce3dc237bbd81b3860c Mon Sep 17 00:00:00 2001 From: lichunjun Date: Mon, 24 Mar 2025 19:45:43 +0800 Subject: [PATCH 6/8] tidy --- tools/graphviz_render/src/transform/zoomable.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tools/graphviz_render/src/transform/zoomable.py diff --git a/tools/graphviz_render/src/transform/zoomable.py b/tools/graphviz_render/src/transform/zoomable.py old mode 100755 new mode 100644 -- Gitee From 56ac9c9c1a46782f86b9a96193d0c300eb55b76b Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 25 Mar 2025 08:53:59 +0800 Subject: [PATCH 7/8] =?UTF-8?q?tidy:=E6=95=B4=E7=90=86=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/graphviz_render.py | 383 ----------------- .../{README.md => README_cn.md} | 13 +- tools/graphviz_render/giv.txt | 401 ------------------ .../{src => }/requirements.txt | 0 tools/graphviz_render/src/data_processing.py | 22 +- tools/graphviz_render/src/data_source.py | 22 +- tools/graphviz_render/src/main.py | 24 +- tools/graphviz_render/src/signal_handler.py | 22 +- .../graphviz_render/src/transform/__init__.py | 22 +- .../graphviz_render/src/transform/zoomable.py | 22 +- .../src/transform/zoomable_cpu.py | 22 +- tools/graphviz_render/src/ui/__init__.py | 22 +- tools/graphviz_render/src/ui/viewer.py | 23 +- 13 files changed, 194 insertions(+), 804 deletions(-) delete mode 100755 tools/graphviz_render.py rename tools/graphviz_render/{README.md => README_cn.md} (34%) delete mode 100644 tools/graphviz_render/giv.txt rename tools/graphviz_render/{src => }/requirements.txt (100%) diff --git a/tools/graphviz_render.py b/tools/graphviz_render.py deleted file mode 100755 index 9d26628..0000000 --- a/tools/graphviz_render.py +++ /dev/null @@ -1,383 +0,0 @@ -#!/usr/bin/env python3 - -# -# .============. -# // M A K E / \ -# // C++ DEV / \ -# // E A S Y / \/ \ -# ++ ----------. \/\ . -# \\ \ \ /\ / -# \\ \ \ / -# \\ \ \ / -# -============' -# -# Copyright (c) 2025 Hevake and contributors, all rights reserved. -# -# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) -# Use of this source code is governed by MIT license that can be found -# in the LICENSE file in the root of the source tree. All contributing -# project authors may be found in the CONTRIBUTORS.md file in the root -# of the source tree. -# - -import os -import sys -import asyncio -import subprocess -from datetime import datetime -from io import BytesIO -from PIL import Image -from PyQt6.QtWidgets import (QApplication, QMainWindow, QLabel, QVBoxLayout, - QWidget, QScrollArea, QFrame) -from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread, QSize, QPoint -from PyQt6.QtGui import QImage, QPixmap, QFont, QPainter, QMouseEvent, QWheelEvent - -class ZoomableLabel(QLabel): - def __init__(self, parent=None): - super().__init__(parent) - self.zoom_factor = 1.0 - self.min_zoom = 0.1 - self.max_zoom = 10.0 - self.zoom_step = 1.2 - self.panning = False - self.last_pos = None - self.offset = QPoint(0, 0) - self.current_pixmap = None - self.setMouseTracking(True) - self.initial_fit = True - self.last_click_time = 0 - self.last_click_pos = None - - def wheelEvent(self, event: QWheelEvent): - if self.current_pixmap is None: - return - - # Get cursor position relative to the widget - cursor_pos = event.position() - - # Update zoom factor - if event.angleDelta().y() > 0: - self.zoom_at_position(cursor_pos.toPoint(), self.zoom_step) - else: - self.zoom_at_position(cursor_pos.toPoint(), 1.0 / self.zoom_step) - - def mousePressEvent(self, event: QMouseEvent): - if event.button() == Qt.MouseButton.RightButton: - self.panning = True - self.last_pos = event.position().toPoint() - self.setCursor(Qt.CursorShape.ClosedHandCursor) - elif event.button() == Qt.MouseButton.MiddleButton: - # Reset zoom and position when middle mouse button is pressed - self.fit_to_window() - elif event.button() == Qt.MouseButton.LeftButton: - current_time = event.timestamp() - current_pos = event.position().toPoint() - - # Check if this is a double click (within 500ms and same position) - if (current_time - self.last_click_time < 500 and - self.last_click_pos is not None and - (current_pos - self.last_click_pos).manhattanLength() < 5): - # Double click detected - zoom in at cursor position - self.zoom_at_position(current_pos, self.zoom_step) - self.last_click_time = 0 # Reset click time - self.last_click_pos = None # Reset click position - else: - # Single click - start panning - self.panning = True - self.last_pos = current_pos - self.setCursor(Qt.CursorShape.ClosedHandCursor) - # Update last click info for potential double click - self.last_click_time = current_time - self.last_click_pos = current_pos - - def mouseReleaseEvent(self, event: QMouseEvent): - if event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: - self.panning = False - self.setCursor(Qt.CursorShape.ArrowCursor) - - def mouseMoveEvent(self, event: QMouseEvent): - if self.panning and self.last_pos is not None: - delta = event.position().toPoint() - self.last_pos - self.offset += delta - self.last_pos = event.position().toPoint() - self.update_image(self.current_pixmap) - - def update_image(self, pixmap: QPixmap, zoom_center: QPoint = None): - if pixmap is None: - return - - self.current_pixmap = pixmap - - # If this is the first image, fit it to the window - if self.initial_fit: - self.initial_fit = False - self.fit_to_window() - return - - # Calculate new size based on zoom factor - new_width = int(pixmap.width() * self.zoom_factor) - new_height = int(pixmap.height() * self.zoom_factor) - - # Scale the pixmap - scaled_pixmap = pixmap.scaled( - new_width, - new_height, - Qt.AspectRatioMode.KeepAspectRatio, - Qt.TransformationMode.SmoothTransformation - ) - - # Create a new pixmap with the same size as the label - result_pixmap = QPixmap(self.size()) - result_pixmap.fill(Qt.GlobalColor.transparent) - - # Create painter for the result pixmap - painter = QPainter(result_pixmap) - - # Calculate the position to draw the scaled image - x = (self.width() - scaled_pixmap.width()) // 2 + self.offset.x() - y = (self.height() - scaled_pixmap.height()) // 2 + self.offset.y() - - # Draw the scaled image - painter.drawPixmap(x, y, scaled_pixmap) - painter.end() - - # Update the display - super().setPixmap(result_pixmap) - - # Update minimum size - self.setMinimumSize(new_width, new_height) - - def fit_to_window(self): - if self.current_pixmap is None: - return - - # Calculate scaling ratio to fit the window - width_ratio = self.width() / self.current_pixmap.width() - height_ratio = self.height() / self.current_pixmap.height() - self.zoom_factor = min(width_ratio, height_ratio) - - # Reset offset - self.offset = QPoint(0, 0) - - # Update display - self.update_image(self.current_pixmap) - - def resizeEvent(self, event): - super().resizeEvent(event) - # Always fit to window on resize - if self.current_pixmap is not None: - self.fit_to_window() - - def zoom_at_position(self, pos: QPoint, factor: float): - if self.current_pixmap is None: - return - - # Calculate cursor position relative to the image - image_x = pos.x() - (self.width() - self.current_pixmap.width() * self.zoom_factor) / 2 - self.offset.x() - image_y = pos.y() - (self.height() - self.current_pixmap.height() * self.zoom_factor) / 2 - self.offset.y() - - # Calculate the ratio of cursor position to image size - ratio_x = image_x / (self.current_pixmap.width() * self.zoom_factor) - ratio_y = image_y / (self.current_pixmap.height() * self.zoom_factor) - - # Update zoom factor - old_zoom = self.zoom_factor - new_zoom = min(self.zoom_factor * factor, self.max_zoom) - - # Calculate new image size - new_width = int(self.current_pixmap.width() * new_zoom) - new_height = int(self.current_pixmap.height() * new_zoom) - - # Calculate new cursor position relative to the image - new_image_x = ratio_x * new_width - new_image_y = ratio_y * new_height - - # Calculate new offset to keep cursor position fixed - new_x = pos.x() - (self.width() - new_width) / 2 - new_image_x - new_y = pos.y() - (self.height() - new_height) / 2 - new_image_y - - # Update zoom and offset - self.zoom_factor = new_zoom - self.offset = QPoint(int(new_x), int(new_y)) - - # Update display - self.update_image(self.current_pixmap) - -class PipeReader(QThread): - data_received = pyqtSignal(str) - - def __init__(self, pipe_name): - super().__init__() - self.pipe_name = pipe_name - self.running = True - - def run(self): - while self.running: - try: - with open(self.pipe_name, 'r') as pipe: - while self.running: - data = pipe.read() - if data: - self.data_received.emit(data) - # Small sleep to prevent CPU overuse - self.msleep(10) - except Exception as e: - print(f"Error reading from pipe: {e}") - self.msleep(1000) # Wait before retrying - - def stop(self): - self.running = False - -class GraphvizViewer(QMainWindow): - def __init__(self, pipe_name): - super().__init__() - self.pipe_name = pipe_name - self.original_image = None - self.current_pixmap = None - self.last_dot_data = '' - self.setup_ui() - - def setup_ui(self): - self.setWindowTitle(f"Graphviz: {self.pipe_name}") - self.setMinimumSize(300, 200) - - # Create central widget and layout - central_widget = QWidget() - self.setCentralWidget(central_widget) - layout = QVBoxLayout(central_widget) - layout.setContentsMargins(0, 0, 0, 0) # Remove margins - layout.setSpacing(0) # Remove spacing - - # Create timestamp label with minimal height - self.timestamp_label = QLabel() - self.timestamp_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.timestamp_label.setStyleSheet("background-color: #f0f0f0; padding: 2px;") - self.timestamp_label.setFont(QFont("Arial", 8)) # Smaller font - self.timestamp_label.setFixedHeight(20) # Fixed height for timestamp - - # Create zoomable image label - self.image_label = ZoomableLabel() - self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.image_label.setMinimumSize(1, 1) # Allow shrinking - - # Add widgets to main layout - layout.addWidget(self.timestamp_label) - layout.addWidget(self.image_label) - - # Setup pipe reader - self.pipe_reader = PipeReader(self.pipe_name) - self.pipe_reader.data_received.connect(self.update_graph) - self.pipe_reader.start() - - # Setup resize timer for debouncing - self.resize_timer = QTimer() - self.resize_timer.setSingleShot(True) - self.resize_timer.timeout.connect(self.update_image) - - # Connect resize event - self.resizeEvent = self.on_resize - - def on_resize(self, event): - super().resizeEvent(event) - # Debounce resize events - self.resize_timer.start(100) - - def update_graph(self, dot_data): - if dot_data != self.last_dot_data: - self.last_dot_data = dot_data - - try: - # Render DOT data using Graphviz - proc = subprocess.Popen( - ['dot', '-Tpng'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE - ) - output, _ = proc.communicate(dot_data.encode()) - - # Convert to PIL Image - self.original_image = Image.open(BytesIO(output)) - - # Update image display - self.update_image() - - except Exception as e: - print(f"Error rendering graph: {e}") - - # Update timestamp - current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - self.timestamp_label.setText(current_time) - - def update_image(self): - if self.original_image is None: - return - - try: - # Convert PIL image to QImage directly - if self.original_image.mode == 'RGBA': - # For RGBA images, use direct conversion - qimg = QImage( - self.original_image.tobytes(), - self.original_image.width, - self.original_image.height, - self.original_image.width * 4, - QImage.Format.Format_RGBA8888 - ) - else: - # For other formats, convert to RGBA first - rgba_image = self.original_image.convert('RGBA') - qimg = QImage( - rgba_image.tobytes(), - rgba_image.width, - rgba_image.height, - rgba_image.width * 4, - QImage.Format.Format_RGBA8888 - ) - - # Create pixmap from QImage - pixmap = QPixmap.fromImage(qimg) - - # Update the zoomable label - self.image_label.update_image(pixmap) - - except Exception as e: - print(f"Error updating image: {e}") - - def closeEvent(self, event): - self.pipe_reader.stop() - self.pipe_reader.wait() - if os.path.exists(self.pipe_name): - os.remove(self.pipe_name) - event.accept() - -def create_pipe(pipe_name): - if os.path.exists(pipe_name) : - os.remove(pipe_name) - os.mkfifo(pipe_name) - -def print_usage(proc_name): - print("This is a real-time rendering tool for Graphviz graphics.") - print("Author: Hevake Lee\n") - print(f"Usage: {proc_name} /some/where/you_pipe_file\n") - -def main(): - if '-h' in sys.argv or '--help' in sys.argv: - print_usage(sys.argv[0]) - sys.exit(0) - - if len(sys.argv) != 2: - print_usage(sys.argv[0]) - sys.exit(1) - - pipe_name = sys.argv[1] - create_pipe(pipe_name) - - app = QApplication(sys.argv) - viewer = GraphvizViewer(pipe_name) - viewer.show() - sys.exit(app.exec()) - - os.remove(pipe_name) - -if __name__ == "__main__": - main() diff --git a/tools/graphviz_render/README.md b/tools/graphviz_render/README_cn.md similarity index 34% rename from tools/graphviz_render/README.md rename to tools/graphviz_render/README_cn.md index f3453cf..103ff2f 100644 --- a/tools/graphviz_render/README.md +++ b/tools/graphviz_render/README_cn.md @@ -1,16 +1,15 @@ -第一步:安装python3.12及以上 +第一步:安装python3及以上 第二步:安装依赖 `pip3 install -r requirements.txt` -第三步:运行工具`graphviz_render/src/` - `python3 main.py /tmp/gviz` (备注: /tmp/gviz是管道名,可自定义) +第三步:运行工具 + `python3 ./src/main.py /tmp/gviz` (备注: /tmp/gviz是管道名,可自定义) 如果运行报以下错误 - ![alt text](images/image.png) + ![](images/image.png) 请安装: `sudo apt-get install libxcb-cursor0 libxcb-xinerama0 libxcb-randr0 libxcb-util0-dev` -第四步:执行监听shell脚本 - ![alt text](images/image-1.png) - 示例:`while true; do echo '/cc/brain/graph_work; quit' | nc 10.8.11.62 3000 > /tmp/gviz; sleep 1; done` +第四步:往管道文件写数据 + 示例:`echo "diagraph{ A->B; B->C; A->C }" > /tmp/gviz` diff --git a/tools/graphviz_render/giv.txt b/tools/graphviz_render/giv.txt deleted file mode 100644 index 7ce047b..0000000 --- a/tools/graphviz_render/giv.txt +++ /dev/null @@ -1,401 +0,0 @@ -digraph { -rankdir=LR; -node [shape="rect",style="filled"]; -action_24 [fillcolor="orange",label="24.Clean\nclean_time = 0\ncollect_enable = true\ndidnot_continue_work_after_leave_ground = false\ndrying_enable = true\nhas_it_been_first_weeting_fill_water = false\nis_do_stop_clean = false\nis_fault_pause = false\nis_need_back_wash = false\nis_need_drying = false\nis_need_sync_task_desc = false\nis_only_discharge_dust = false\nis_pause_task_timeout = false\nis_quiet_mode = false\nis_relocation_fail = false\nis_timeout_recharge = false\nis_user_click_recharge = false\nlast_collect_dust_clean_timepoint = 0\npause_task_timeout_times = 3\nremain_clean_area = 0.0"]; -action_25 [fillcolor="green",label="25.BackupMapBag"]; -action_24->action_25 [label="0.prework"]; -action_26 [fillcolor="white",label="26.FirstWettingAndFillWaterAction\nis_allow_interrupt = false"]; -action_27 [fillcolor="white",label="27.BackWashOnly\nis_allow_interrupt = false\nis_self_clean_finish = false\nis_succ_all_action = true\nis_water_empty_during_self_clean = false\nself_clean_time = 10"]; -action_28 [fillcolor="white",label="28.SetPowerToWork"]; -action_27->action_28 [label="0.conncet_bt_action"]; -action_69 [fillcolor="white",label="69.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; -action_71 [fillcolor="white",label="71.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_72 [fillcolor="white",label="72.Prepare"]; -action_78 [fillcolor="white",label="78.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_79 [fillcolor="white",label="79.Function\n[clean fault]"]; -action_78->action_79 [label="[0]"]; -action_73 [fillcolor="white",label="73.Check"]; -action_74 [fillcolor="white",label="74.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_75 [fillcolor="white",label="75.Function"]; -action_74->action_75 [label="[0]"]; -action_76 [fillcolor="white",label="76.SetPowerToWork"]; -action_74->action_76 [label="[1]"]; -action_77 [fillcolor="white",label="77.Function"]; -action_74->action_77 [label="[2]"]; -action_73->action_74; -action_78->action_73 [label="[1]"]; -action_81 [fillcolor="white",label="81.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_80 [fillcolor="white",label="80.WaitLidarReady"]; -action_81->action_80 [label="[0]"]; -action_82 [fillcolor="white",label="82.Sleep"]; -action_81->action_82 [label="[1]"]; -action_78->action_81 [label="[2]"]; -action_83 [fillcolor="white",label="83.Function"]; -action_78->action_83 [label="[3]"]; -action_84 [fillcolor="white",label="84.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_78->action_84 [label="[4]"]; -action_72->action_78; -action_71->action_72 [label="[0]"]; -action_69->action_71 [label="1.exit_base_seq"]; -action_70 [fillcolor="white",label="70.SetPowerToWork"]; -action_69->action_70 [label="3.set_power_mode"]; -action_125 [fillcolor="white",label="125.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_126 [fillcolor="white",label="126.SetMucPowerManageMode"]; -action_125->action_126 [label="[0]"]; -action_127 [fillcolor="white",label="127.Open22VoltChannel"]; -action_125->action_127 [label="[1]"]; -action_69->action_125 [label="4.open_22v_channel"]; -action_129 [fillcolor="white",label="129.WaitBtUploadOnBase"]; -action_69->action_129 [label="5.pending_bt_upload_on_base"]; -action_128 [fillcolor="white",label="128.SetMucPowerManageMode"]; -action_69->action_128 [label="6.close_mcu_charge_mode"]; -action_131 [fillcolor="white",label="131.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; -action_69->action_131 [label="7.do_dill_water"]; -action_130 [fillcolor="white",label="130.DischargeSewage"]; -action_69->action_130 [label="8.discharge_sewage"]; -action_27->action_69 [label="3.change_water"]; -action_132 [fillcolor="white",label="132.DoSelfClean"]; -action_133 [fillcolor="white",label="133.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_134 [fillcolor="white",label="134.LoopIf"]; -action_135 [fillcolor="white",label="135.Function"]; -action_134->action_135 [label="0.if"]; -action_136 [fillcolor="white",label="136.Sleep"]; -action_134->action_136 [label="1.exec"]; -action_133->action_134 [label="[0]"]; -action_132->action_133; -action_27->action_132 [label="4.do_self_clean"]; -action_137 [fillcolor="white",label="137.RinseFilter"]; -action_138 [fillcolor="white",label="138.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_139 [fillcolor="white",label="139.Function"]; -action_138->action_139 [label="[0]"]; -action_140 [fillcolor="white",label="140.SendOpenBaseValveCmdAction"]; -action_138->action_140 [label="[1]"]; -action_141 [fillcolor="white",label="141.Sleep"]; -action_138->action_141 [label="[2]"]; -action_142 [fillcolor="white",label="142.SendDischargeSewageCmd"]; -action_138->action_142 [label="[3]"]; -action_143 [fillcolor="white",label="143.Sleep"]; -action_138->action_143 [label="[4]"]; -action_137->action_138; -action_27->action_137 [label="5.rinse_filter"]; -action_26->action_27 [label="1.fully_wetting_action"]; -action_144 [fillcolor="white",label="144.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; -action_26->action_144 [label="2.host_fill_water_action"]; -action_24->action_26 [label="1.prework"]; -action_145 [fillcolor="lightblue",label="145.Clean\nhas_it_been_wetted = false\nis_clean_with_explore = false\nlast_state_before_pause = 1"]; -action_191 [fillcolor="white",label="191.IfElse"]; -action_190 [fillcolor="white",label="190.Function\n[is_switch_to_clean_with_explore]"]; -action_191->action_190 [label="0.if"]; -action_176 [fillcolor="white",label="176.Prepare"]; -action_182 [fillcolor="white",label="182.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_183 [fillcolor="white",label="183.Function\n[clean fault]"]; -action_182->action_183 [label="[0]"]; -action_177 [fillcolor="white",label="177.Check"]; -action_178 [fillcolor="white",label="178.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_179 [fillcolor="white",label="179.Function"]; -action_178->action_179 [label="[0]"]; -action_180 [fillcolor="white",label="180.SetPowerToWork"]; -action_178->action_180 [label="[1]"]; -action_181 [fillcolor="white",label="181.Function"]; -action_178->action_181 [label="[2]"]; -action_177->action_178; -action_182->action_177 [label="[1]"]; -action_185 [fillcolor="white",label="185.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_184 [fillcolor="white",label="184.WaitLidarReady"]; -action_185->action_184 [label="[0]"]; -action_186 [fillcolor="white",label="186.Sleep"]; -action_185->action_186 [label="[1]"]; -action_182->action_185 [label="[2]"]; -action_187 [fillcolor="white",label="187.Function"]; -action_182->action_187 [label="[3]"]; -action_188 [fillcolor="white",label="188.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_182->action_188 [label="[4]"]; -action_189 [fillcolor="white",label="189.StartSlam\nmode = 2\nrelocate_state = 0"]; -action_182->action_189 [label="[5]"]; -action_176->action_182; -action_191->action_176 [label="1.then"]; -action_146 [fillcolor="white",label="146.IfElse\n[prepare_clean_by_is_need_mop]"]; -action_147 [fillcolor="white",label="147.Function"]; -action_146->action_147 [label="0.if"]; -action_162 [fillcolor="white",label="162.Prepare"]; -action_168 [fillcolor="white",label="168.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_169 [fillcolor="white",label="169.Function\n[clean fault]"]; -action_168->action_169 [label="[0]"]; -action_163 [fillcolor="white",label="163.Check"]; -action_164 [fillcolor="white",label="164.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_165 [fillcolor="white",label="165.Function"]; -action_164->action_165 [label="[0]"]; -action_166 [fillcolor="white",label="166.SetPowerToWork"]; -action_164->action_166 [label="[1]"]; -action_167 [fillcolor="white",label="167.Function"]; -action_164->action_167 [label="[2]"]; -action_163->action_164; -action_168->action_163 [label="[1]"]; -action_171 [fillcolor="white",label="171.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_170 [fillcolor="white",label="170.WaitLidarReady"]; -action_171->action_170 [label="[0]"]; -action_172 [fillcolor="white",label="172.Sleep"]; -action_171->action_172 [label="[1]"]; -action_168->action_171 [label="[2]"]; -action_173 [fillcolor="white",label="173.Function"]; -action_168->action_173 [label="[3]"]; -action_174 [fillcolor="white",label="174.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_168->action_174 [label="[4]"]; -action_175 [fillcolor="white",label="175.StartSlam\nmode = 2\nrelocate_state = 0"]; -action_168->action_175 [label="[5]"]; -action_162->action_168; -action_146->action_162 [label="1.then"]; -action_148 [fillcolor="white",label="148.Prepare"]; -action_154 [fillcolor="white",label="154.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_155 [fillcolor="white",label="155.Function\n[clean fault]"]; -action_154->action_155 [label="[0]"]; -action_149 [fillcolor="white",label="149.Check"]; -action_150 [fillcolor="white",label="150.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_151 [fillcolor="white",label="151.Function"]; -action_150->action_151 [label="[0]"]; -action_152 [fillcolor="white",label="152.SetPowerToWork"]; -action_150->action_152 [label="[1]"]; -action_153 [fillcolor="white",label="153.Function"]; -action_150->action_153 [label="[2]"]; -action_149->action_150; -action_154->action_149 [label="[1]"]; -action_157 [fillcolor="white",label="157.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_156 [fillcolor="white",label="156.WaitLidarReady"]; -action_157->action_156 [label="[0]"]; -action_158 [fillcolor="white",label="158.Sleep"]; -action_157->action_158 [label="[1]"]; -action_154->action_157 [label="[2]"]; -action_159 [fillcolor="white",label="159.Function"]; -action_154->action_159 [label="[3]"]; -action_160 [fillcolor="white",label="160.ExitBase\nis_force_exit = false\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_154->action_160 [label="[4]"]; -action_161 [fillcolor="white",label="161.StartSlam\nmode = 2\nrelocate_state = 0"]; -action_154->action_161 [label="[5]"]; -action_148->action_154; -action_146->action_148 [label="2.else"]; -action_191->action_146 [label="2.else"]; -action_145->action_191 [label="1.prepare"]; -action_200 [fillcolor="white",label="200.RollerWetting"]; -action_145->action_200 [label="2.wetting"]; -action_198 [fillcolor="white",label="198.DoClean"]; -action_199 [fillcolor="white",label="199.WaitTeFinish"]; -action_198->action_199; -action_145->action_198 [label="3.do_clean"]; -action_192 [fillcolor="white",label="192.CleanTaskRelocateFail"]; -action_145->action_192 [label="4.relocate_fail"]; -action_24->action_145 [label="2.clean"]; -action_241 [fillcolor="orange",label="241.BackBaseChangeWater"]; -action_282 [fillcolor="orange",label="282.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; -action_284 [fillcolor="white",label="284.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_285 [fillcolor="white",label="285.Prepare"]; -action_291 [fillcolor="white",label="291.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_292 [fillcolor="white",label="292.Function\n[clean fault]"]; -action_291->action_292 [label="[0]"]; -action_286 [fillcolor="white",label="286.Check"]; -action_287 [fillcolor="white",label="287.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_288 [fillcolor="white",label="288.Function"]; -action_287->action_288 [label="[0]"]; -action_289 [fillcolor="white",label="289.SetPowerToWork"]; -action_287->action_289 [label="[1]"]; -action_290 [fillcolor="white",label="290.Function"]; -action_287->action_290 [label="[2]"]; -action_286->action_287; -action_291->action_286 [label="[1]"]; -action_294 [fillcolor="white",label="294.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_293 [fillcolor="white",label="293.WaitLidarReady"]; -action_294->action_293 [label="[0]"]; -action_295 [fillcolor="white",label="295.Sleep"]; -action_294->action_295 [label="[1]"]; -action_291->action_294 [label="[2]"]; -action_296 [fillcolor="white",label="296.Function"]; -action_291->action_296 [label="[3]"]; -action_297 [fillcolor="white",label="297.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_291->action_297 [label="[4]"]; -action_285->action_291; -action_284->action_285 [label="[0]"]; -action_282->action_284 [label="1.exit_base_seq"]; -action_283 [fillcolor="green",label="283.SetPowerToWork"]; -action_282->action_283 [label="3.set_power_mode"]; -action_338 [fillcolor="white",label="338.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_339 [fillcolor="white",label="339.SetMucPowerManageMode"]; -action_338->action_339 [label="[0]"]; -action_340 [fillcolor="white",label="340.Open22VoltChannel"]; -action_338->action_340 [label="[1]"]; -action_282->action_338 [label="4.open_22v_channel"]; -action_342 [fillcolor="white",label="342.WaitBtUploadOnBase"]; -action_282->action_342 [label="5.pending_bt_upload_on_base"]; -action_341 [fillcolor="green",label="341.SetMucPowerManageMode"]; -action_282->action_341 [label="6.close_mcu_charge_mode"]; -action_344 [fillcolor="orange",label="344.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 12"]; -action_491 [fillcolor="orange",label="491.Sequence\nindex = 2\nmode = \"AnyFail\""]; -action_515 [fillcolor="green",label="515.ConnectBt"]; -action_491->action_515 [label="[0]"]; -action_516 [fillcolor="green",label="516.WaitCombinedBaseStatusSync"]; -action_491->action_516 [label="[1]"]; -action_494 [fillcolor="orange",label="494.IfElse"]; -action_495 [fillcolor="green",label="495.Function\n[check_is_need_send_water_base_fill_water_cmd]"]; -action_494->action_495 [label="0.if"]; -action_497 [fillcolor="orange",label="497.Sequence\nindex = 3\nmode = \"AnyFail\""]; -action_493 [fillcolor="green",label="493.Function\n[update_water_base_fill_water_seconds_action]"]; -action_497->action_493 [label="[0]"]; -action_492 [fillcolor="green",label="492.SendOpenBaseValveCmdAction"]; -action_497->action_492 [label="[1]"]; -action_496 [fillcolor="green",label="496.Sleep"]; -action_497->action_496 [label="[2]"]; -action_498 [fillcolor="orange",label="498.Sequence\nindex = 2\nmode = \"AnyFail\""]; -action_499 [fillcolor="green",label="499.SetMucPowerManageMode"]; -action_498->action_499 [label="[0]"]; -action_500 [fillcolor="green",label="500.SendFillWaterWithCleaningCiquidCmd"]; -action_498->action_500 [label="[1]"]; -action_501 [fillcolor="orange",label="501.Sleep"]; -action_498->action_501 [label="[2]"]; -action_502 [fillcolor="white",label="502.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_503 [fillcolor="white",label="503.Function"]; -action_502->action_503 [label="[0]"]; -action_504 [fillcolor="white",label="504.Sleep"]; -action_502->action_504 [label="[1]"]; -action_505 [fillcolor="white",label="505.Function"]; -action_502->action_505 [label="[2]"]; -action_498->action_502 [label="[3]"]; -action_497->action_498 [label="[3]"]; -action_494->action_497 [label="1.then"]; -action_506 [fillcolor="white",label="506.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_507 [fillcolor="white",label="507.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_508 [fillcolor="white",label="508.SetMucPowerManageMode"]; -action_507->action_508 [label="[0]"]; -action_509 [fillcolor="white",label="509.SendFillWaterWithCleaningCiquidCmd"]; -action_507->action_509 [label="[1]"]; -action_510 [fillcolor="white",label="510.Sleep"]; -action_507->action_510 [label="[2]"]; -action_511 [fillcolor="white",label="511.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_512 [fillcolor="white",label="512.Function"]; -action_511->action_512 [label="[0]"]; -action_513 [fillcolor="white",label="513.Sleep"]; -action_511->action_513 [label="[1]"]; -action_514 [fillcolor="white",label="514.Function"]; -action_511->action_514 [label="[2]"]; -action_507->action_511 [label="[3]"]; -action_506->action_507 [label="[0]"]; -action_494->action_506 [label="2.else"]; -action_491->action_494 [label="[2]"]; -action_344->action_491 [label="1.filling_water"]; -action_485 [fillcolor="white",label="485.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_486 [fillcolor="white",label="486.SetMucPowerManageMode"]; -action_485->action_486 [label="[0]"]; -action_487 [fillcolor="white",label="487.SendCloseBaseValveCmd"]; -action_485->action_487 [label="[1]"]; -action_488 [fillcolor="white",label="488.SendCloseBaseValveCmd"]; -action_485->action_488 [label="[2]"]; -action_489 [fillcolor="white",label="489.Function"]; -action_485->action_489 [label="[3]"]; -action_490 [fillcolor="white",label="490.Sleep"]; -action_485->action_490 [label="[4]"]; -action_344->action_485 [label="2.fill_water_fulled"]; -action_282->action_344 [label="7.do_dill_water"]; -action_343 [fillcolor="green",label="343.DischargeSewage"]; -action_282->action_343 [label="8.discharge_sewage"]; -action_241->action_282 [label="2.change_water"]; -action_24->action_241 [label="3.change_water"]; -action_345 [fillcolor="white",label="345.BackWashExtend\nis_allow_interrupt = false\nis_succ_all_action = true"]; -action_346 [fillcolor="white",label="346.BackWashOnly\nis_allow_interrupt = false\nis_self_clean_finish = false\nis_succ_all_action = true\nis_water_empty_during_self_clean = false\nself_clean_time = 60"]; -action_347 [fillcolor="white",label="347.SetPowerToWork"]; -action_346->action_347 [label="0.conncet_bt_action"]; -action_388 [fillcolor="white",label="388.ChangeWater\ndischarge_sewage_remain_times = 3\ndiscontacted_return_times = 3\nis_force_fill_water = false\nis_need_discharge_sewage = true\nis_need_fill_water = true\nopen_22v_fail_retry_times = 3\npending_times = 3"]; -action_390 [fillcolor="white",label="390.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_391 [fillcolor="white",label="391.Prepare"]; -action_397 [fillcolor="white",label="397.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_398 [fillcolor="white",label="398.Function\n[clean fault]"]; -action_397->action_398 [label="[0]"]; -action_392 [fillcolor="white",label="392.Check"]; -action_393 [fillcolor="white",label="393.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_394 [fillcolor="white",label="394.Function"]; -action_393->action_394 [label="[0]"]; -action_395 [fillcolor="white",label="395.SetPowerToWork"]; -action_393->action_395 [label="[1]"]; -action_396 [fillcolor="white",label="396.Function"]; -action_393->action_396 [label="[2]"]; -action_392->action_393; -action_397->action_392 [label="[1]"]; -action_400 [fillcolor="white",label="400.Parallel\n[set power mode and wait 500ms]\nmode = \"AllFinish\""]; -action_399 [fillcolor="white",label="399.WaitLidarReady"]; -action_400->action_399 [label="[0]"]; -action_401 [fillcolor="white",label="401.Sleep"]; -action_400->action_401 [label="[1]"]; -action_397->action_400 [label="[2]"]; -action_402 [fillcolor="white",label="402.Function"]; -action_397->action_402 [label="[3]"]; -action_403 [fillcolor="white",label="403.ExitBase\nis_force_exit = true\nis_recv_te_exit_succ = false\nis_te_task_started = false\nis_te_task_stoped = false"]; -action_397->action_403 [label="[4]"]; -action_391->action_397; -action_390->action_391 [label="[0]"]; -action_388->action_390 [label="1.exit_base_seq"]; -action_389 [fillcolor="white",label="389.SetPowerToWork"]; -action_388->action_389 [label="3.set_power_mode"]; -action_444 [fillcolor="white",label="444.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_445 [fillcolor="white",label="445.SetMucPowerManageMode"]; -action_444->action_445 [label="[0]"]; -action_446 [fillcolor="white",label="446.Open22VoltChannel"]; -action_444->action_446 [label="[1]"]; -action_388->action_444 [label="4.open_22v_channel"]; -action_448 [fillcolor="white",label="448.WaitBtUploadOnBase"]; -action_388->action_448 [label="5.pending_bt_upload_on_base"]; -action_447 [fillcolor="white",label="447.SetMucPowerManageMode"]; -action_388->action_447 [label="6.close_mcu_charge_mode"]; -action_450 [fillcolor="white",label="450.DoFillWater\nis_need_bt_interaction = true\nwater_base_fill_water_seconds = 0"]; -action_388->action_450 [label="7.do_dill_water"]; -action_449 [fillcolor="white",label="449.DischargeSewage"]; -action_388->action_449 [label="8.discharge_sewage"]; -action_346->action_388 [label="3.change_water"]; -action_451 [fillcolor="white",label="451.DoSelfClean"]; -action_452 [fillcolor="white",label="452.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_453 [fillcolor="white",label="453.LoopIf"]; -action_454 [fillcolor="white",label="454.Function"]; -action_453->action_454 [label="0.if"]; -action_455 [fillcolor="white",label="455.Sleep"]; -action_453->action_455 [label="1.exec"]; -action_452->action_453 [label="[0]"]; -action_451->action_452; -action_346->action_451 [label="4.do_self_clean"]; -action_456 [fillcolor="white",label="456.RinseFilter"]; -action_457 [fillcolor="white",label="457.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_458 [fillcolor="white",label="458.Function"]; -action_457->action_458 [label="[0]"]; -action_459 [fillcolor="white",label="459.SendOpenBaseValveCmdAction"]; -action_457->action_459 [label="[1]"]; -action_460 [fillcolor="white",label="460.Sleep"]; -action_457->action_460 [label="[2]"]; -action_461 [fillcolor="white",label="461.SendDischargeSewageCmd"]; -action_457->action_461 [label="[3]"]; -action_462 [fillcolor="white",label="462.Sleep"]; -action_457->action_462 [label="[4]"]; -action_456->action_457; -action_346->action_456 [label="5.rinse_filter"]; -action_345->action_346 [label="1.back_wash_only_"]; -action_463 [fillcolor="white",label="463.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_464 [fillcolor="white",label="464.SetMucPowerManageMode"]; -action_463->action_464 [label="[0]"]; -action_465 [fillcolor="white",label="465.Open22VoltChannel"]; -action_463->action_465 [label="[1]"]; -action_466 [fillcolor="white",label="466.Sleep"]; -action_463->action_466 [label="[2]"]; -action_345->action_463 [label="2.discharge_to_water_base"]; -action_24->action_345 [label="4.back_wash"]; -action_467 [fillcolor="white",label="467.SaveMapWaitLabelChange\nis_auto_save = false"]; -action_468 [fillcolor="white",label="468.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_469 [fillcolor="white",label="469.SaveMap"]; -action_468->action_469 [label="[0]"]; -action_474 [fillcolor="white",label="474.IfElse"]; -action_470 [fillcolor="white",label="470.Function"]; -action_474->action_470 [label="0.if"]; -action_471 [fillcolor="white",label="471.Sequence\nindex = 0\nmode = \"AnyFail\""]; -action_472 [fillcolor="white",label="472.Function"]; -action_471->action_472 [label="[0]"]; -action_473 [fillcolor="white",label="473.WaitLabelChange"]; -action_471->action_473 [label="[1]"]; -action_474->action_471 [label="1.then"]; -action_468->action_474 [label="[1]"]; -action_467->action_468; -action_24->action_467 [label="6.save_map"]; -} - - diff --git a/tools/graphviz_render/src/requirements.txt b/tools/graphviz_render/requirements.txt similarity index 100% rename from tools/graphviz_render/src/requirements.txt rename to tools/graphviz_render/requirements.txt diff --git a/tools/graphviz_render/src/data_processing.py b/tools/graphviz_render/src/data_processing.py index a03ccb3..18443dd 100644 --- a/tools/graphviz_render/src/data_processing.py +++ b/tools/graphviz_render/src/data_processing.py @@ -1,4 +1,22 @@ -#!/usr/bin/env python3 +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# import os import subprocess @@ -111,4 +129,4 @@ class DataProcessing(QThread): """安全停止线程""" with self._cond: self._running = False - self._cond.notify_all() # 唤醒可能处于等待的线程 \ No newline at end of file + self._cond.notify_all() # 唤醒可能处于等待的线程 diff --git a/tools/graphviz_render/src/data_source.py b/tools/graphviz_render/src/data_source.py index 386a806..c127c50 100644 --- a/tools/graphviz_render/src/data_source.py +++ b/tools/graphviz_render/src/data_source.py @@ -1,4 +1,22 @@ -#!/usr/bin/env python3 +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# from PyQt5.QtCore import QThread, pyqtSignal @@ -29,4 +47,4 @@ class DataSource(QThread): def stop(self): self.running = False with open(self.pipe_name, 'w') as pipe: - pipe.write(exit_str) \ No newline at end of file + pipe.write(exit_str) diff --git a/tools/graphviz_render/src/main.py b/tools/graphviz_render/src/main.py index 14e092c..d016fee 100755 --- a/tools/graphviz_render/src/main.py +++ b/tools/graphviz_render/src/main.py @@ -1,3 +1,25 @@ +#!/usr/bin/env python3 + +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + import os import sys import signal @@ -38,4 +60,4 @@ def main(): os.remove(pipe_name) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/tools/graphviz_render/src/signal_handler.py b/tools/graphviz_render/src/signal_handler.py index eb97aa5..b4f0e6f 100644 --- a/tools/graphviz_render/src/signal_handler.py +++ b/tools/graphviz_render/src/signal_handler.py @@ -1,3 +1,23 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + import signal from PyQt5.QtCore import QObject, pyqtSignal, QTimer @@ -11,4 +31,4 @@ class SignalHandler(QObject): def handle_signal(self, signum, _): print(f"捕获到信号 {signum}") - self.sig_interrupt.emit() # 通过Qt信号触发主线程退出 \ No newline at end of file + self.sig_interrupt.emit() # 通过Qt信号触发主线程退出 diff --git a/tools/graphviz_render/src/transform/__init__.py b/tools/graphviz_render/src/transform/__init__.py index 7849877..1cff8f6 100644 --- a/tools/graphviz_render/src/transform/__init__.py +++ b/tools/graphviz_render/src/transform/__init__.py @@ -1 +1,21 @@ -from .zoomable import ZoomableGraphicsView \ No newline at end of file +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +from .zoomable import ZoomableGraphicsView diff --git a/tools/graphviz_render/src/transform/zoomable.py b/tools/graphviz_render/src/transform/zoomable.py index 087bdbe..348c6f2 100644 --- a/tools/graphviz_render/src/transform/zoomable.py +++ b/tools/graphviz_render/src/transform/zoomable.py @@ -1,3 +1,23 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + from PyQt5.QtCore import pyqtProperty from PyQt5.QtGui import QPixmap, QWheelEvent, QMouseEvent, QKeyEvent, QPainter, QColor from PyQt5.QtCore import Qt, QPointF, QPropertyAnimation, QTimer, QEasingCurve @@ -235,4 +255,4 @@ class ZoomableGraphicsView(QGraphicsView): def resizeEvent(self, event): """窗口大小变化时保持自适应""" self.fit_to_view() - super().resizeEvent(event) \ No newline at end of file + super().resizeEvent(event) diff --git a/tools/graphviz_render/src/transform/zoomable_cpu.py b/tools/graphviz_render/src/transform/zoomable_cpu.py index 7f4e214..7260ced 100644 --- a/tools/graphviz_render/src/transform/zoomable_cpu.py +++ b/tools/graphviz_render/src/transform/zoomable_cpu.py @@ -1,4 +1,22 @@ -#!/usr/bin/env python3 +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# from PyQt5.QtWidgets import QLabel, QApplication, QOpenGLWidget from PyQt5.QtCore import Qt, QPoint, QTime, QTimer @@ -78,4 +96,4 @@ class ZoomableLabel(QLabel): def force_redraw(self): if self.redraw_pending and self.current_pixmap: self.update_image(self.current_pixmap, use_fast=True) - self.redraw_pending = False \ No newline at end of file + self.redraw_pending = False diff --git a/tools/graphviz_render/src/ui/__init__.py b/tools/graphviz_render/src/ui/__init__.py index 650d985..ad8aec0 100644 --- a/tools/graphviz_render/src/ui/__init__.py +++ b/tools/graphviz_render/src/ui/__init__.py @@ -1 +1,21 @@ -from .viewer import GraphvizViewer \ No newline at end of file +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +from .viewer import GraphvizViewer diff --git a/tools/graphviz_render/src/ui/viewer.py b/tools/graphviz_render/src/ui/viewer.py index 24e7b16..08d35e4 100644 --- a/tools/graphviz_render/src/ui/viewer.py +++ b/tools/graphviz_render/src/ui/viewer.py @@ -1,4 +1,22 @@ -#!/usr/bin/env python3 +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2025 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# import os import subprocess @@ -98,4 +116,5 @@ class GraphvizViewer(QMainWindow): self.data_processing.wait() if os.path.exists(self.pipe_name): os.remove(self.pipe_name) - event.accept() \ No newline at end of file + event.accept() + -- Gitee From 8a743715815c9a9ef5d4954df1b90fffc7d553a5 Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Tue, 25 Mar 2025 08:57:32 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E5=88=A0=E9=99=A4images?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/graphviz_render/images/image-1.png | Bin 15804 -> 0 bytes tools/graphviz_render/images/image.png | Bin 53627 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tools/graphviz_render/images/image-1.png delete mode 100644 tools/graphviz_render/images/image.png diff --git a/tools/graphviz_render/images/image-1.png b/tools/graphviz_render/images/image-1.png deleted file mode 100644 index 014867aaf1281bee4b1e510b53dc30cf15dba913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15804 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 Gp_-qa~ueMp8vK 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 Hu0mjf3L)4p -- Gitee