forms.SelectFromList and it´s parameters

Hello pyRevit friends :slight_smile:

I need help building a form for selecting sheets.
Let´s start step by step, more questions to come.

  1. name_attr (str , optional )
    I want to use SheetNumber + Name as names for my items.
    But the name_attr parameter lets me only use 1 parameter as name. How can I build my desired item name?
    image
ops = SheetSet
res = forms.SelectFromList.show(ops,
                                multiselect=True,
                                name_attr='SheetNumber',
                                button_name='Select Sheet')
  1. Groups
    I´d like to group my sheets by sheetset.
    While I get the concept of grouping in the following example, I don´t know how I can use this method with my lists.
>>> 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')

I´d start like that:

  • Make a list of all sheetset names
  • Make a list of lists with the sheets
  • Zip lists together and use a for loop to somehow iterate through the lists and get my desired opt

But I´m pretty shure this is wrong so I don´t even try. Would this even work with elements like sheets?
Or do I have to use this method if I want to group sheets and get the selected sheets as output?

>>> 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]

Happy about any advice :slight_smile:

https://pyrevit.readthedocs.io/en/latest/pyrevit/forms.html#:~:text=pyrevit.forms.SelectFromList

Take a look at the pyrevit forms.select_sheets() function. It may provide all the functionality you need, or if it doesn’t, you can dissect the code and recreate something custom.

1 Like

Hello Martin :slight_smile:

The code for select_Sheets also uses the standard SelectFromList form and it does not even use the optional name_attr. So I´m afraid that doesn´t help me.

Edit: Oh, you are right , the select_sheets form relly uses sheetnumber+sheetname, but i don´t know why.

I tried if it works if just don´t use the optional name_attr parameter, but that doesn´t work:

ops = SheetSet
res = forms.SelectFromList.show(ops,
                                multiselect=True,
                                button_name='Select Sheet')

If you’re passing an object (not just a text string) into the form, you have to either define the name_attr (can only be one attribute) or wrap those objects in a template list item that uses a ‘name’ property.

class MyOptionSheet(forms.TemplateListItem):
    @property
    def name(self):
        return '{}:{} - {}'.format(self.item.ViewType,
            self.item.Name,
            self.item.SheetNumber)

ops = [MyOptionSheet(x) for x in sheets if not x.IsPlaceholder]
2 Likes
class MyOptionSheet(forms.TemplateListItem):
    @property
    def name(self):
        return '{}:{} - {}'.format(self.item.ViewType,
            self.item.SheetNumber,
            self.item.SheetNumber)

class MyOptionView(forms.TemplateListItem):
    @property
    def name(self):
        return '{}:{}'.format(self.item.ViewType,
            self.item.Name)

sheetsets = DB.FilteredElementCollector(doc)\
    .OfClass(DB.ViewSheetSet)

views = DB.FilteredElementCollector(doc)\
    .OfClass(DB.View)\
    .WhereElementIsNotElementType()

sheets = DB.FilteredElementCollector(doc)\
    .OfClass(DB.ViewSheet)\
    .WhereElementIsNotElementType()
approvedViews = [
    DB.ViewType.FloorPlan,
    DB.ViewType.CeilingPlan,
    DB.ViewType.Elevation,
    DB.ViewType.ThreeD,
    DB.ViewType.DrawingSheet,
    DB.ViewType.DraftingView,
    DB.ViewType.Legend,
    DB.ViewType.EngineeringPlan,
    DB.ViewType.AreaPlan,
    DB.ViewType.Section,
    DB.ViewType.Detail,
    DB.ViewType.Rendering]
ops = {}
viewsAndSheets = []
viewsAndSheets.extend([MyOptionView(x) for x in views if not x.IsTemplate and x.ViewType in approvedViews])
viewsAndSheets.extend([MyOptionSheet(x) for x in sheets if not x.IsPlaceholder])
ops['All'] = viewsAndSheets

for sheetset in sheetsets:
    sheets = []
    for view in sheetset.Views:
        if view.GetType() == DB.ViewSheet:
            sheets.append(MyOptionSheet(view))
        else:
            sheets.append(MyOptionView(view))
    ops[sheetset.Name] = sheets

selectedSheets = forms.SelectFromList.show(ops, 'Title', multiselect=True)

if selectedSheets is None:
    script.exit()
1 Like

Oh now i understand, it’s the SheetOption Class i didn’t see in the select_sheets code.

Now i should get this to work :slight_smile:
Thanks for your help martin!

I tried my best but I´m still having trouble with the grouping.
My current result is, that the group names are correct, but if I select a group nothing happens. There are always all sheets displayed.

I’m not grouping by the standard sheetsets like the select-sheets form does, I want to group by a sheet parameter (project browser sorting level). This is why i made a list of the unique parameters and a list of sheet lists by this parameter.

def Sheets():
    Sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    return Sheets

def SheetSets():
    SheetSets = []
    for s in Sheets:
		paravalue = s.LookupParameter(BrowserSortParameter).AsString()
		if paravalue not in SheetSets:
			SheetSets.append(paravalue)
    return SheetSets
    
def SheetsBySheetSet():
    outlist = []
    for SheetSet in SheetSets:
        outlist2 = []
        for Sheet in Sheets:
            paravalue = Sheet.LookupParameter(BrowserSortParameter).AsString()
            if paravalue == SheetSet:
                outlist2.append(Sheet)
        outlist.append(outlist2)
    return outlist

class MyOptionSheet(forms.TemplateListItem):
    @property
    def name(self):
        return '{} - {}'.format(
            self.item.SheetNumber,
            self.item.Name)

BrowserSortParameter = BrowserSortParameter()
Sheets = Sheets()
SheetSets = SheetSets()
SheetsBySheetSet = SheetsBySheetSet()

ops = {}
AllSheets = []
AllSheets.extend([MyOptionSheet(x) for x in Sheets if not x.IsPlaceholder])
ops['All'] = AllSheets

for SheetSet in SheetSets:
    sheets = []
    for SheetBySheetSet in SheetsBySheetSet:
        for s in SheetBySheetSet:
            sheets.append(MyOptionSheet(s))
    ops[SheetSet] = sheets

selectedSheets = forms.SelectFromList.show(ops, 'Title', multiselect=True)

I think my problem is that I use the basic sheets list for the options:

ops = {}
AllSheets = []
AllSheets.extend([MyOptionSheet(x) for x in Sheets if not x.IsPlaceholder])
ops['All'] = AllSheets

Maybe Somehow I have to use the multi level list SheetsBySheetSet for the options?
Or am I doing something else wrong?

Happy about any advice :slight_smile:

Go it working by ziping sheetsetnames and sheets together :slight_smile:

for SheetSetName, SheetsBySheetSet in zip(SheetSetNames,SheetsBySheetSet):
    sheets = []
    for Sheet in SheetsBySheetSet:
        sheets.append(MyOptionSheet(Sheet))
    ops[SheetSetName] = sheets

But I have the next question.
The order of the groups is alphabetic, so in my case the “ALL” group comes after the groups that start with numbers, so pretty much at the bottom of the list.

Can I change the order of the groups so that the ALL group is always on top?

Oh, now I see that the sorting is pretty bad anyway for my purpose, I will need a natural sort.

I also managed to make a group for the currently opened sheets, would also be nice to have this group on top.

full code:

def BrowserSortParameter():
	br_org = BrowserOrganization.GetCurrentBrowserOrganizationForSheets(doc)
	br_folders = br_org.GetFolderItems(doc.ActiveView.Id)

	for folder in br_folders:
		BrowserSortParameter = doc.GetElement(folder.ElementId).Name
	return BrowserSortParameter 

def OpenSheets():
    outlist = []
    uiviews = uidoc.GetOpenUIViews()
    for uiview in uiviews:
        view = doc.GetElement(uiview.ViewId)
        if view.Category.Name == "Sheets" and view.Name != "None":
            outlist.append(view)
    return outlist

def Sheets():
    Sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    return Sheets

def SheetSetNames():
    SheetSetNames = []
    for s in Sheets:
		paravalue = s.LookupParameter(BrowserSortParameter).AsString()
		if paravalue not in SheetSetNames:
			SheetSetNames.append(paravalue)
    return SheetSetNames
    
def SheetsBySheetSet():
    outlist = []
    for SheetSetName in SheetSetNames:
        outlist2 = []
        for Sheet in Sheets:
            paravalue = Sheet.LookupParameter(BrowserSortParameter).AsString()
            if paravalue == SheetSetName:
                outlist2.append(Sheet)
        outlist.append(outlist2)
    return outlist
    
def AddOpenSheets():
    if OpenSheets:
        SheetSetNames.insert(0,"Open")
        SheetsBySheetSet.insert(0,OpenSheets)
    return SheetSetNames, SheetsBySheetSet
    

class MyOptionSheet(forms.TemplateListItem):
    @property
    def name(self):
        return '{} - {}'.format(
            self.item.SheetNumber,
            self.item.Name)

BrowserSortParameter = BrowserSortParameter()
OpenSheets = OpenSheets()
Sheets = Sheets()
SheetSetNames = SheetSetNames()
SheetsBySheetSet = SheetsBySheetSet()

SheetSetNames, SheetsBySheetSet = AddOpenSheets()

ops = {}
AllSheets = []
AllSheets.extend([MyOptionSheet(x) for x in Sheets if not x.IsPlaceholder])
ops['All'] = AllSheets

for SheetSetName, SheetsBySheetSet in zip(SheetSetNames,SheetsBySheetSet):
    sheets = []
    for Sheet in SheetsBySheetSet:
        sheets.append(MyOptionSheet(Sheet))
    ops[SheetSetName] = sheets

selectedSheets = forms.SelectFromList.show(ops, 'Title', multiselect=True)