Feature combobox button bundle#2930
Conversation
- 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
|
Unable to perform a code review. You have run out of credits 😔 |
|
@romangolev this may touch your actual refactoring work |
|
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. |
There was a problem hiding this comment.
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:
|
@sanzoghenzo I'd like your opinionated review 😸 |
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? |
100% Both PR are quite extensive, so expect turbulences. |
|
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. |
sanzoghenzo
left a comment
There was a problem hiding this comment.
HI, here's my review.
I didn't test any of this, just looked at the code style 😉
pyrevitlib/pyrevit/loader/uimaker.py
Outdated
| return None | ||
|
|
||
|
|
||
| def _produce_ui_combobox(ui_maker_params): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Alright! I did my best to logically break that down to a few sub-routines to make it easier to read and review now. Still, the complexity is there - mainly because we don't have a single button this time around, but we deal with all the sub-menu (combobox) items as well.
|
@devloai please review |
There was a problem hiding this comment.
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 withcreate_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:
- resolved auto generated Copilot checks
- Fix debug logging levels and exception handling - Improve code style: reduce nesting, extract helpers - Add missing docstring documentation - Remove redundant code and clarify comments Addresses Copilot AI and human review suggestions.
- Extract _setup_combobox_objects() to handle validation and object creation - Extract _add_combobox_members() to handle member addition logic - Refactor _produce_ui_combobox() for better readability and maintainability - Fix critical indentation bug: unindent code after icon check so tooltips, members, and activation run regardless of icon file presence This refactoring improves code organization by separating concerns: - Setup/validation logic is isolated and testable - Member addition logic is self-contained - Main function focuses on high-level configuration flow
- Add _sanitize_script_file() function to replace common non-ASCII characters with ASCII equivalents - Call sanitization before loading combobox scripts to prevent SyntaxError - Handles em/en dashes, smart quotes, ellipsis, and non-breaking spaces
|
Thank you for the wonderful reviews, @sanzoghenzo and @jmcouffin ! Very keen eye, and I hope I was able to accommodate all the requests at this point. If you could cast another eye on it, that would be wonderful! Meanwhile, I created this draft PR to follow up with, with implementation under the |
|
@dnenov Thank you so much for this one. @romangolev just tested and approved. I'll see to get to your PR and then put the combobox in your task list :) |
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 inscript.pyto allow scripts to access the fully constructed UI object and set up event handlers after the UI is created.Combobox UI Element
Key Features
__selfinit__pattern (following SmartButton architecture)__selfinit__function, consistent with SmartButton implementationTechnical Changes
_PyRevitRibbonComboBoxclass with all API methods and properties__selfinit__pattern (SmartButton pattern) for deferred script initializationBug Fixes
Checklist
Before submitting your pull request, ensure the following requirements are met:
pipenv run black {source_file_or_directory}Related Issues
If applicable, link the issues resolved by this pull request:
Additional Notes
Files Modified
pyrevitlib/pyrevit/extensions/components.py- Fixed member dictionary preservationpyrevitlib/pyrevit/coreutils/ribbon.py- Added complete ComboBox wrapper classpyrevitlib/pyrevit/loader/uimaker.py- Enhanced ComboBox creation and member configurationArchitecture Pattern
The ComboBox implementation follows the SmartButton pattern for deferred initialization:
bundle.yamlmetadatascript.pyis loaded as a module__selfinit__function exists, it's called with(component, ui_item, uiapp)parameters__selfinit__returnsFalse, the ComboBox is deactivatedThis pattern allows scripts to:
Usage Example
ComboBoxes can now be created with full metadata support in
bundle.yaml:And in
script.py, use the__selfinit__pattern to wire ComboBox items to commands:Key Points:
idfield frombundle.yamlmembers becomes theNameproperty of theComboBoxMembertextfield becomes theItemTextpropertysender.Current.Nameto get the member ID for routing to commandssender.Current.ItemTextto get the display textFUNCTION_MAP) to connect member IDs to your command functionsAPI Methods Available
All ComboBox properties and methods from Autodesk.Revit.UI are now accessible:
name,current,visible,enabled,get_title(), etc.set_icon(),set_tooltip(),add_item(),get_items(), etc.CurrentChanged,DropDownOpened,DropDownClosed(viaadd_*_handler()methods)Gotchas
Thread Context: Do not use
print()or equivalent logging inside the__selfinit__initialization phase, as the thread context will break. Useloggerfrompyrevit.scriptinstead:Member Icons: Using
image(oricon) 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! 🎉