Some observations/issues with the output window. Come join me down the rabbit hole

Default display monitor:
The output window will display on the monitor set as default in the windows display settings

If the default monitor is a different size or resolution from the monitor displaying Revit, then setting output.center() will use the dimensions of the monitor showing Revit and not the monitor the output is actually drawn on, meaning it will be drawn off center.
Monitor 1 with Revit, Monitor 2 default, different size/resolution:

Monitor 1 with Revit and default:

Is there a way to specify the monitor I want to display the output window on? I use 3 external monitors at work and some people even use the laptop as a 4th, and its not uncommon for me to have 3 versions of Revit open simultaneously in different windows, and it gets really confusing when the output for all three get added to the default screen instead of with the Revit they belong to. Using Windows 11 at home, and windows 10 at work, both with same behavior.

Error when used with updater
I’m also running into errors that appear to be caused by a doc-changed updater I’m using and I’m not sure why.


checking the lines indicated in init.py show it is the output.center(). I can comment out this line and use the updater, and no error. I can disable the updater and re enable this line in the script, and no error.
image

Here is the code from the doc-updater.py. Def badly in need of refactoring and cleanup since this is several scripts smooshed together basically, but nothing I would expect to interfere the the output windows ability to center itself.

# doc-updater.py (complete)

from pyrevit import forms, script, revit, DB, HOST_APP, EXEC_PARAMS

sender = __eventsender__
args = __eventargs__

def set_param_if_empty(parameter, new_value):
    if parameter and parameter.AsString() != new_value:
        parameter.Set(new_value)

def set_rod_host_parameters(part, rod_info):
    doc = args.GetDocument()
    rod_count = rod_info.RodCount
    hostelementid_params = [part.LookupParameter("ACCO_HostElementId"), part.LookupParameter("ACCO_HostElementId 2")]
    linkedelementid_params = [part.LookupParameter("ACCO_LinkedElementId"), part.LookupParameter("ACCO_LinkedElementId 2")]
    linkedinstanceid_params = [part.LookupParameter("ACCO_LinkInstanceId"), part.LookupParameter("ACCO_LinkInstanceId 2")]
    linkedinstancename_params = [part.LookupParameter("ACCO_Rod Host Linked Model Name"), part.LookupParameter("ACCO_Rod Host Linked Model Name 2")]
    rod_host_element_category_params = [part.LookupParameter("ACCO_Rod Host Element Category"), part.LookupParameter("ACCO_Rod Host Element Category 2")]
    elements_with_unattached_rods = []
    for i in range(rod_count):
        try:
            rod_host = rod_info.GetRodAttachedElementId(i)
            if rod_host.HostElementId != DB.ElementId.InvalidElementId:
                host_doc = doc
            else:
                link_instance = doc.GetElement(rod_host.LinkInstanceId) if rod_host.LinkInstanceId != DB.ElementId.InvalidElementId else None
                if link_instance:
                    host_doc = link_instance.GetLinkDocument()
                    if linkedinstancename_params[i]:
                        # print(host_doc.Title)
                        try:
                            linkedinstancename_params[i].Set(host_doc.Title)
                        except Exception:
                            linkedinstancename_params[i].Set("Link name not found. Is the link unloaded?")

            if host_doc:
                linked_element = host_doc.GetElement(rod_host.LinkedElementId) if rod_host.LinkedElementId != DB.ElementId.InvalidElementId else None
                if not linked_element:
                    linked_element = host_doc.GetElement(rod_host.HostElementId) if rod_host.HostElementId != DB.ElementId.InvalidElementId else None
                if linkedinstanceid_params[i]:
                    linkedinstanceid_params[i].Set(str(rod_host.LinkInstanceId.IntegerValue) if rod_host.LinkInstanceId != DB.ElementId.InvalidElementId else "-1")
                if linkedelementid_params[i]:
                    linkedelementid_params[i].Set(str(rod_host.LinkedElementId.IntegerValue) if rod_host.LinkedElementId != DB.ElementId.InvalidElementId else "-1")
                if hostelementid_params[i]:
                    hostelementid_params[i].Set(str(rod_host.HostElementId.IntegerValue) if rod_host.HostElementId != DB.ElementId.InvalidElementId else "-1")
                if linked_element and linked_element.Category and rod_host_element_category_params[i]:
                    rod_host_element_category_params[i].Set(linked_element.Category.Name)
                elif rod_host_element_category_params[i]:
                    rod_host_element_category_params[i].Set("-1")
            else:
                elements_with_unattached_rods.append(part)
        except Exception:
            elements_with_unattached_rods.append(part)

    return elements_with_unattached_rods

def main():
    doc = args.GetDocument()
    modified_el_ids = args.GetModifiedElementIds()
    modified_el = [doc.GetElement(e_id) for e_id in modified_el_ids]
    not_allowed_cids = 1238
    if modified_el:
        allowed_cats = [DB.ElementId(DB.BuiltInCategory.OST_FabricationHangers)]
        for el in modified_el:
            if el.Category.Id in allowed_cats and el.ItemCustomId != not_allowed_cids:
                p1 = el.LookupParameter("ACCO_Pipe_Id")
                p2 = el.LookupParameter("ACCO_Pipe_Unique_Id")
                host_info = el.GetHostedInfo()
                rod_info = el.GetRodInfo()
                if rod_info:
                    set_rod_host_parameters(el, rod_info)
                if host_info:
                    host_id = host_info.HostId
                    if host_id:
                        host_elem = doc.GetElement(host_id)
                        if host_elem:
                            host_elem_unique_id = host_elem.UniqueId
                            set_param_if_empty(p1, str(host_id))
                            set_param_if_empty(p2, host_elem_unique_id)

if __name__ == '__main__':
    main()

Here is the output part of a script causing this error, although it affects any script where output.cetner() is used:

# output part of any script.py

    output = script.get_output()
    output.center()
    output.close_others(all_open_outputs=True)

    if elements_with_invalid_rods:
        mlogger.warning("Invalid rod size")
        for e in elements_with_invalid_rods:
            link_message = output.linkify(e, "Select or zoom to hanger: {}".format(e))
            print(link_message)
        mlogger.warning("Invalid rod size")
        results.invalid_rods = ("Invalid rod size: {}").format(len(elements_with_invalid_rods))
        
    if elements_with_rods_not_hosted:
        if elements_with_invalid_rods:
            print("\n")
        
    if elements_with_rods_not_hosted:
        mlogger.warning("Rods not attached to structure")
        for e in elements_with_rods_not_hosted:
            link_message = output.linkify(e, "Select or zoom to rod: {}".format(e))
            print(link_message)
        mlogger.warning("Rods not attached to structure")
        results.rods_not_hosted = ("Rods not attached to structure: {}").format(len(elements_with_rods_not_hosted))
        
    if elements_with_rods_not_hosted or elements_with_invalid_rods:
        if unattached_hangers:
            print("\n")
    
    if unattached_hangers:
        mlogger.warning("Hangers not attached to pipe")
        for e in unattached_hangers:
            link_message = output.linkify(e, "Select or zoom to hanger: {}".format(e))
            print(link_message)
        mlogger.warning("Hangers not attached to pipe")
        results.hangers_not_hosted = ("Hangers not attached to pipe: {}").format(len(unattached_hangers))

    if elements_with_invalid_rods or elements_with_rods_not_hosted or unattached_hangers:
        output.show()

if __name__ == '__main__':
    main()

no output window is displayed using the code about, but if I comment and move the center() next to show() then it will both show the output window and display the same error, and you can see the updater has intercepted the output window somehow and now shows the name of that file at the top of the window.

# same as output of script.py above with center() moved next to show

    if elements_with_invalid_rods or elements_with_rods_not_hosted or unattached_hangers:
        output.center() # commented out in original location above
        output.show()

Am I doing something wrong or is this a bug?

Nobody has run into the updater hijacking the output window before?

nope, but I haven’t used updaters in a while
Also I tend to prefer doc-changed event which is more stable/safe