若要在 Mac 上讓 Graphviz 階層看起來像縮排大綱,請為每個節點指定列與縮排座標,然後使用 neato -n2 進行繪製。這樣可以保留父子邊,同時讓你控制閱讀順序。
本指南會建立一個六節點範例並匯出 PDF。目標很明確:每行一個節點、子孫節點緊接在其父節點下方,而且相同深度的節點縮排相同。Graphviz 說明了如何透過 -n 命令列選項使用固定位置進行繪製。
為什麼一般的 Graphviz 樹狀圖看起來不像大綱?
階層描述的是關係;縮排大綱還會指定閱讀順序。在這個範例中,根節點包含兩個分支,而第一個分支的子孫節點必須排在第二個分支之前。目標順序如下:
Node R
Node A
Node C
Node D
Node B
Node E
圖中的關係是根節點到 A、根節點到 B、A 到 C、A 到 D,以及 B 到 E。D 和 B 之間沒有關係,不會只因為 B 出現在下一行就產生關係。DOT 使用 -> 運算子表示有向邊;節點宣告與其呈現屬性是語言中彼此分開的部分。請參閱官方的 DOT 語言參考資料。
Graphviz 的 dot 引擎會產生階層式或分層式圖形。它會嘗試讓邊朝向共同方向,同時減少交叉和邊的長度。這項設計適合分支圖,但不會定義上方所示的每列一個節點的大綱。請參閱 dot 配置說明文件。
幾個常見屬性只能解決部分問題:
| 設定 | 說明文件記載的用途 | 尚未解決的部分 |
|---|---|---|
rankdir=LR |
將配置方向改為由左至右 | 未指定垂直大綱順序 |
rank=same |
將子圖中的節點放在同一層級 | 未為每個節點指定專屬的大綱列 |
ordering=out |
限制輸出邊依輸入順序由左至右顯示 | 未定義依深度縮排及列座標 |
這些界線可從 rankdir、rank 和 ordering 的官方參考資料中確認。若要產生本文所示的特定列排列,明確指定座標比組合這些屬性更直接。
如何在 Mac 上準備 Graphviz?
開啟 Terminal。如果你已經在使用 Homebrew,請執行以下命令安裝 Graphviz。官方的 Graphviz 下載頁面列出了這種 Mac 安裝方式:
brew install graphviz
如果你使用 MacPorts,該下載頁面也列有 sudo port install graphviz。選擇你已在使用的套件管理工具。如果兩者都尚未安裝,請先依照該頁面上的相關連結安裝,再繼續本指南。
確認繪製工具可供使用:
neato -V
-V 選項會顯示 Graphviz 版本資訊。比較不同電腦上的結果時,請記下顯示的版本;本文不要求特定版本號。Graphviz 在其命令列參考資料中說明了這個選項。
如何建立縮排階層?
以下完整的 shell 區塊會建立新的工作資料夾,並在其中寫入 DOT 檔案。請在 Terminal 中執行;完成後會印出資料夾路徑。加引號的 heredoc 分隔符會保留原樣的 DOT 文字。
outline_dir=$(mktemp -d "$HOME/graphviz-outline.XXXXXX")
if [ -n "$outline_dir" ] && cd "$outline_dir"; then
cat > hierarchy.dot <<'DOT'
digraph Hierarchy {
graph [splines=ortho, bgcolor=white];
node [
shape=box,
width=1.2,
height=0.35,
fixedsize=true,
fontname="Courier",
fontsize=12
];
r [label="Node R", pos="100,300"];
a [label="Node A", pos="136,252"];
c [label="Node C", pos="172,204"];
d [label="Node D", pos="172,156"];
b [label="Node B", pos="136,108"];
e [label="Node E", pos="172,60"];
r -> a;
r -> b;
a -> c;
a -> d;
b -> e;
}
DOT
pwd
fi
節點位置編碼了大綱結構。每往下一列就向下移動 48 點,每增加一層深度就向右移動 36 點。這些數值只是示範用的間距選擇,並非 Graphviz 預設值。pos 說明文件指出,節點位置以節點中心為基準;搭配 -n 使用時,neato 會將位置解讀為點。
| 節點 | 深度 | 列,從零開始 | 中心位置 |
|---|---|---|---|
| Node R | 0 | 0 | 100,300 |
| Node A | 1 | 1 | 136,252 |
| Node C | 2 | 2 | 172,204 |
| Node D | 2 | 3 | 172,156 |
| Node B | 1 | 4 | 136,108 |
| Node E | 2 | 5 | 172,60 |
在這個配置中,座標規則是 x = 100 + 36 × depth 和 y = 300 − 48 × row。注意從 Node D 回到 Node B:列數增加了,但深度降低了。正是這個轉換,讓圖形讀起來像大綱,而非一條鏈。
所有方框尺寸都相同,因此中心座標相同的節點,其左緣也會對齊。等長的示意標籤也讓文字位置容易比較。使用 fixedsize=true 時,Graphviz 會採用指定的節點尺寸,而不會為了容納內容而放大方框;標籤過大時會產生警告。請參閱 fixedsize 參考資料。
如何繪製並檢查結果?
在同一個 Terminal 工作階段和工作資料夾中,執行:
neato -n2 -Tpdf hierarchy.dot -o hierarchy.pdf
在這裡,-n2 會保留提供的節點排列,不進行重疊調整,並為沒有自訂位置的邊計算路徑。因此,每個節點都必須有 pos 值。-Tpdf 和 -o 選項分別用來選擇 PDF 輸出格式與目的地。這些行為記載於命令列參考資料。
在 Finder 中開啟工作資料夾,再開啟 hierarchy.pdf。PDF 輸出需要包含 Cairo 繪製器的 Graphviz 組建,詳情見 Graphviz 的 PDF 輸出頁面。本範例已依據說明文件核對,但由於研究環境中沒有 Graphviz,因此未在此進行繪製測試。
分別檢查以下三點:
- 由上往下讀:R、A、C、D、B、E 應該各占一列,依序排列。
- 比較縮排:A 和 B 應該對齊;C、D 和 E 應該再往右對齊一格。
- 追蹤關係:R 連到 A 和 B;A 連到 C 和 D;B 連到 E。
前兩項檢查的是座標配置;第三項檢查實際圖形。將這些檢查分開,有助於發現看似整齊、卻包含錯誤父子邊的圖形。
連接線會和檔案瀏覽器中的樹狀結構一模一樣嗎?
節點列與縮排是明確指定的;連接線路徑仍由系統計算。範例設定了 splines=ortho,會讓邊沿著軸對齊的線段繞行。Graphviz 說明文件也指出,這種繞行方式不支援連接埠,因此加入羅盤方向連接埠並不能作為強制指定特定轉折圖樣的文件依據。請參閱 splines 參考資料。
因此,這種配置會產生帶有自動繞行直角連接線的縮排階層,但不會指定一條共用的垂直主幹及精確放置的分支交會點。若連接線幾何形狀至關重要,就需要另外建立明確指定邊路徑的配置;單靠 splines=ortho 無法表達這項需求。
若要建立不顯示連接線的乾淨大綱,請將圖形設定改為 splines=none,然後重新繪製。DOT 檔案仍保留父子邊,但繪製器會省略邊線。這是同一份 splines 參考資料中記載的繪製選項。
如何調整範例而不改變其意義?
先獨立於邊清單,寫下預期的閱讀順序。先讀父節點,再讀它的第一個子節點及其子孫節點,之後才移至下一個子節點。依照這個順序為每個節點指定一列,再依深度指定縮排。
例如,若要在 Node D 之後為 Node A 新增另一個子節點,請將它插入閱讀順序中,排在 Node B 之前。指定深度為二,將後續列向下移動,並新增一條從 A 指向新節點的邊。列號變更本身不會改變既有的父子關係。
編輯後若結果看起來不對,請依照以下順序檢查:
- 圖形散開成不同排列: 確認命令仍使用
neato -n2。範例依賴指定的位置,而非一般的自動配置。 - 繪製時回報缺少位置: 檢查每個節點 ID,包括只出現在邊敘述中的 ID。使用這種工作流程時,每個節點都需要有指定位置的宣告。
- 標籤超出方框: 加大所有方框的尺寸,或縮短標籤。固定尺寸的方框不會自動放大。
- 放大方框後列與列重疊: 增加列間距。所選繪製模式會保留位置,不會自動修正重疊。
- 分支連到錯誤的節點: 修正邊敘述。移動方框無法修正錯誤的關係。
缺少位置與重疊的情況,符合 Graphviz 說明文件記載的 -n2 行為;標籤相關情況則符合其 fixedsize 行為。對這個手動定位的範例而言,增加間距是相應的設計調整方式。
以階層的邊清單作為關係的來源,以座標表作為呈現方式的來源。對於小型 Mac 桌面圖來說,分開維護兩者會更直接:階層變更時修改邊,閱讀順序變更時修改列,標籤需要更多空間時修改間距。