visualization.md
537 lines · skills/etetoolkit/references/visualization.md at 5f2bc03
Most marks come from matching words in the file, so treat them as pointers.
ETE 4 Visualization
ETE 4 has two drawing systems:
- SmartView: current web-based explorer and adaptive renderer; best for interactive work and very large trees
- Treeview: optional Qt renderer retained for static PNG, PDF, and SVG
Do not mix their layout or face classes. SmartView classes live under ete4.smartview; Qt classes live under ete4.treeview.
Renderer Decision
Use SmartView when:
- Exploring, searching, collapsing, or editing interactively
- Serving a tree locally or through an SSH tunnel
- Exploring from a long-lived Jupyter kernel through the browser
- Creating a raster PNG screenshot
- Manually downloading the current browser view as SVG or PNG
- Working with a tree too large to draw fully expanded
Use Qt treeview when:
- Programmatic or headless output must be PDF or SVG
- Physical drawing dimensions or raster DPI matter
- Maintaining an existing ETE treeview layout
Installation
Interactive SmartView is included in the base package:
uv pip install "ete4==4.4.0"Static SmartView screenshots use Selenium:
uv pip install "ete4[render-sm]==4.4.0"Qt rendering uses PyQt6:
uv pip install "ete4[treeview]==4.4.0"Raw lines 29 to 48Interactive SmartView
Raw lines 49 to 50Python
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()With no layouts argument, ETE applies BASIC_LAYOUT, which displays leaf names, branch lengths, and support.
explore() returns immediately. A standalone script must keep the process alive, for example with input(), or use keep_server=True. The bundled visualization helper waits and stops the server cleanly.
In Jupyter/IPython, SmartView is still browser-based; the current documentation does not provide an inline SmartView widget. Keep the kernel alive and open the served URL.
Raw lines 51 to 74Command line
ete4 explore -t tree.nw --src_tree_format 1Inspect the active release's full options:
ete4 explore --helpThe CLI supports basic source-tree selection and parser options. Although ETE 4.4.0 help exposes --face, its handler does not apply that argument; use Python Layout objects for customization.
Control the server
tree.explore(
host="127.0.0.1",
port=5000,
open_browser=False,
)Keep the default loopback binding unless remote access is intentionally secured. For a remote host, tunnel the loopback port:
ssh -L 5000:localhost:5000 user@remote-hostThen open http://localhost:5000 locally. Do not expose an unauthenticated explorer on 0.0.0.0 to an untrusted network.
SmartView Layouts
A SmartView Layout combines:
draw_tree(tree): tree-wide style and header/legend facesdraw_node(node[, collapsed]): styles and faces for individual nodesname: GUI identifiercache_size: memoization control for node drawing
Circular tree with labels and support
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])Raw lines 120 to 171Conditional node styling
Support values may be fractions or percentages. Set one explicit convention for the entire source; a value of 1 cannot disambiguate them:
import math
from ete4 import Tree
from ete4.smartview import Layout, PropFace
tree = Tree("((A:1,B:1)95:0.2,C:1);", parser="support")
SUPPORT_MAX = 100 # use 1 only for a verified fractional source
def support_fraction(value):
if value is None:
return None
if not math.isfinite(value) or not 0 <= value <= SUPPORT_MAX:
raise ValueError("Support is outside the declared source range")
return value / SUPPORT_MAX
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])Raw lines 172 to 220Tree style keys
Common tree-wide keys:
shape:"rectangular"or"circular"radius: circular layout radiusangle-start,angle-end,angle-span: circular extentnode-height-min: collapse threshold in pixelscontent-height-min: minimum height before faces appearcollapsed: collapsed-node styleshow-popup-props,hide-popup-props: property disclosureis-leaf-fn: dynamic terminal-node rulebox,dot,hz-line,vt-line: default CSS-like styles
Example:
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])Limit popup properties when nodes contain sensitive or irrelevant metadata.
Raw lines 221 to 255SmartView Faces
Common classes from ete4.smartview:
TextFace: literal textPropFace: one node property with optional formattingEvalTextFace: expression-derived textCircleFace,RectFace,BoxFace: shapesImageFace: imageSeqFace: molecular sequenceLegendFace: legend
Positions include top, bottom, left, right, and aligned; tree-level text can also use header.
Aligned metadata columns
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])Raw lines 271 to 288Collapsed-node behavior
For a special collapsed representation, accept a second argument:
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])Use collapse thresholds instead of trying to render every face for tens of thousands of leaves.
Raw lines 289 to 310SmartView Static 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,
)In ETE 4.4.0, render_sm() captures PNG screenshot data. Give the output a .png suffix. Supplying .svg or .pdf does not convert the screenshot to a vector format.
ETE 4.4.0 warns and returns without creating a file when Selenium is absent; that return is not successful rendering. The bundled helper checks the dependency and requires a PNG signature before replacing the destination.
SmartView header faces occupy the aligned panel; a long title at a narrow viewport can be clipped. Shorten the title or increase the width, then inspect the image at its final size.
Static SmartView rendering needs a browser usable by Selenium. If browser discovery fails, install a compatible Chrome/Chromium browser or use Qt treeview.
The interactive SmartView browser can download its current view as SVG or PNG and export Newick. That browser action is distinct from render_sm(), which is PNG-only and has no PDF mode.
Qt Treeview for PNG/PDF/SVG
Treeview classes are not top-level ETE 4 imports.
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.4.0's Qt PDF path selects A4 paper. Width/height control the drawing; they do not set a tightly cropped PDF page. Inspect the PDF page bounds and crop in a separate graphics/PDF tool if needed, or use SVG for a tight vector canvas. The local PDF smoke confirmed the A4 page and its surrounding whitespace.
Qt treeview still uses TreeStyle, NodeStyle, and Qt face classes, but ETE 4 predicates remain properties:
if node.is_leaf:
...not:
if node.is_leaf():
...Raw lines 354 to 419Circular Qt output
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)Raw lines 420 to 429Headless Qt
On a headless Linux host, a common first attempt is:
QT_QPA_PLATFORM=offscreen python render_tree.pyIf the platform plugin or required shared libraries are unavailable, use SmartView PNG, a container with the Qt runtime, or render on a workstation.
Raw lines 430 to 440Bundled Visualization Script
Interactive SmartView:
uv run --no-project --isolated --with "ete4==4.4.0" python scripts/quick_visualize.py \
tree.nw --parser 1SmartView PNG:
uv run --no-project --isolated --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 --support-scale percent \
--title "Maximum-likelihood tree"Qt SVG or PDF:
uv run --no-project --isolated --with "ete4[treeview]==4.4.0" python scripts/quick_visualize.py \
tree.nw tree.svg \
--parser 1 \
--engine treeview \
--mode rectangular \
--title "Species tree"Support coloring requires --support-scale percent (0–100) or fraction (0–1). Thresholds remain fractions in both cases. Invalid or nonfinite support values fail before rendering.
The script's auto engine chooses SmartView for interactive use and PNG, and Qt treeview for PDF/SVG.
Publication Checklist
- Prefer SVG/PDF for line art that will be resized or edited.
- Use explicit physical width for journal figures.
- Check support scale before mapping colors or labels.
- Use a colorblind-safe palette and do not rely on color alone.
- Keep tip labels legible at final print size.
- State rooting, branch-length units, and support statistic in the caption.
- Export only intended node properties.
- Test the exact artifact, not just the interactive explorer.
Troubleshooting
Raw lines 491 to 492ImportError for SmartView static rendering
uv pip install "ete4[render-sm]==4.4.0"Raw lines 493 to 498ImportError for Qt classes
Use:
from ete4.treeview import NodeStyle, TreeStyleafter:
uv pip install "ete4[treeview]==4.4.0"Raw lines 499 to 512Tree renders without expected names or support
The likely cause is the parser. Re-read using parser="name" or parser="support" as appropriate and inspect:
print(tree.to_str(props=["name", "dist", "support"], compact=True))Raw lines 513 to 521Large tree appears collapsed
SmartView adaptively collapses branches below node-height-min. Zoom in or lower the threshold in a layout.
Upstream References
- SmartView tutorial: https://etetoolkit.github.io/ete/tutorial/tutorial_smartview.html
- SmartView API: https://etetoolkit.github.io/ete/reference/reference_smartview.html
- Qt treeview tutorial: https://etetoolkit.github.io/ete/tutorial/tutorial_treeview.html
- Qt treeview API: https://etetoolkit.github.io/ete/reference/reference_treeview.html
- ETE Gallery: https://github.com/etetoolkit/ete-gallery