I added an additional argument to the class SelectFromList in the init.py file.
Also added a description and raising a PyRevitException for wrong argument input . Hope this will help someone in the future.
import re
class SelectFromList(TemplateUserInputWindow):
"""Standard form to select from a list of items.
Any object can be passed in a list to the ``context`` argument. This class
wraps the objects passed to context, in :obj:`TemplateListItem`.
This class provides the necessary mechanism to make this form work both
for selecting items from a list, and from a list of checkboxes. See the
list of arguments below for additional options and features.
Args:
context (list[str] or dict[list[str]]):
list of items to be selected from
OR
dict of list of items to be selected from.
use dict when input items need to be grouped
e.g. List of sheets grouped by sheet set.
title (str, optional): window title. see super class for defaults.
width (int, optional): window width. see super class for defaults.
height (int, optional): window height. see super class for defaults.
button_name (str, optional):
name of select button. defaults to 'Select'
name_attr (str, optional):
object attribute that should be read as item name.
multiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to False
info_panel (bool, optional):
show information panel and fill with .description property of item
return_all (bool, optional):
return all items. This is handly when some input items have states
and the script needs to check the state changes on all items.
This options works in multiselect mode only. defaults to False
filterfunc (function):
filter function to be applied to context items.
resetfunc (function):
reset function to be called when user clicks on Reset button
group_selector_title (str):
title for list group selector. defaults to 'List Group'
default_group (str): name of defautl group to be selected
sort_groups (str, optional):
Determines the sorting type applied to the list groups. This attribute can take one of the following values:
'sorted': This will sort the groups in standard alphabetical order
'natural': This will sort the groups in a manner that is more intuitive for human perception, especially when there are numbers involved.
'unsorted': The groups will maintain the original order in which they were provided, without any reordering.
Defaults to 'sorted'.
Example:
>>> from pyrevit import forms
>>> items = ['item1', 'item2', 'item3']
>>> forms.SelectFromList.show(items, button_name='Select Item')
>>> ['item1']
>>> from pyrevit import forms
>>> ops = [viewsheet1, viewsheet2, viewsheet3]
>>> res = forms.SelectFromList.show(ops,
... multiselect=False,
... name_attr='Name',
... button_name='Select Sheet')
>>> from pyrevit import forms
>>> ops = {'Sheet Set A': [viewsheet1, viewsheet2, viewsheet3],
... 'Sheet Set B': [viewsheet4, viewsheet5, viewsheet6]}
>>> res = forms.SelectFromList.show(ops,
... multiselect=True,
... name_attr='Name',
... group_selector_title='Sheet Sets',
... button_name='Select Sheets',
... sort_groups='sorted')
This module also provides a wrapper base class :obj:`TemplateListItem`
for when the checkbox option is wrapping another element,
e.g. a Revit ViewSheet. Derive from this base class and define the
name property to customize how the checkbox is named on the dialog.
>>> from pyrevit import forms
>>> class MyOption(forms.TemplateListItem):
... @property
... def name(self):
... return '{} - {}{}'.format(self.item.SheetNumber,
... self.item.SheetNumber)
>>> ops = [MyOption('op1'), MyOption('op2', True), MyOption('op3')]
>>> res = forms.SelectFromList.show(ops,
... multiselect=True,
... button_name='Select Item')
>>> [bool(x) for x in res] # or [x.state for x in res]
[True, False, True]
"""
xaml_source = 'SelectFromList.xaml'
@property
def use_regex(self):
"""Is using regex?"""
return self.regexToggle_b.IsChecked
def _setup(self, **kwargs):
# custom button name?
button_name = kwargs.get('button_name', 'Select')
if button_name:
self.select_b.Content = button_name
# attribute to use as name?
self._nameattr = kwargs.get('name_attr', None)
# multiselect?
if kwargs.get('multiselect', False):
self.multiselect = True
self.list_lb.SelectionMode = Controls.SelectionMode.Extended
self.show_element(self.checkboxbuttons_g)
else:
self.multiselect = False
self.list_lb.SelectionMode = Controls.SelectionMode.Single
self.hide_element(self.checkboxbuttons_g)
# info panel?
self.info_panel = kwargs.get('info_panel', False)
# return checked items only?
self.return_all = kwargs.get('return_all', False)
# filter function?
self.filter_func = kwargs.get('filterfunc', None)
# reset function?
self.reset_func = kwargs.get('resetfunc', None)
if self.reset_func:
self.show_element(self.reset_b)
# context group title?
self.ctx_groups_title = \
kwargs.get('group_selector_title', 'List Group')
self.ctx_groups_title_tb.Text = self.ctx_groups_title
self.ctx_groups_active = kwargs.get('default_group', None)
# group sorting?
self.sort_groups = kwargs.get('sort_groups', 'sorted')
if self.sort_groups not in ['sorted', 'unsorted', 'natural']:
raise PyRevitException("Invalid value for 'sort_groups'. Allowed values are: 'sorted', 'unsorted', 'natural'.")
# check for custom templates
items_panel_template = kwargs.get('items_panel_template', None)
if items_panel_template:
self.Resources["ItemsPanelTemplate"] = items_panel_template
item_container_template = kwargs.get('item_container_template', None)
if item_container_template:
self.Resources["ItemContainerTemplate"] = item_container_template
item_template = kwargs.get('item_template', None)
if item_template:
self.Resources["ItemTemplate"] = \
item_template
# nicely wrap and prepare context for presentation, then present
self._prepare_context()
# setup search and filter fields
self.hide_element(self.clrsearch_b)
# active event listeners
self.search_tb.TextChanged += self.search_txt_changed
self.ctx_groups_selector_cb.SelectionChanged += self.selection_changed
self.clear_search(None, None)
def _prepare_context_items(self, ctx_items):
new_ctx = []
# filter context if necessary
if self.filter_func:
ctx_items = filter(self.filter_func, ctx_items)
for item in ctx_items:
if isinstance(item, TemplateListItem):
item.checkable = self.multiselect
new_ctx.append(item)
else:
new_ctx.append(
TemplateListItem(item,
checkable=self.multiselect,
name_attr=self._nameattr)
)
return new_ctx
@staticmethod
def _natural_sort_key(key):
return [int(c) if c.isdigit() else c.lower() for c in re.split('(\d+)', key)]
def _prepare_context(self):
if isinstance(self._context, dict) and self._context.keys():
# Sort the groups if necessary
if self.sort_groups == "sorted":
sorted_groups = sorted(self._context.keys())
elif self.sort_groups == "natural":
sorted_groups = sorted(self._context.keys(), key=self._natural_sort_key)
else:
sorted_groups = self._context.keys() # No sorting
self._update_ctx_groups(sorted_groups)
new_ctx = OrderedDict()
for ctx_grp in sorted_groups:
items = self._prepare_context_items(self._context[ctx_grp])
new_ctx[ctx_grp] = items # Do not sort the items within the groups
self._context = new_ctx
else:
self._context = self._prepare_context_items(self._context)