ETE 4 系统发育树可视化实战:SmartView 交互探索与 Qt 矢量渲染全指南
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
本指南基于 scientific-agent-skills 仓库中 etetoolkit 技能 的 visualization.md 文档展开,系统讲解 ETE 4.4.0 的两套绘图系统——面向交互探索的SmartView与面向出版级矢量输出的Qt treeview。读完本文,你将掌握渲染器选型决策、SmartView 布局与 Faces 的编写方法、静态 PNG 截图、Qt 引擎输出 PNG/PDF/SVG 的完整流程,以及配套脚本 quick_visualize.py 的一键可视化用法。
两套绘图系统:SmartView 与 Qt treeview
ETE 4 提供了两套彼此独立的绘图系统,它们的定位、类层次与输出能力完全不同:
- SmartView:基于 Web 的当前交互式浏览器与自适应渲染器,最适合交互式工作与超大型树的探索。相关类全部位于
ete4.smartview命名空间下。 - Qt treeview:保留的可选 Qt 渲染器,用于静态 PNG、PDF 与 SVG 输出。相关类位于
ete4.treeview命名空间下。
关键约束:两套系统不能混用其布局(Layout)类与 Face 类——ete4.smartview下的类只供 SmartView 使用,ete4.treeview下的类只供 Qt 渲染使用。混用会导致ImportError或渲染异常。这一隔离也与仓库 quick_visualize.py 中 SmartView 布局与 Qt 样式构建函数 各自独立实现的源码结构一一对应。
渲染器决策:什么时候用哪一套?
文档给出了一条清晰的选型分界线,实际选择时按以下场景判断:
优先使用 SmartView,当你需要:
- 交互式地探索、搜索、折叠或编辑树;
- 在本机或通过 SSH 隧道服务一棵树;
- 在长期存活的 Jupyter 内核中通过浏览器探索;
- 生成光栅 PNG 截图;
- 手动从浏览器下载当前视图为 SVG 或 PNG;
- 处理一棵大到无法完全展开绘制的树。
使用 Qt treeview,当你要:
- 以程序化或无头(headless)方式输出PDF 或 SVG矢量图;
- 精确控制物理尺寸或 DPI;
- 维护已有的 ETE treeview 布局代码。
这一决策在仓库测试中被固化下来:EngineChoiceTests验证了「无输出文件时选 SmartView、.png后缀走 SmartView、.pdf/.svg后缀走 treeview、无法识别的后缀直接报错」的完整行为(见 tests/etetoolkit/test_scripts.py)。也就是说,矢量 PDF/SVG 必须走 Qt treeview,SmartView 的静态输出只有 PNG 截图这一种形式。
安装:按渲染需求选择 extras
ETE 4.4.0 的安装粒度很细,交互式 SmartView 已包含在基础包中,静态截图与 Qt 渲染则需要额外的 extras:
# 基础包:包含交互式 SmartView uv pip install "ete4==4.4.0" # 静态 SmartView 截图(需要 Selenium 驱动浏览器) uv pip install "ete4[render-sm]==4.4.0" # Qt 渲染(PNG/PDF/SVG) uv pip install "ete4[treeview]==4.4.0"SKILL.md 强调「只安装工作流所需的那一个 extra」:render-sm只为render_sm()的静态 PNG 截图服务,treeview只为 Qt 的矢量输出服务(见 SKILL.md)。安装后可用一条命令确认环境:
uv run --with "ete4==4.4.0" python -c "import ete4; print(ete4.__version__)"注意运行环境前提:仓库技能声明内置脚本需要 Python 3.10+ 与 ete4 4.4.0(上游 ete4 支持 Python >= 3.7);SmartView 探索需要网络访问,静态 PNG 渲染需要ete4[render-sm],Qt PDF/SVG 渲染需要ete4[treeview](见 SKILL.md)。
交互式 SmartView
从 Python 启动
SmartView 的核心入口是Tree.explore():
from pathlib import Path from ete4 import Tree with Path("tree.nw").open(encoding="utf-8") as handle: tree = Tree(handle, parser=1) tree.explore()细节要点:
- 不传
layouts参数时,ETE 应用默认的BASIC_LAYOUT,展示叶名、分支长度与支持值。 explore()立即返回。独立脚本必须让进程保持存活——例如调用input()等待回车,或使用keep_server=True。仓库自带的可视化辅助脚本正是通过「input()阻塞 +finally中调用explorer.stop_server()」来干净地关闭服务(见 quick_visualize.py)。- 在 Jupyter/IPython 中,SmartView 依然是基于浏览器的,当前文档没有提供内联 SmartView widget;保持内核存活并打开服务返回的 URL 即可。
从命令行启动
ete4 explore -t tree.nw --src_tree_format 1用以下命令查看当前发行版支持的完整选项:
ete4 explore --help需要留意的是:CLI 只支持基础的源树选择与解析器选项。虽然 ETE 4.4.0 的帮助文本暴露了--face参数,但其 handler 并不实际应用该参数——定制外观必须使用 Python 的Layout对象。
控制服务端与安全绑定
可以通过参数精确控制服务端行为:
tree.explore( host="127.0.0.1", port=5000, open_browser=False, )安全方面的要求非常明确:
- 保持默认的 loopback(127.0.0.1)绑定,除非远程访问经过了有意的安全加固;
- 远程访问时,通过 SSH 隧道转发 loopback 端口,然后在本地打开浏览器:
ssh -L 5000:localhost:5000 user@remote-host打开http://localhost:5000即可。不要把未经认证的 explorer 绑定到0.0.0.0暴露给不受信任的网络。
仓库对这一安全边界有完整的测试覆盖:BindAddressTests逐一验证了127.0.0.1、127.0.0.2、::1、localhost等 loopback 拼写无需显式授权即可绑定;而0.0.0.0、192.168.1.10、example.com等一切非 loopback 地址都必须显式传入--allow-remote-bind才被接受(见 tests/etetoolkit/test_scripts.py)。测试注释也点明了原因:SmartView 服务的是一个未经认证的交互式查看器,绑定地址因此是安全闸门。
SmartView 布局(Layout)
SmartView 的Layout由四部分组成:
draw_tree(tree):树级样式,以及头部/图例 Faces;draw_node(node[, collapsed]):单个节点的样式与 Faces;name:GUI 中的布局标识符;cache_size:节点绘制结果的记忆化(memoization)控制。
draw_tree与draw_node都是生成器,通过yield产出字典(样式)或 Face 对象。
环形树:标签与支持值标注
from ete4 import Tree from ete4.smartview import Layout, PropFace, TextFace tree = Tree("((A:1,B:1)95:0.2,C:1);", parser="support") def draw_tree(_tree): yield { "shape": "circular", "node-height-min": 8, "content-height-min": 4, } yield TextFace( "Example phylogeny", fs_min=8, fs_max=22, position="header", ) def draw_node(node): if node.is_leaf: yield PropFace( "name", fs_min=4, fs_max=16, position="right", ) return if node.support is not None: yield TextFace( f"{node.support:g}", fs_min=3, fs_max=12, style={"fill": "#555"}, position="top", ) layout = Layout( "circular labels and support", draw_tree=draw_tree, draw_node=draw_node, ) tree.explore(layouts=[layout])注意这里用parser="support"(等价于数字parser=0)读取 Newick,内部节点字段被解释为支持值;而「环形」由draw_tree产出的"shape": "circular"决定。从 api_reference.md 可以查到常用 parser 映射:parser="support"/0将内部字段读为支持值,parser="name"/1将内部字段读为名称,parser=8为全节点命名、parser=9仅叶命名、parser=100仅拓扑。
条件节点样式:按支持值着色
支持值可能是分数(0~1)也可能是百分比(0~100),只有在确认来源文件的约定之后才能归一化:
from ete4 import Tree from ete4.smartview import Layout, PropFace tree = Tree("((A:1,B:1)95:0.2,C:1);", parser="support") def support_fraction(value): if value is None: return None return value / 100 if value > 1 else value def draw_node(node): if node.is_leaf: yield PropFace("name", position="right") support = support_fraction(node.support) if support is None: color = "#888" elif support >= 0.9: color = "#1b7837" elif support >= 0.7: color = "#e08214" else: color = "#b2182b" yield { "dot": { "shape": "circle", "radius": 5, "fill": color, } } layout = Layout("support colors", draw_node=draw_node) tree.explore(layouts=[layout])这个「按支持值分区着色」模式在仓库中被提升为 CLI 一等公民。quick_visualize.py中的support_fraction()使用完全相同的value / 100 if value > 1 else value归一化逻辑(见 quick_visualize.py),并配套--high-support(默认 0.9)、--moderate-support(默认 0.7)等阈值参数与高/中/低/缺失四种颜色(默认#1b7837/#e08214/#b2182b/#999999,见 quick_visualize.py)。测试还验证了边界行为:恰好为 1 的支持值被当作满支持分数而非 1%,None支持值映射为缺失色,阈值可调(见 tests/etetoolkit/test_scripts.py)。
树级样式键速查
常见树级样式键及其含义:
| 键 | 含义 |
|---|---|
shape | "rectangular"或"circular" |
radius | 环形布局的半径 |
angle-start、angle-end、angle-span | 环形布局的角度范围(度) |
node-height-min | 折叠阈值(像素),节点高度低于此值时折叠 |
content-height-min | Faces 出现所需的最小高度 |
collapsed | 折叠节点的样式 |
show-popup-props、hide-popup-props | 弹窗中展示/隐藏的属性 |
is-leaf-fn | 动态终末节点判定规则 |
box、dot、hz-line、vt-line | 类 CSS 的默认样式键 |
示例——半圆布局并限定弹窗属性:
tree_style = { "shape": "circular", "angle-start": -180, "angle-span": 180, "node-height-min": 10, "collapsed": { "shape": "outline", "fill-opacity": 0.6, }, "show-popup-props": ["name", "dist", "support", "group"], } layout = Layout("semicircle", draw_tree=tree_style) tree.explore(layouts=[layout])当节点携带敏感或不相关的元数据时,应当限制弹窗中展示的属性。仓库脚本默认只弹出["name", "dist", "support"]三个属性(见 quick_visualize.py),正是这一原则的落地。
SmartView Faces
ete4.smartview下常用的 Face 类:
TextFace:字面文本;PropFace:节点的某个属性(支持可选格式化);EvalTextFace:表达式求值得到的文本;CircleFace、RectFace、BoxFace:几何形状;ImageFace:图像;SeqFace:分子序列;LegendFace:图例。
Face 的位置(position)包括top、bottom、left、right和aligned;树级文本还可使用header。
对齐的元数据列
利用position="aligned"配合column参数,可以构建基因树/样本树中常见的对齐注释列(如宿主、采样地点等元数据):
from ete4.smartview import Layout, PropFace def draw_node(node): if not node.is_leaf: return yield PropFace("name", position="aligned", column=0) yield PropFace("host", position="aligned", column=1) yield PropFace("location", position="aligned", column=2) layout = Layout("sample metadata", draw_node=draw_node) tree.explore(layouts=[layout])该示例假设树节点上已通过add_props(host=..., location=...)之类的 API 挂载了属性;属性标注的具体方式见 SKILL.md 与 api_reference.md。
折叠节点的特殊表示
draw_node可以接受第二个参数collapsed,从而为折叠状态提供专属外观:
from ete4.smartview import Layout, TextFace def draw_node(node, collapsed): if node.name != "large_clade": return text = "large_clade (collapsed)" if collapsed else "large_clade" return TextFace(text, position="right") layout = Layout("collapsed label", draw_node=draw_node) tree.explore(layouts=[layout])面对数万片叶子时,应当依赖折叠阈值而非试图渲染每个节点的每个 Face——这正是node-height-min/content-height-min存在的意义。从源码看,脚本默认将折叠阈值设为--collapse-pixels(默认 8 像素)、内容最小高度设为--content-pixels(默认 4 像素)(见 quick_visualize.py),并测试确认这些阈值会被原样写入 SmartView 样式字典(见 tests/etetoolkit/test_scripts.py)。
SmartView 静态 PNG 渲染
Tree.render_sm()负责将当前布局渲染为 PNG 截图:
from ete4 import Tree from ete4.smartview import Layout, PropFace tree = Tree("((A:1,B:1),C:1);") def draw_node(node): if node.is_leaf: return PropFace("name", position="right") layout = Layout("leaf labels", draw_node=draw_node) tree.render_sm( "tree.png", layouts=[layout], w=1200, h=800, )三个必须记住的边界条件:
- 在 ETE 4.4.0 中,
render_sm()只捕获 PNG 截图数据,输出文件必须使用.png后缀;传入.svg或.pdf并不会把截图转换为矢量格式。 - 静态 SmartView 渲染需要 Selenium 可用的浏览器。如果浏览器发现失败,请安装兼容的 Chrome/Chromium,或改用 Qt treeview。
- 交互式 SmartView 浏览器可以将其当前视图下载为 SVG/PNG 并导出 Newick——但那是浏览器行为,与
render_sm()无关;render_sm()仅支持 PNG,没有 PDF 模式。
仓库脚本同样强制执行「PNG 专用」:render_smartview()在输出后缀不是.png时直接报错并提示改用--engine treeview(见 quick_visualize.py),对应的测试test_smartview_refuses_a_non_png_destination也验证了这一点(见 tests/etetoolkit/test_scripts.py)。
Qt Treeview:PNG / PDF / SVG 矢量输出
Treeview 的类不是 ETE 4 的顶层导入,必须从ete4.treeview导入。这是一个与 ETE 3 兼容的 API 面:TreeStyle、NodeStyle以及 Qt Face 类仍然沿用,但 ETE 4 的谓词改为属性:
from ete4 import Tree from ete4.treeview import NodeStyle, TextFace, TreeStyle tree = Tree("((A:1,B:1)95:0.2,C:1);", parser="support") for node in tree.traverse(): style = NodeStyle() style["size"] = 6 if node.is_leaf else 4 style["fgcolor"] = "navy" if node.is_leaf else "gray" node.set_style(style) tree_style = TreeStyle() tree_style.show_leaf_name = True tree_style.show_branch_support = True tree_style.show_scale = True tree_style.title.add_face( TextFace("Example phylogeny", fsize=18, bold=True), column=0, ) tree.render( "tree.svg", w=180, units="mm", tree_style=tree_style, ) tree.render( "tree.pdf", w=180, units="mm", tree_style=tree_style, ) tree.render( "tree.png", w=2400, units="px", dpi=300, tree_style=tree_style, )注意写法差异:ETE 4 中必须写node.is_leaf(属性),不能写 ETE 3 时代的node.is_leaf()(方法)。同一份代码中units可取px/mm/in,这是控制期刊插图物理尺寸的关键手段。
环形 Qt 输出
tree_style = TreeStyle() tree_style.mode = "c" tree_style.arc_start = -180 tree_style.arc_span = 180 tree.render("semicircle.svg", tree_style=tree_style)Qt 侧用mode = "c"(circular)对应 SmartView 的"shape": "circular",半圆则通过arc_start/arc_span控制角度范围。
无头(Headless)环境下的 Qt
在无显示器的 Linux 主机上,常见的第一种尝试是:
QT_QPA_PLATFORM=offscreen python render_tree.py如果平台插件或所需共享库不可用,文档给出的替代路径是:改用 SmartView PNG、使用带 Qt 运行时的容器,或在工作站上渲染。仓库脚本的create_treeview_style()同样只依赖 Qt 离屏渲染,并在缺少ete4[treeview]extra 时给出安装提示(见 quick_visualize.py 与对应测试 test_scripts.py)。
仓库配套可视化脚本:quick_visualize.py 实战
仓库为上述全部能力封装了一个命令行入口,脚本位置为 skills/etetoolkit/scripts/quick_visualize.py,通过uv run --with在隔离的固定版本运行时中执行。
交互式 SmartView(不需要任何 extra):
uv run --with "ete4==4.4.0" python scripts/quick_visualize.py \ tree.nw --parser 1SmartView PNG(需要ete4[render-sm]):
uv run --with "ete4[render-sm]==4.4.0" python scripts/quick_visualize.py \ tree.nw tree.png \ --parser support \ --mode circular \ --show-support \ --color-by-support \ --title "Maximum-likelihood tree"Qt SVG 或 PDF(需要ete4[treeview]):
uv run --with "ete4[treeview]==4.4.0" python scripts/quick_visualize.py \ tree.nw tree.svg \ --parser 1 \ --engine treeview \ --mode rectangular \ --title "Species tree"脚本的auto引擎会自动决策:交互式使用与 PNG 走 SmartView,PDF/SVG 走 Qt treeview。这一决策逻辑在 choose_engine() 中实现,并有完整测试覆盖(见 tests/etetoolkit/test_scripts.py)。
脚本还支持一组经过校验的高价值参数(见 quick_visualize.py):
- 解析器:
--parser同时接受数字(如0、1)与命名别名(如support、name); - 布局:
--mode接受rectangular/r与circular/c; - 显示:
--show-names(默认开)、--show-support、--show-lengths、--show-scale、--label-size、--leaf-size、--leaf-color、--internal-color; - 支持值着色:
--color-by-support、--high-support、--moderate-support及三种分档颜色; - SmartView:
--collapse-pixels、--content-pixels、--host、--port、--no-browser、--allow-remote-bind; - 静态输出:
--width、--height、--units(px/mm/in)、--dpi、--arc-start、--arc-span。
参数的取值约束同样被严格校验并测试:支持阈值必须满足0 <= moderate <= high <= 1、尺寸与 DPI 必须为正、端口必须在 1~65535、角度在合法范围内(见 validate_args() 与 ValidateArgsTests)。
发表级插图检查清单
无论走哪套渲染器,将图片投入正式出版前都应逐项核对:
- 需要缩放或二次编辑的线条图优先 SVG/PDF矢量格式;
- 期刊插图使用明确的物理宽度(如
units="mm"+ 指定宽度); - 上色或标注前先核对支持值的量纲(0~1 还是 0~100);
- 使用色盲安全调色板,且不要只依赖颜色传递信息;
- 保证终版印刷尺寸下叶标签可读(注意
fs_min/fs_max/label-size的设置); - 在图注中说明根的位置、分支长度单位与支持值统计量;
- 只导出预期的节点属性(用
show-popup-props或props白名单控制); - 对最终产物本身做测试,而不只是交互式探索器——例如仓库脚本测试会直接检查渲染路径拒绝错误后缀、缺失目录等边界(见 RenderDestinationTests)。
故障排查
SmartView 静态渲染出现ImportError
缺少render-smextra,安装即可:
uv pip install "ete4[render-sm]==4.4.0"Qt 类导入报错
必须先安装 extra 再正确导入:
uv pip install "ete4[treeview]==4.4.0"from ete4.treeview import NodeStyle, TreeStyle树渲染出来却没有预期的名称或支持值
最可能的原因是解析器不匹配。按数据来源重新读取,例如parser="name"或parser="support",并用以下命令检查节点属性是否被正确解析:
print(tree.to_str(props=["name", "dist", "support"], compact=True))这也是 SKILL.md 反复强调的核心点:解析器不匹配是NewickError、内部标签丢失、支持值被当作名称读取的最常见原因。解析器完整对照表见 api_reference.md。
大树的显示呈折叠状态
SmartView 会在分支高度低于node-height-min阈值时自适应折叠。遇到这种情况:在视图中放大,或在布局中调低阈值(脚本对应参数为--collapse-pixels)。从源码看,默认阈值为 8 像素、内容最小高度为 4 像素(见 quick_visualize.py),对包含数万片叶子的大树,可适当下调该值以显示更多细节。
延伸阅读
本技能仓库内还有其他可配合阅读的参考资料与脚本:
- 技能总览 SKILL.md:完整范围、快速上手、树操作/比较/进化事件/分类学查询工作流与脚本清单;
- api_reference.md:ETE 4 核心类、解析器、节点属性、遍历与 I/O 的完整参考;
- workflows.md:完整分析模式、校验、调和、批处理与大树处理;
- taxonomy.md:NCBI/GTDB 分类学数据集的初始化与查询;
- migration-ete3-to-ete4.md:ETE 3 到 ETE 4 的破坏性变更与迁移清单;
- tree_operations.py:统计、ASCII 绘制、转换、重根、剪枝与拓扑比较等树操作脚本;
- tests/etetoolkit/test_scripts.py:本文所述引擎选择、支持值归一化、绑定地址安全与参数校验逻辑的完整测试证据。
【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考