Skip to content

uimaker

UI maker.

Attributes

mlogger = get_logger(__name__) module-attribute

CONFIG_SCRIPT_TITLE_POSTFIX = '●' module-attribute

current_ui = ribbon.get_current_ui() module-attribute

Classes

UIMakerParams(par_ui, par_cmp, cmp_item, asm_info, create_beta=False)

UI maker parameters.

Parameters:

Name Type Description Default
par_ui _PyRevitUI

Parent UI item

required
par_cmp GenericUIComponent

Parent UI component

required
cmp_item GenericUIComponent

UI component item

required
asm_info AssemblyInfo

Assembly info

required
create_beta bool

Create beta button. Defaults to False

False
Source code in pyrevitlib/pyrevit/loader/uimaker.py
def __init__(self, par_ui, par_cmp, cmp_item, asm_info, create_beta=False):
    self.parent_ui = par_ui
    self.parent_cmp = par_cmp
    self.component = cmp_item
    self.asm_info = asm_info
    self.create_beta_cmds = create_beta

Attributes

parent_ui = par_ui instance-attribute
parent_cmp = par_cmp instance-attribute
component = cmp_item instance-attribute
asm_info = asm_info instance-attribute
create_beta_cmds = create_beta instance-attribute

Functions

setup_combobox(combobox, ui_item, uiapp, script_globals)

Set up ComboBox event handlers from script globals.

This function looks for special global variables in the script and wires them up as event handlers for the ComboBox.

Recognized global variables
  • selfinit(component, ui_item, uiapp): Called for initialization
  • cmb_on_change(sender, args, ctx): Called when selection changes
  • cmb_dropdown_close(sender, args, ctx): Called when dropdown closes
  • cmb_dropdown_open(sender, args, ctx): Called when dropdown opens

Parameters:

Name Type Description Default
combobox

The parsed component metadata

required
ui_item

The pyRevit UI wrapper for the ComboBox

required
uiapp

The Revit UIApplication instance

required
script_globals dict

The globals() dict from the script

required

Returns:

Type Description
bool

True if setup succeeded, False otherwise

Source code in pyrevitlib/pyrevit/loader/uimaker.py
def setup_combobox(combobox, ui_item, uiapp, script_globals):
    """Set up ComboBox event handlers from script globals.

    This function looks for special global variables in the script and
    wires them up as event handlers for the ComboBox.

    Recognized global variables:
        - __selfinit__(component, ui_item, uiapp): Called for initialization
        - __cmb_on_change__(sender, args, ctx): Called when selection changes
        - __cmb_dropdown_close__(sender, args, ctx): Called when dropdown closes
        - __cmb_dropdown_open__(sender, args, ctx): Called when dropdown opens

    Args:
        combobox: The parsed component metadata
        ui_item: The pyRevit UI wrapper for the ComboBox
        uiapp: The Revit UIApplication instance
        script_globals (dict): The globals() dict from the script

    Returns:
        (bool): True if setup succeeded, False otherwise
    """
    try:
        cmb = ui_item.get_rvtapi_object() if hasattr(ui_item, 'get_rvtapi_object') else None
        if not cmb:
            mlogger.debug("Could not get ComboBox API object")
            return False

        # Call __selfinit__ if present (traditional pattern for initialization)
        selfinit = script_globals.get('__selfinit__')
        if callable(selfinit):
            mlogger.debug("Calling __selfinit__ for ComboBox")
            try:
                res = selfinit(combobox, ui_item, uiapp)
                if res is False:
                    mlogger.debug("__selfinit__ returned False, deactivating")
                    return False
            except Exception as init_err:
                mlogger.error("Error in ComboBox __selfinit__: %s", init_err)
                return False

        # Create context object for event handlers
        ctx = components.ComboBoxContext(combobox, ui_item, uiapp, cmb)

        # Get event handlers from script globals
        on_change = script_globals.get('__cmb_on_change__')
        on_dropdown_close = script_globals.get('__cmb_dropdown_close__')
        on_dropdown_open = script_globals.get('__cmb_dropdown_open__')

        handlers_found = False

        # Wire up CurrentChanged event
        if callable(on_change):
            handlers_found = True
            def current_changed_handler(sender, args):
                try:
                    on_change(sender, args, ctx)
                except Exception as ex:
                    mlogger.error("Error in __cmb_on_change__: %s", ex)

            cmb.CurrentChanged += current_changed_handler
            # Keep reference alive
            if hasattr(ui_item, '__dict__'):
                ui_item._cmb_current_changed_handler = current_changed_handler
            mlogger.debug("Wired __cmb_on_change__ handler")

        # Wire up DropDownClosed event
        if callable(on_dropdown_close) and hasattr(cmb, 'DropDownClosed'):
            handlers_found = True
            def dropdown_closed_handler(sender, args):
                try:
                    on_dropdown_close(sender, args, ctx)
                except Exception as ex:
                    mlogger.error("Error in __cmb_dropdown_close__: %s", ex)

            cmb.DropDownClosed += dropdown_closed_handler
            if hasattr(ui_item, '__dict__'):
                ui_item._cmb_dropdown_closed_handler = dropdown_closed_handler
            mlogger.debug("Wired __cmb_dropdown_close__ handler")

        # Wire up DropDownOpened event
        if callable(on_dropdown_open) and hasattr(cmb, 'DropDownOpened'):
            handlers_found = True
            def dropdown_opened_handler(sender, args):
                try:
                    on_dropdown_open(sender, args, ctx)
                except Exception as ex:
                    mlogger.error("Error in __cmb_dropdown_open__: %s", ex)

            cmb.DropDownOpened += dropdown_opened_handler
            if hasattr(ui_item, '__dict__'):
                ui_item._cmb_dropdown_opened_handler = dropdown_opened_handler
            mlogger.debug("Wired __cmb_dropdown_open__ handler")

        # Store context on ui_item for later access
        if hasattr(ui_item, '__dict__'):
            ui_item._cmb_context = ctx

        if handlers_found:
            mlogger.info("ComboBox handlers set up successfully for '%s'", 
                        getattr(combobox, 'display_name', str(combobox)))
        else:
            mlogger.debug("No ComboBox event handlers found in script globals")

        return True

    except Exception as ex:
        mlogger.error("Error setting up ComboBox handlers: %s", ex)
        return False

get_combobox_context(ui_item)

Get the ComboBoxContext for a UI item.

Parameters:

Name Type Description Default
ui_item

The pyRevit UI wrapper for the ComboBox

required

Returns:

Type Description
ComboBoxContext

The context object or None

Source code in pyrevitlib/pyrevit/loader/uimaker.py
def get_combobox_context(ui_item):
    """Get the ComboBoxContext for a UI item.

    Args:
        ui_item: The pyRevit UI wrapper for the ComboBox

    Returns:
        (ComboBoxContext): The context object or None
    """
    if hasattr(ui_item, '_cmb_context'):
        return ui_item._cmb_context
    return None

update_pyrevit_ui(ui_ext, ext_asm_info, create_beta=False)

Updates/Creates pyRevit ui for the extension and assembly dll address.

Parameters:

Name Type Description Default
ui_ext GenericUIContainer

UI container.

required
ext_asm_info AssemblyInfo

Assembly info.

required
create_beta bool

Create beta ui. Defaults to False.

False
Source code in pyrevitlib/pyrevit/loader/uimaker.py
def update_pyrevit_ui(ui_ext, ext_asm_info, create_beta=False):
    """Updates/Creates pyRevit ui for the extension and assembly dll address.

    Args:
        ui_ext (GenericUIContainer): UI container.
        ext_asm_info (AssemblyInfo): Assembly info.
        create_beta (bool, optional): Create beta ui. Defaults to False.
    """
    mlogger.debug("Creating/Updating ui for extension: %s", ui_ext)
    cmp_count = _recursively_produce_ui_items(
        UIMakerParams(current_ui, None, ui_ext, ext_asm_info, create_beta)
    )
    mlogger.debug("%s components were created for: %s", cmp_count, ui_ext)

sort_pyrevit_ui(ui_ext)

Sorts pyRevit UI.

Parameters:

Name Type Description Default
ui_ext GenericUIContainer

UI container.

required
Source code in pyrevitlib/pyrevit/loader/uimaker.py
def sort_pyrevit_ui(ui_ext):
    """Sorts pyRevit UI.

    Args:
        ui_ext (GenericUIContainer): UI container.
    """
    # only works on panels so far
    # re-ordering of ui components deeper than panels have not been implemented
    for tab in current_ui.get_pyrevit_tabs():
        for litem in ui_ext.find_layout_items():
            if litem.directive:
                if litem.directive.directive_type == "before":
                    tab.reorder_before(litem.name, litem.directive.target)
                elif litem.directive.directive_type == "after":
                    tab.reorder_after(litem.name, litem.directive.target)
                elif litem.directive.directive_type == "afterall":
                    tab.reorder_afterall(litem.name)
                elif litem.directive.directive_type == "beforeall":
                    tab.reorder_beforeall(litem.name)

cleanup_pyrevit_ui()

Cleanup the pyrevit UI.

Hide all items that were not touched after a reload meaning they have been removed in extension folder structure and thus are not updated.

Source code in pyrevitlib/pyrevit/loader/uimaker.py
def cleanup_pyrevit_ui():
    """Cleanup the pyrevit UI.

    Hide all items that were not touched after a reload
    meaning they have been removed in extension folder structure
    and thus are not updated.
    """
    untouched_items = current_ui.get_unchanged_items()
    for item in untouched_items:
        if not item.is_native():
            try:
                mlogger.debug("Deactivating: %s", item)
                item.deactivate()
            except Exception as deact_err:
                # Log as debug to avoid cluttering output with expected errors
                mlogger.debug(
                    "Could not deactivate item (may be native): %s | %s",
                    item,
                    deact_err,
                )

reflow_pyrevit_ui(direction=applocales.DEFAULT_LANG_DIR)

Set the flow direction of the tabs.

Source code in pyrevitlib/pyrevit/loader/uimaker.py
def reflow_pyrevit_ui(direction=applocales.DEFAULT_LANG_DIR):
    """Set the flow direction of the tabs."""
    if direction == "LTR":
        current_ui.set_LTR_flow()
    elif direction == "RTL":
        current_ui.set_RTL_flow()