gv(3tcl)

πŸ’– gv(3tcl) πŸ’–

okay bestie, sit down, we're learning gv(3tcl) today and honestly? it's giving main character energy. this is the manual page for the Tcl graph extension, and by the end of it you're gonna be manipulating graphs like it's nothing. no cap. πŸ¦‹


✨ NAME ✨

gv_tcl β€” graph manipulation in tcl. that's it, that's the whole name, short and iconic πŸ’…


🌸 SYNOPSIS 🌸

#!/usr/bin/tclsh
package require gv

look at her. two lines. effortless. πŸ‘‘


πŸŽ€ USAGE πŸŽ€

Requires tcl8.3 or later. yes, or later. don't be out here running ancient tcl and then acting surprised, i'm literally begging πŸ™ˆ


🌈 INTRODUCTION 🌈

gv_tcl is a dynamically loaded extension for tcl that provides access to the graph facilities of graphviz. so like, tcl gets to hang out with graphviz and honestly the friendship is thriving πŸ’–


🫢 COMMANDS 🫢

this is the part where it gets long, bestie. hydrate. we're going through all of it. every single one. periodt.

🧚 New graphs

🌱 New empty graph

<graph_handle> gv::graph <name>
<graph_handle> gv::digraph <name>
<graph_handle> gv::strictgraph <name>
<graph_handle> gv::strictdigraph <name>

four flavours of blank canvas. pick your vibe: plain, directed, strict, or strict-and-directed. lowkey a personality quiz πŸ’œ

πŸ“„ New graph from a dot-syntax string or file

<graph_handle> gv::readstring <string>
<graph_handle> gv::read <string> filename
<graph_handle> gv::read <channel>

got a graph already sitting in a dot string, a filename, or an open channel? gv::read said "say no more". gv::readstring takes the raw string. easy. ✨

πŸͺ† Add new subgraph to existing graph

<graph_handle> gv::graph <graph_handle> <name>

yes it's the same gv::graph command, but now it's nesting. graphs within graphs. it's giving inception, it's giving matryoshka, we love a recursive moment 🎁

πŸ“ New nodes

⭐ Add new node to existing graph

<node_handle> gv::node <graph_handle> <name>

you hand it a graph, you hand it a name, it hands you back a node handle. a little transaction. a little exchange of goods. so cute πŸ₯°

πŸ”₯ New edges

πŸ”— Add new edge between existing nodes

<edge_handle> gv::edge <tail_node_handle> <head_node_handle>

both ends already exist? perfect, we just connect them. slay.

πŸ”— Add a new edge between an existing tail node, and a named head node which will be induced in the graph if it doesn't already exist

<edge_handle> gv::edge <tail_node_handle> <head_name>

you've got the tail already, you just name the head, and if that head isn't in the graph yet β€” boom, it gets induced. it manifests. it shows up uninvited and honestly we respect it πŸ’…

πŸ”— Add a new edge between an existing head node, and a named tail node which will be induced in the graph if it doesn't already exist

<edge_handle> gv::edge <tail_name> <head_node_handle>

same energy, other direction. the tail is the named one this time, and it also gets induced if it doesn't already exist. symmetry. we love to see it 🌸

πŸ”— Add a new edge between named tail and head nodes which will be induced in the graph if they don't already exist

<edge_handle> gv::edge <graph_handle> <tail_name> <head_name>

neither end exists yet? doesn't matter, bestie. give it the graph and two names and both nodes get induced. iconic. no notes. πŸ‘‘

πŸ’œ Setting attribute values

✍️ Set value of named attribute of graph/node/edge - creating attribute if necessary

<string> gv::setv <graph_handle> <attr_name> <attr_value>
<string> gv::setv <node_handle> <attr_name> <attr_value>
<string> gv::setv <edge_handle> <attr_name> <attr_value>

name the attribute, give it a value, and if that attribute didn't exist? gv::setv creates it for you. it's the friend who shows up with snacks you didn't even ask for πŸ“

✍️ Set value of existing attribute of graph/node/edge (using attribute handle)

<string> gv::setv <graph_handle> <attr_handle> <attr_value>
<string> gv::setv <node_handle> <attr_handle> <attr_value>
<string> gv::setv <edge_handle> <attr_handle> <attr_value>

same command, but now you're passing an attribute handle instead of a name, and the attribute has to already exist. glow-up edition πŸ’…

πŸ‘‘ Getting attribute values

πŸ” Get value of named attribute of graph/node/edge

<string> gv::getv <graph_handle> <attr_name>
<string> gv::getv <node_handle> <attr_name>
<string> gv::getv <edge_handle> <attr_name>

you ask by name, you get a <string> back. simple. clean. ✨

πŸ” Get value of attribute of graph/node/edge (using attribute handle)

<string> gv::getv <graph_handle> <attr_handle>
<string> gv::getv <node_handle> <attr_handle>
<string> gv::getv <edge_handle> <attr_handle>

and here you ask with an attribute handle instead. two ways in, same <string> coming out the other side πŸ’–

🏷️ Obtain names from handles

<string> gv::nameof <graph_handle>
<string> gv::nameof <node_handle>
<string> gv::nameof <attr_handle>

you've got a handle but you forgot what it's called? relatable. gv::nameof reminds you. bestie behavior fr fr 🫢

πŸ”Ž Find handles from names

<graph_handle> gv::findsubg <graph_handle> <name>
<node_handle> gv::findnode <graph_handle> <name>
<edge_handle> gv::findedge <tail_node_handle> <head_node_handle>
<attr_handle> gv::findattr <graph_handle> <name>
<attr_handle> gv::findattr <node_handle> <name>
<attr_handle> gv::findattr <edge_handle> <name>

the reverse move: you've got names, you want handles. gv::findsubg, gv::findnode, gv::findedge (that one wants the tail and head node handles, not names, btw πŸ‘€), and gv::findattr which works on a graph, a node, or an edge. finders keepers 🌈

🧭 Misc graph navigators returning handles

<node_handle> gv::headof <edge_handle>
<node_handle> gv::tailof <edge_handle>
<graph_handle> gv::graphof <graph_handle>
<graph_handle> gv::graphof <edge_handle>
<graph_handle> gv::graphof <node_handle>
<graph_handle> gv::rootof <graph_handle>

little handle hopscotch πŸ’« gv::headof and gv::tailof give you the nodes on either end of an edge, gv::graphof climbs up from a graph, an edge, or a node, and gv::rootof takes you all the way to the root. it's giving ancestry.com 🧬

πŸ‘― Obtain handles of proto node/edge for setting default attribute values

<node_handle> gv::protonode <graph_handle>
<edge_handle> gv::protoedge <graph_handle>

the proto node and proto edge! set your defaults on these and every future node/edge inherits the vibe. it's the blueprint. it's the template. it's the mood board ✨

πŸ” Iterators

βœ… Iteration termination tests

<boolean_string> gv::ok <graph_handle>
<boolean_string> gv::ok <node_handle>
<boolean_string> gv::ok <edge_handle>
<boolean_string> gv::ok <attr_handle>

the vibe check of the iterator world 🫑 you get a <boolean_string> back, and it tells you whether to keep going or wrap it up.

🌳 Iterate over subgraphs of a graph

<graph_handle> gv::firstsubg <graph_handle>
<graph_handle> gv::nextsubg <graph_handle> <subgraph_handle>

first one, then keep asking for the next one. classic. no notes.

πŸŒ€ Iterate over supergraphs of a graph (obscure and rarely useful)

<graph_handle> gv::firstsupg <graph_handle>
<graph_handle> gv::nextsupg <graph_handle> <subgraph_handle>

okay the page itself calls this "obscure and rarely useful" so i'm just passing that along πŸ’€ it exists! it's there! it's yours if you want it!

πŸ”— Iterate over edges of a graph

<edge_handle> gv::firstedge <graph_handle>
<edge_handle> gv::nextedge <graph_handle> <edge_handle>

every edge, one at a time. πŸ¦‹

πŸ”— Iterate over outedges of a graph

<edge_handle> gv::firstout <graph_handle>
<edge_handle> gv::nextout <graph_handle> <edge_handle>

the outgoing ones specifically. out with it. πŸ’…

πŸ”— Iterate over edges of a node

<edge_handle> gv::firstedge <node_handle>
<edge_handle> gv::nextedge <node_handle> <edge_handle>

same commands, but the starting point is a node now. yes, gv::firstedge and gv::nextedge again β€” they're flexible like that 😌

πŸ”— Iterate over out-edges of a node

<edge_handle> gv::firstout <node_handle>
<edge_handle> gv::nextout <node_handle> <edge_handle>

out-edges, but from a node. and yep, gv::firstout and gv::nextout do double duty πŸŽ€

🎯 Iterate over head nodes reachable from out-edges of a node

<node_handle> gv::firsthead <node_handle>
<node_handle> gv::nexthead <node_handle> <head_node_handle>

you're not iterating edges, you're iterating the head nodes those out-edges point at. follow the arrows, bestie ➑️

πŸ”— Iterate over in-edges of a graph

<edge_handle> gv::firstin <graph_handle>
<edge_handle> gv::nextin <node_handle> <edge_handle>

in-edges, graph edition. exactly as written, character for character πŸ’œ

πŸ”— Iterate over in-edges of a node

<edge_handle> gv::firstin <node_handle>
<edge_handle> gv::nextin <graph_handle> <edge_handle>

and the node version. also exactly as written 🫢

🎯 Iterate over tail nodes reachable from in-edges of a node

<node_handle> gv::firsttail <node_handle>
<node_handle> gv::nexttail <node_handle> <tail_node_handle>

the mirror image of the head thing: now you're collecting the tail nodes those in-edges come from. 🌸

🌟 Iterate over nodes of a graph

<node_handle> gv::firstnode <graph_handle>
<node_handle> gv::nextnode <graph_handle> <node_handle>

every node in the graph. go forth. ⭐

🌟 Iterate over nodes of an edge

<node_handle> gv::firstnode <edge_handle>
<node_handle> gv::nextnode <edge_handle> <node_handle>

an edge has two nodes and you can walk them. tiny graph, big energy πŸ”₯

🏷️ Iterate over attributes of a graph

<attr_handle> gv::firstattr <graph_handle>
<attr_handle> gv::nextattr <graph_handle> <attr_handle>

all the graph's attributes, handed to you one by one as <attr_handle>s πŸ’–

🏷️ Iterate over attributes of an edge

<attr_handle> gv::firstattr <edge_handle>
<attr_handle> gv::nextattr <edge_handle> <attr_handle>

same deal, edge edition. πŸ¦‹

🏷️ Iterate over attributes of a node

<attr_handle> gv::firstattr <node_handle>
<attr_handle> gv::nextattr <node_handle> <attr_handle>

and node edition. gv::firstattr / gv::nextattr really said "i contain multitudes" πŸ‘‘

πŸ—‘οΈ Remove graph objects

<boolean_string> gv::rm <graph_handle>
<boolean_string> gv::rm <node_handle>
<boolean_string> gv::rm <edge_handle>

yeet. you get a <boolean_string> back so you know how it went. this is the unalive button and it works on graphs, nodes, and edges πŸ’€

🎨 Layout

🧠 Annotate a graph with layout attributes and values using a specific layout engine

<boolean_string> gv::layout <graph_handle> <string> engine

you pick the engine (as a <string>) and it goes and annotates your graph with layout attributes and values. you get a <boolean_string> back. this is where the graph gets its lil outfit on ✨

πŸ–ΌοΈ Render

🎨 Render a layout into attributes of the graph

<boolean_string> gv::render <graph_handle>

no format, no file β€” just render the layout straight into the graph's own attributes. keep it in-house πŸ’…

πŸ–¨οΈ Render a layout to stdout

<boolean_string> gv::render <graph_handle> <string> format

give it a format and it prints to stdout like it owns the place. 🫢

πŸ“ Render to an open file

<boolean_string> gv::render <graph_handle> <string> format
<channel> fout

format plus an already-open <channel> called fout. it writes right into the file you've got open. teamwork! 🀝

πŸ“ Render a layout to an unopened file by name

<boolean_string> gv::render <graph_handle> <string> format
<string> filename

no open channel needed β€” just hand over a filename and it handles the rest. the self-sufficient icon it is πŸ‘‘

πŸ“ Render to a string result

<string> gv::renderresult <graph_handle> ing <string> format
gv::renderresult <graph_handle> <string> format <string> outdata

yes, that first line has an ing in it. that's literally what the page says, so that's what you get β€” i do not make the rules πŸ’€ and the second form takes outdata and gives nothing back, no <string> return on that one, exactly as written πŸŽ€

πŸ“‘ Render to an open channel

<boolean_string> gv::renderchannel <graph_handle> <string> format
<string> channelname

same idea, but you name the channel as a <string> instead of passing the channel itself. naming rights πŸ’–

🧨 Render a layout to a malloc'ed string, to be free'd by the caller

(deprecated - too easy to leak memory)

(still needed for "eval [gv::renderdata $G tk]" )

<string> gv::renderdata <graph_handle> <string> format

okay listen. this one hands you a malloc'ed string and you have to free it. the page says it's deprecated because it is β€” it's too easy to leak memory. 😭 that is not a drill, bestie, that's a genuine memory leak waiting to happen.

but also: it's still needed for "eval [gv::renderdata $G tk]" so it's not going anywhere. complicated queen. we respect her even if we don't fully trust her πŸ™ˆ

πŸ’Ύ Writing graph back to file

<boolean_string> gv::write <graph_handle> <string> filename
<boolean_string> gv::write <graph_handle> <channel>

save your work! by filename, or straight into an open <channel>. <boolean_string> back either way. πŸ’Ύβœ¨

πŸ”„ Graph transformation tools

<boolean_string> gv::tred <graph_handle>

gv::tred β€” a graph transformation tool. that's the one the page gives us, and it returns a <boolean_string> like all the other doers πŸŒ€


πŸ’« KEYWORDS πŸ’«

graph, dot, neato, fdp, circo, twopi, tcl.

(the search terms that got you here. hi. welcome. glad you came. 🫢)


and that's the whole page, bestie!! you made it all the way to the bottom, which honestly deserves a round of applause πŸ‘πŸ‘πŸ‘

this was gv(3tcl), dated 21 March 2026 β€” so if you're reading this and the date on your screen says something else, no you're not, mind your business πŸ’…

now go manipulate some graphs. you're literally so good at this. i'm obsessed. bye!! πŸ’–πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from
  • fedora 44: graphviz-tcl 14.1.4-2.fc44 Β· epl-1.0 AND cpl-1.0 AND bsd-3-clause AND mit AND gpl-3.0-or-later WITH bison-exception-2.2 AND apache-1.1 AND lgpl-2.0-or-later WITH libtool-exception AND smlnj AND hpnd-uc
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms