Column of Pretty Tables tables and a plot to a png file

Hello,
Does anyone know how to write a column of, say, three small Pretty Tables tables followed by a plot to a png file with mankie.jl or with some other tool?
Thanks

You mean you want to put a table inside a Makie plot? I once made this but didn’t merge it Makie table extension by jkrumbiegel Β· Pull Request #24 Β· PumasAI/SummaryTables.jl Β· GitHub

But in principle, yeah, you can build a table with GridLayout, Label and Box

using CairoMakie
table_text = DataFrame(
    Category = ["All Counties", "Served Counties", "Served Population"],
    Count = [all_counties, served_counties, served_population],
    Percentage = ["100%", percentage_counties_served, percentage_served_population]
)
io = IOBuffer()
pretty_table(io, table_text; backend=:text, alignment=[:l, :r, :r], show_first_column_label_only = true)
str_table = String(take!(io))

Then just place it in a Figure object.

f = Figure()
ax = Axis()
Label(axis, str_table, font="IBM Plex Mono", fontsize=10, justification=:left, halign=:left, valign=:bottom)
save("output.png", f)

Use GridLayout() to arrange multiple tables.

4 Likes

This is a cool idea. @Ronis_BR if someone wanted to make a Makie backend for PrettyTables.jl is there a certain list of methods that need to be implemented?

I have no idea! However, PrettyTables.jl renders to HTML, would it suffice to embed the HTML table inside the Makie plot?

Hi @technocrat !

This was an awesome use for PrettyTables.jl! If you do not want to use DataFrames.jl, you can simplify the code as follows:

data = [
    "All Counties"      all_counties      "100%"
    "Served Counties"   served_counties   served_population
    "Served Population" served_population percentage_served_population
]

column_labels = ["Category", "Count", "Percentage"]

str_table = pretty_table(String, table_text; alignment = [:l, :r, :r], column_labels = column_labels)
2 Likes

I wonder if there could be an interface for PrettyTables backends, so that users could write extensions for their own targets by just implementing a certain well-defined set of things. I can imagine people wanting backends for any number of document formats.

The internal structure is not very organized right now to allow for external back ends. This is something I plan to do eventually.

2 Likes

@Ronis_BR Great β€œless is more improvement.” I tried to do it without PrettyTable.jl either, but had to rely on TOML.Parser().

headers = ["Name", "Age", "City"]
rows = [["Alice", "25", "New York"], 
        ["Bob", "30", "Chicago"],
        ["Carol", "22", "Boston"]]
table = format_table_as_text(headers, rows)
println(table)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Name   β”‚Age β”‚City      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚Alice  β”‚25  β”‚New York  β”‚
β”‚Bob    β”‚30  β”‚Chicago   β”‚
β”‚Carol  β”‚22  β”‚Boston    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β€œβ€"
format_table_as_text(headers::Vector{String}, rows::Vector{Vector{String}}, padding::Int=2)

Format data as an ASCII table with borders and proper column alignment.

# Arguments

  • headers::Vector{String}: Column header names
  • rows::Vector{Vector{String}}: Data rows, where each row is a vector of strings
  • padding::Int=2: Additional padding space around each cell (default: 2)

# Returns

  • String: Formatted ASCII table with Unicode box-drawing characters
    # Examples
headers = ["Name", "Age", "City"]
rows = [["Alice", "25", "New York"], 
        ["Bob", "30", "Chicago"],
        ["Carol", "22", "Boston"]]
table = format_table_as_text(headers, rows)
\# Returns a formatted table with borders and proper alignment
\# Notes
- Uses Unicode box-drawing characters (β”Œβ”€β”¬β”€β”β”‚β”œβ”€β”Όβ”€β”€β””β”€β”΄β”€β”˜)
- Automatically calculates column widths based on content
- Each cell is padded for consistent spacing
- Useful for creating publication-ready ASCII tables
"""
function format_table_as_text(headers::Vector{String}, rows::Vector{Vector{String}}, 
    padding::Int=2)
    parser = Parser()
    all_rows = [headers; rows]

    # Calculate column widths
    col_widths = Int[]
    for col in 1:length(headers)
    max_width = maximum(length(row[col]) for row in all_rows)
    push!(col_widths, max_width + padding)
    end

    # Format rows
    formatted_lines = String[]

    # Header
    header_line = join([rpad(headers[i], col_widths[i]) for i in 1:length(headers)], "β”‚")
    push!(formatted_lines, "β”‚" * header_line * "β”‚")

    # Separator
    separator = "β”œ" * join([repeat("─", col_widths[i]) for i in 1:length(headers)], "β”Ό") * "─"
    push!(formatted_lines, separator)

    # Data rows
    for row in rows
    data_line = join([rpad(row[i], col_widths[i]) for i in 1:length(row)], "β”‚")
    push!(formatted_lines, "β”‚" * data_line * "β”‚")
    end

    # Top and bottom borders
    top_border = "β”Œ" * join([repeat("─", col_widths[i]) for i in 1:length(headers)], "┬") * "┐"
    bottom_border = "β””" * join([repeat("─", col_widths[i]) for i in 1:length(headers)], "β”΄") * "β”˜"

    return join([top_border, formatted_lines..., bottom_border], "\n")
end

export format_table_as_text