Pyrevit.output table formatting


I am adding multiple tables to the output window, and the formatting seems to be off. How can I get the ‘Instance Count’ columns lined up? See image for reference.

Anyone have any feedback as to why this formatting isn’t consistent?

Can you show the code and also the data you are feeding to the print_table with a print(data) statement?
It tools me a while to get the hang of the data structure that feeds the table. Usually fixed by either transposing the data or filing in the gaps in the dataset

Sure, the code is straight forward and in alignment with the examples that I was able to find:
output = script.get_output()
output.print_table(table_data = data,
title = tabletitle + " Category, Total Families Found: " + str(famCount) + " >>>",
columns = [“Family Name”, “Instance Count”],
formats = [‘’, ‘’])

I have also included another image of the output. I suspect that the width of the strings in the ‘Family Name’ column has something to do with where the ‘Instance Count’ column starts. Is there a way to “hard code” those column widths in order to maintain consistency?

Hello again, any thoughts?

thinking out loud about an easy fix:

  • adding a empty column between the fam name and the instance count columns
  • or reducing the output window width

Neither of the suggestions offered resolved or changed the output/display.

Here’s some sample code I used to look at this problem.

Basically, because pyrevit prints out all the text as html, we add “&nbsp” (non-breaking space) to the end of all text entries.
We add as many as we need to match the length of the longest string of all the tables. This ensures all the values have the same “length” including white space, so all the tables have the same formatting.
There may be a better way but I don’t want to spend that much time on it.
Hope this helps.

from pyrevit import script
from pyrevit.revit import DB, HOST_APP

doc = HOST_APP.doc

def format_value(value, max_length):
    value = str(value)
    return value + (" " * (max_length - len(value)))

def format_data(data, max_length):
    return [[format_value(value, max_length) for value in item] for item in data]

def get_families_by_category(category):
    category_id = DB.ElementId(category)
    return [fam for fam in DB.FilteredElementCollector(doc).OfClass(DB.Family) if fam.FamilyCategory.Id == category_id]

def get_instance_count(family):
    return len([element for element in DB.FilteredElementCollector(family.Document).OfClass(DB.FamilyInstance).WhereElementIsNotElementType() if element.Symbol.Family.Id == family.Id])

def get_data_by_category(category):
    families = get_families_by_category(category)
    data = {}
    for family in families:
        if len(family.Name) > MAX_STRING_LENGTH:
            MAX_STRING_LENGTH = len(family.Name)
        data[family.Name] = get_instance_count(family)
    return len(families), [[key, value] for key, value in data.items()]

plumb_fixture_family_count, plumb_fixture_data = get_data_by_category(DB.BuiltInCategory.OST_PlumbingFixtures)
mech_equipment_family_count, mech_equipment_data = get_data_by_category(DB.BuiltInCategory.OST_MechanicalEquipment)

column_headers = [format_value("Family Name", MAX_STRING_LENGTH), format_value("Instance Count", MAX_STRING_LENGTH)]

plumb_fixture_data = format_data(plumb_fixture_data, MAX_STRING_LENGTH)
mech_equipment_data = format_data(mech_equipment_data, MAX_STRING_LENGTH)

output = script.get_output()
output.print_table(table_data = plumb_fixture_data,
title = "PLUMBING FIXTURE Category, Total Families Found: {} >>>".format(plumb_fixture_family_count),
columns = column_headers)

output.print_table(table_data = mech_equipment_data,
title = "MECHANICAL EQUIPMENT Category, Total Families Found: {} >>>".format(mech_equipment_family_count),
columns = column_headers)

Has anyone managed to solve it?

Hi, the kind of conttrol you need isn’t possible with the current implementation of the print_table method.
This is because it builds a markdown table that then gets converted to html, but markdown tables don’t have any control over the columns size.

One way to solve this would be create a new method to directly output the html table, and expose some parameter to let set fixed widths to each column.

Anyone wants to try to tackle this? If you’re not confident with git, you could just write a function and share it here.

It should be easy using string templates:

HEADER = '<th style="width:{width}">{title}</th>'
CELL = "<td>{}</td>"
ROW = "<tr>{}</tr>"
TABLE = "<table><theader>{}</theader><tbody>{}</tbody></table>"

def row(cells):
    return ROW.format("".join(cells))

headers = row(HEADER.format(**column) for column in header_config)
rows = "".join(row(CELL.format(data) for data in row_data) for row_data in table_data)
table = TABLE.format(headers, rows)