Skip to content

Conversation

@dnenov
Copy link
Contributor

@dnenov dnenov commented Nov 25, 2025

Complete ComboBox Implementation with Full API Support

Description

This PR implements comprehensive ComboBox support for pyRevit extensions, exposing all properties and methods from the Autodesk.Revit.UI ComboBox API. The implementation enables full control over ComboBox creation, member management, and property configuration through bundle.yaml metadata.

The ComboBox implementation follows the same SmartButton pattern for deferred initialization, using the __selfinit__ function in script.py to allow scripts to access the fully constructed UI object and set up event handlers after the UI is created.

Combobox UI Element

Revit_gpZ8Xs46xi

Key Features

  • Full Property Support: All ComboBox properties are now accessible (Name, ToolTip, Image, ItemText, Enabled, Visible, Current, LongDescription, ToolTipImage)
  • Complete Method Implementation: All ComboBox methods are exposed (add_item, add_items, add_separator, get_items, etc.)
  • Rich Member Metadata: ComboBox members support icons, tooltips, groups, extended tooltips, and tooltip images
  • Event Handling: Support for CurrentChanged, DropDownOpened, and DropDownClosed events via __selfinit__ pattern (following SmartButton architecture)
  • Proper Property Setting: Fixed member property assignment to work on ComboBoxMember objects (not ComboBoxMemberData)
  • SmartButton Pattern: Uses deferred initialization via __selfinit__ function, consistent with SmartButton implementation

Technical Changes

  1. components.py: Fixed member dictionary preservation - was converting rich metadata to simple tuples, losing icon/tooltip/group properties
  2. ribbon.py: Added complete _PyRevitRibbonComboBox class with all API methods and properties
  3. uimaker.py: Enhanced ComboBox creation to set all properties from metadata and properly configure members. Implements __selfinit__ pattern (SmartButton pattern) for deferred script initialization

Bug Fixes

  • Fixed member icons/tooltips not appearing by preserving full member dictionaries
  • Fixed property setting by using ComboBoxMember objects returned from AddItem()
  • Added encoding declaration to fix non-ASCII character syntax error
  • Fixed icon path resolution for member icons

Checklist

Before submitting your pull request, ensure the following requirements are met:

  • Code follows the PEP 8 style guide.
  • Code has been formatted with Black using the command:
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected.

Related Issues

If applicable, link the issues resolved by this pull request:

  • Related to ComboBox support feature request

Additional Notes

Files Modified

  • pyrevitlib/pyrevit/extensions/components.py - Fixed member dictionary preservation
  • pyrevitlib/pyrevit/coreutils/ribbon.py - Added complete ComboBox wrapper class
  • pyrevitlib/pyrevit/loader/uimaker.py - Enhanced ComboBox creation and member configuration

Architecture Pattern

The ComboBox implementation follows the SmartButton pattern for deferred initialization:

  1. UI is created and configured from bundle.yaml metadata
  2. script.py is loaded as a module
  3. If __selfinit__ function exists, it's called with (component, ui_item, uiapp) parameters
  4. Script can access the fully constructed UI object to set up event handlers and perform custom initialization
  5. If __selfinit__ returns False, the ComboBox is deactivated

This pattern allows scripts to:

  • Access the ComboBox UI object after it's fully created
  • Set up event handlers (CurrentChanged, DropDownOpened, DropDownClosed)
  • Perform custom initialization logic
  • Access all ComboBox properties and methods

Usage Example

ComboBoxes can now be created with full metadata support in bundle.yaml:

title: Panel Selector
tooltip: Switch between panels
tooltip_ext: Extended description
icon: icon.png
media_file: tooltip.png
members:
  - id: "settings"
    text: "Settings"
    icon: icon_settings.png
    tooltip: "Open Settings panel"
    tooltip_ext: "Configure settings"
    group: "Main Panels"
  - id: "views_sheets"
    text: "Views & Sheets"
    icon: icon_views.png
    tooltip: "Open Views & Sheets panel"
    group: "Main Panels"

And in script.py, use the __selfinit__ pattern to wire ComboBox items to commands:

def __selfinit__(component, ui_item, uiapp):
    """Deferred initializer - called after UI is created."""
    cmb = ui_item.get_rvtapi_object()
    
    # Define your command functions
    def show_settings():
        # Your logic here
        pass
    
    def show_views_sheets():
        # Your logic here
        pass
    
    # Map ComboBox member IDs to functions
    # The 'id' field from bundle.yaml members is used as the key
    FUNCTION_MAP = {
        "settings": show_settings,
        "views_sheets": show_views_sheets,
        # You can also map by display text as fallback
        "Settings": show_settings,
        "Views & Sheets": show_views_sheets,
    }
    
    def on_current_changed(sender, args):
        """Handle ComboBox selection change."""
        try:
            # Get the currently selected item
            current = sender.Current
            if not current:
                return
            
            # Access the member's ID and display text
            selected_id = current.Name      # The 'id' from bundle.yaml
            selected_text = current.ItemText  # The 'text' from bundle.yaml
            
            # Look up the function to call
            func = FUNCTION_MAP.get(selected_id) or FUNCTION_MAP.get(selected_text)
            
            if func:
                # Execute the command
                func()
            else:
                # Handle unknown selection
                logger.warning("No handler for: {} ({})".format(selected_text, selected_id))
        except Exception as e:
            logger.error("Error in ComboBox handler: {}".format(e))
    
    # Hook the event handler
    ui_item.add_current_changed_handler(on_current_changed)
    
    # Keep reference to prevent garbage collection
    ui_item._current_changed_handler = on_current_changed
    
    return True  # Return False to deactivate ComboBox

Key Points:

  • The id field from bundle.yaml members becomes the Name property of the ComboBoxMember
  • The text field becomes the ItemText property
  • Use sender.Current.Name to get the member ID for routing to commands
  • Use sender.Current.ItemText to get the display text
  • Create a mapping dictionary (FUNCTION_MAP) to connect member IDs to your command functions
  • The event handler receives the ComboBox sender and event args, allowing access to the selected member

API Methods Available

All ComboBox properties and methods from Autodesk.Revit.UI are now accessible:

  • Properties: name, current, visible, enabled, get_title(), etc.
  • Methods: set_icon(), set_tooltip(), add_item(), get_items(), etc.
  • Events: CurrentChanged, DropDownOpened, DropDownClosed (via add_*_handler() methods)

Gotchas

  • Thread Context: Do not use print() or equivalent logging inside the __selfinit__ initialization phase, as the thread context will break. Use logger from pyrevit.script instead:

    from pyrevit import script
    logger = script.get_logger()
    logger.warning("This works correctly")
  • Member Icons: Using image (or icon) for ComboBox member items can yield strange UI behavior in Revit's ComboBox dropdown. The items may appear with extra padding/margin, pushing text to the right. This is a limitation of the Revit API's ComboBox rendering and is not controllable via the Ribbon API. Consider using icons sparingly or testing thoroughly if visual alignment is critical.

Testing

Tested with Spectrum extension ComboBox implementation. All member icons, tooltips, and properties are working correctly. Event handlers work as expected using the __selfinit__ pattern.


Reviewers

FYI

Thank you for contributing to pyRevit! 🎉

- added initial combobox bundle to test case
- Add COMBOBOX_POSTFIX to extension directory hash calculation
- Implement ComboBox creation and member handling in uimaker.py
- Add _PyRevitRibbonComboBox wrapper class in ribbon.py
- Add ComboBoxGroup component class with members parsing
- the combobox loads and tirggers update
- because of event handling and initialization, we had to match the design pattern used with the smartbutton
- using delayed script loading
- Added comprehensive ComboBox property support (Name, ToolTip, Image, ItemText, Current, etc.)
- Implemented all ComboBox methods (add_item, add_items, add_separator, get_items, etc.)
- Fixed member properties by preserving full member dictionaries in components.py (was converting to tuples)
- Fixed member icon/tooltip/group properties by setting on ComboBoxMember object after AddItem
- Added encoding declaration to uimaker.py to fix non-ASCII character error
- Enhanced logging for ComboBox creation and member property setting
- Updated add_item() to return ComboBoxMember for property setting
- Full support for ComboBoxMemberData properties (icon, tooltip, group, tooltip_ext, tooltip_image)
- Added comprehensive ComboBox property support (Name, ToolTip, Image, ItemText, Current, etc.)
- Implemented all ComboBox methods (add_item, add_items, add_separator, get_items, etc.)
- Fixed member properties by preserving full member dictionaries in components.py (was converting to tuples)
- Fixed member icon/tooltip/group properties by setting on ComboBoxMember object after AddItem
- Added encoding declaration to uimaker.py to fix non-ASCII character error
- Updated add_item() to return ComboBoxMember for property setting
- Full support for ComboBoxMemberData properties (icon, tooltip, group, tooltip_ext, tooltip_image)
- Removed all debugging logging statements
- Formatted components.py, ribbon.py, and uimaker.py with Black
- Ensures code follows PEP 8 style guidelines
@devloai
Copy link
Contributor

devloai bot commented Nov 25, 2025

Unable to perform a code review. You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

@jmcouffin jmcouffin added New Feature New feature request [class->Implemented #{number}: {title}] Highlight Highlight these issues on Release notes labels Nov 25, 2025
@jmcouffin
Copy link
Contributor

@romangolev this may touch your actual refactoring work

@jmcouffin
Copy link
Contributor

This is great stuff @dnenov

I'll take a look as soon as I can.

In the meantime, if it is not too much to ask, could make a sample/test in the pyrevit dev extensions in a new PR. That would be helpful.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements comprehensive ComboBox support for pyRevit extensions, exposing all properties and methods from the Autodesk.Revit.UI ComboBox API. The implementation follows the SmartButton pattern for deferred initialization using the __selfinit__ function.

Key Changes:

  • Complete ComboBox API wrapper with all properties and methods (name, tooltip, image, current, events, etc.)
  • Rich member metadata support (icons, tooltips, groups, extended tooltips, tooltip images)
  • SmartButton-style deferred initialization pattern for event handler setup
  • Bug fixes for member property setting and icon path resolution

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
pyrevitlib/pyrevit/loader/uimaker.py Added _produce_ui_combobox function implementing full ComboBox creation with member configuration and __selfinit__ pattern support
pyrevitlib/pyrevit/extensions/components.py Added ComboBoxGroup class to handle ComboBox metadata and member preservation
pyrevitlib/pyrevit/extensions/__init__.py Added COMBOBOX_POSTFIX constant for ComboBox component identification
pyrevitlib/pyrevit/coreutils/ribbon.py Added _PyRevitRibbonComboBox wrapper class with complete API methods, properties, and event handler support
Comments suppressed due to low confidence (1)

pyrevitlib/pyrevit/loader/uimaker.py:1023

  • Except block directly handles BaseException.
                except:

Comment on lines +177 to +198
# Log panel items before adding slideout (for debugging order issues)
try:
if hasattr(parent_ui_item, "get_rvtapi_object"):
existing_items = parent_ui_item.get_rvtapi_object().GetItems()
mlogger.warning(
"SLIDEOUT: Panel has %d items before adding slideout",
len(existing_items),
)
for idx, item in enumerate(existing_items):
mlogger.warning(
"SLIDEOUT: Existing item %d: %s (type: %s)",
idx,
getattr(item, "Name", "unknown"),
type(item).__name__,
)
except Exception as log_err:
mlogger.debug("SLIDEOUT: Could not log existing items: %s", log_err)

mlogger.warning("SLIDEOUT: Adding slide out to: %s", parent_ui_item)
try:
parent_ui_item.add_slideout()
mlogger.warning("SLIDEOUT: Slideout added successfully")
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug logging code left in production. These warning-level logs (lines 181-191, 195, 198) appear to be temporary debugging code based on the comment 'for debugging order issues'. Warning-level logs should be reserved for actual warnings, not diagnostic information. Either remove this debug code or change to mlogger.debug() level if the diagnostic information is needed long-term.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, use mlogget.debug

Comment on lines +1018 to +1027
# Diagnostic logging to track panel placement issues
parent_name = getattr(ui_maker_params.parent_ui, "name", None)
if not parent_name:
try:
parent_name = str(type(ui_maker_params.parent_ui))
except:
parent_name = "unknown"
mlogger.warning(
"BUILDING COMPONENT: %s parent: %s", sub_cmp.name, parent_name
)
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bare except clause catches all exceptions including system exits. Replace except: on line 1023 with except Exception: to avoid catching SystemExit, KeyboardInterrupt, and other system-level exceptions that should not be suppressed.

Copilot uses AI. Check for mistakes.
parent_name = str(type(ui_maker_params.parent_ui))
except:
parent_name = "unknown"
mlogger.warning(
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug logging code left in production. This warning-level log (lines 1025-1027) with comment 'Diagnostic logging to track panel placement issues' appears to be temporary debugging code. Warning-level logs should be reserved for actual warnings. Either remove this debug code or change to mlogger.debug() level if the diagnostic information is needed long-term.

Suggested change
mlogger.warning(
mlogger.debug(

Copilot uses AI. Check for mistakes.
Comment on lines +614 to +619
# Add member to ComboBox first (returns ComboBoxMember object)
try:
member = combobox_ui.add_item(member_data)
if not member:
mlogger.warning("AddItem returned None for: %s", member_text)
continue
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name 'member' is reused and shadows the loop variable from line 572. The returned ComboBoxMember object should use a different name such as 'member_obj' or 'added_member' to avoid confusion and shadowing.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +26
# Enable verbose logging to see WARNING messages (can be removed later)
# Uncomment the line below to see all log messages:
# mlogger.set_verbose_mode()
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove or clean up commented-out debug configuration. The comment 'can be removed later' on line 24 suggests this is temporary code. Either remove these lines or if verbose mode configuration is intended to stay, document when and why users would enable it.

Suggested change
# Enable verbose logging to see WARNING messages (can be removed later)
# Uncomment the line below to see all log messages:
# mlogger.set_verbose_mode()

Copilot uses AI. Check for mistakes.
member_data (UI.ComboBoxMemberData): Member data to add
Returns:
(UI.ComboBoxMember): The created ComboBoxMember object, or None
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method documentation should explain when None is returned. The docstring states the return value can be None but doesn't document under what conditions this occurs (e.g., when in itemdata_mode or on error).

Suggested change
(UI.ComboBoxMember): The created ComboBoxMember object, or None
(UI.ComboBoxMember): The created ComboBoxMember object, or None.
None is returned if the ComboBox is in itemdata_mode (i.e., when the underlying Revit API object is not available and items cannot be added directly).

Copilot uses AI. Check for mistakes.
"""Add multiple items to the ComboBox.
Args:
member_data_list (list): List of UI.ComboBoxMemberData objects
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method missing return value documentation. The docstring should document what this method returns (appears to return None based on the implementation).

Suggested change
member_data_list (list): List of UI.ComboBoxMemberData objects
member_data_list (list): List of UI.ComboBoxMemberData objects
Returns:
None

Copilot uses AI. Check for mistakes.
)

def add_separator(self):
"""Add a separator to the ComboBox dropdown list."""
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method missing return value and exception documentation. The docstring should document what this method returns and that it may raise PyRevitUIError on failure.

Suggested change
"""Add a separator to the ComboBox dropdown list."""
"""Add a separator to the ComboBox dropdown list.
Returns:
None
Raises:
PyRevitUIError: If adding the separator fails.
"""

Copilot uses AI. Check for mistakes.
@jmcouffin
Copy link
Contributor

@sanzoghenzo I'd like your opinionated review 😸

@romangolev
Copy link
Contributor

@romangolev this may touch your actual refactoring work

Cool stuff! @jmcouffin would be nice to implement this functionality in new loader as well. Do you think we could confirm that that it works, confirm that my PR works as well and then proceed with this?
Splitting it into chunks might be more controllable

@jmcouffin
Copy link
Contributor

@romangolev this may touch your actual refactoring work

Cool stuff! @jmcouffin would be nice to implement this functionality in new loader as well. Do you think we could confirm that that it works, confirm that my PR works as well and then proceed with this? Splitting it into chunks might be more controllable

100%
I'll get both validated as soon as I can and as soon as I can the brilliant reviews from my brother in arm: Sanzo. While having dinner with Deyan tonight IRL, yeah, that actually happened, I asked him to had a test/example in the pyrevitdev extension.

Both PR are quite extensive, so expect turbulences.

@dnenov
Copy link
Contributor Author

dnenov commented Nov 27, 2025

I will do my best to get a test/example in the dev extension - I wasn't aware of it! As you guys can see, the actual, in-code implementation of the combobox is a bit complicated, but that's to be expected. It will be of great value to have that tested the way you want, so I'll jump on it asap!

Re-splitting this one up - it's a singular new feature :/ I struggle to find a way to have it broken down, as each piece works together with the rest. If you guys, @romangolev @jmcouffin have a better idea how to get it split up, ... yes, up for it too.

Essentially, it's a wrapper around the native Revit combobox ui element, much like the other ui elements we have.

Copy link
Contributor

@sanzoghenzo sanzoghenzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HI, here's my review.
I didn't test any of this, just looked at the code style 😉

self.members = []

# Read members from metadata
if self.meta:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert the if condition and exit early to reduce code indentation

Suggested change
if self.meta:
if not self.meta:
return

Comment on lines +1062 to +1089
if adwindows_obj:
exToolTip = self.get_rvtapi_object().ToolTip
if not isinstance(exToolTip, str):
exToolTip = None
adwindows_obj.ToolTip = AdWindows.RibbonToolTip()
adwindows_obj.ToolTip.Title = self.ui_title
adwindows_obj.ToolTip.Content = exToolTip
_StackPanel = System.Windows.Controls.StackPanel()
_video = System.Windows.Controls.MediaElement()
_video.Source = Uri(tooltip_video)
_video.LoadedBehavior = System.Windows.Controls.MediaState.Manual
_video.UnloadedBehavior = System.Windows.Controls.MediaState.Manual

def on_media_ended(sender, args):
sender.Position = System.TimeSpan.Zero
sender.Play()

_video.MediaEnded += on_media_ended

def on_loaded(sender, args):
sender.Play()

_video.Loaded += on_loaded
_StackPanel.Children.Add(_video)
adwindows_obj.ToolTip.ExpandedContent = _StackPanel
adwindows_obj.ResolveToolTip()
else:
self.tooltip_video = tooltip_video
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert if condition

"""
try:
adwindows_obj = self.get_adwindows_object()
if adwindows_obj:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invert if condition

"""
if self.itemdata_mode:
return self.ui_title
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for an else here

Comment on lines +1117 to +1120
else:
if hasattr(self._rvtapi_object, "ItemText"):
self._rvtapi_object.ItemText = self.ui_title = ui_title
self._dirty = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
else:
if hasattr(self._rvtapi_object, "ItemText"):
self._rvtapi_object.ItemText = self.ui_title = ui_title
self._dirty = True
elif hasattr(self._rvtapi_object, "ItemText"):
self._rvtapi_object.ItemText = self.ui_title = ui_title
self._dirty = True

Comment on lines +177 to +198
# Log panel items before adding slideout (for debugging order issues)
try:
if hasattr(parent_ui_item, "get_rvtapi_object"):
existing_items = parent_ui_item.get_rvtapi_object().GetItems()
mlogger.warning(
"SLIDEOUT: Panel has %d items before adding slideout",
len(existing_items),
)
for idx, item in enumerate(existing_items):
mlogger.warning(
"SLIDEOUT: Existing item %d: %s (type: %s)",
idx,
getattr(item, "Name", "unknown"),
type(item).__name__,
)
except Exception as log_err:
mlogger.debug("SLIDEOUT: Could not log existing items: %s", log_err)

mlogger.warning("SLIDEOUT: Adding slide out to: %s", parent_ui_item)
try:
parent_ui_item.add_slideout()
mlogger.warning("SLIDEOUT: Slideout added successfully")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, use mlogget.debug

Comment on lines +484 to +487
mlogger.error("Error calling create_combobox: %s", create_err)
import traceback

mlogger.error("Traceback: %s", traceback.format_exc())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just use

Suggested change
mlogger.error("Error calling create_combobox: %s", create_err)
import traceback
mlogger.error("Traceback: %s", traceback.format_exc())
mlogger.exception("Error calling create_combobox: %s", create_err)

it will add the exception info automatically

return None


def _produce_ui_combobox(ui_maker_params):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is really long, is there a way to split it into logical chunks?
Also, is this code that error prone that you need to catch an exception on each line? (I didn't try it so this is just by reading the code).
If this is necessary, can you reduce the scope of the exceptions to only catch those expected?


# IMPORTANT: only bail here if it is still ComboBoxData
if isinstance(combobox_obj, UI.ComboBoxData):
return combobox_ui # <- ONLY in this branch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the comment means

Comment on lines +760 to +770
except PyRevitException as err:
mlogger.error("UI error creating ComboBox: %s", err.msg)
import traceback

mlogger.error("Traceback: %s", traceback.format_exc())
return None
except Exception as err:
mlogger.error("Error creating ComboBox: %s", err)
import traceback

mlogger.error("Full traceback: %s", traceback.format_exc())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You basically already catched each and every exception line by line, and you didn't (re)raised anything, is this still necessary?

@jmcouffin
Copy link
Contributor

@devloai please review

Copy link
Contributor

@devloai devloai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary:

This PR implements comprehensive ComboBox support for pyRevit extensions, exposing all properties and methods from the Autodesk.Revit.UI ComboBox API. Key features include:

  • Full property support (Name, ToolTip, Image, ItemText, Enabled, Visible, Current, etc.)
  • Complete method implementation (add_item, add_items, add_separator, get_items, etc.)
  • Rich member metadata support (icons, tooltips, groups, extended tooltips)
  • Event handling via __selfinit__ pattern (following SmartButton architecture)
  • Proper member property assignment on ComboBoxMember objects

Review Summary:

The implementation is well-structured and follows existing pyRevit patterns correctly. The code properly:

  • Uses the SmartButton pattern for deferred initialization via __selfinit__
  • Handles member property extraction and assignment correctly (initial concern about variable shadowing was unfounded - the code cleverly extracts properties before reassigning the variable)
  • Follows the existing pattern where create_* methods don't return values (consistent with create_pulldown_button, create_split_button, etc.)
  • Implements comprehensive error handling with appropriate logging levels
  • Preserves full member dictionaries to maintain rich metadata (icons, tooltips, groups)

The architecture aligns with the SmartButton pattern documented in the knowledge base, allowing scripts to access fully constructed UI objects and set up event handlers after UI creation.

No critical issues found. The implementation is solid and ready to merge. 🎉

Follow-up suggestions:

  • @devloai create example/test bundle in pyrevitdev extension showing ComboBox usage
  • @devloai add documentation for ComboBox bundle.yaml metadata format

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Highlight Highlight these issues on Release notes New Feature New feature request [class->Implemented #{number}: {title}]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants