Looping to allow for multiple filtered selections?

I have a dynamic selection filter that I want to use to help organize tags on drawings. Tag types are selected in the xaml interface, user hits the go button, they then select tag elements with the aid of filters and can use arrows or mouse to shift the tag locations to clean up a drawing.

I want to be able to loop this action such that the user can deselect after moving a selection, make a new selection, and then repeat as many times as needed before hitting escape to regen the interface and select a new subset of tags. What’s the best way to set up a loop to allow for this action more than once? Here’s the basic code, it does what I want it to do, but only once. Thanks!

        def Move_Tags_Struct(self, button, eventargs):
                
                #this sets globals used to filter selection based on boxes checked in the interface
                self.Get_Tags_In_Focus()  

                self.Close()

                msfilter = MassSelectionFilter_Struct_Tags()
                
                selection = revit.get_selection()
                selection_list = revit.pick_rectangle(pick_filter=msfilter)

                ids = []
                for i in selection_list:
                        ids.append(i.Id)

                highlight = List[ElementId](ids)

                uidoc.Selection.SetElementIds(highlight)

                #Close Hidden and open a fresh window when the user hits escape?
                # if msvcrt.kbhit() and msvcrt.getch().decode() == chr(27):
                #         self.Close()
                #         Window_Generator_Struct()
                

Not exactly sure what you doing with the tags, but I think a
while True:
will let you keep going with the selection. when you hit finish, and then escape is how you exit the command after making multiple sets of selections/actions

look at the match tool in pyrevit towards bottom to see how its used

1 Like

This is just to help quickly arrange tags in the active view (clean up drawings.) The user selects tag types in the interface, which determines the selection filter, then hits the go button and draws a rectangle to select only the tags they want to move with the aid of the filter.

It works as it should (one time) but I want the user to be able to make a new selection (with the same filter active) move another group of tags, and repeat until breaking out of the cycle with ESC or some other trigger to return to the interface where they can select a different subset of tag types to filter by.

The While True loop seems to get me stuck without a pause in the cycle to allow for moving the selected tags. I thought subscribing to the SelectionChanged event to trigger another pick when the user finishes moving a selection and clicks in whitespace to deselect the previous set might work, but I’m not having any luck subscribing to that event. Also not sure how to create an open ended transaction to absorb the manual shift of the annotation elements. Seems like there should be a simpler way to go about it.

Maybe I just need to have the user define two points on the screen after making the selection and programmatically align the selected tags with the invisible line. At least I can picture how the transaction and triggers would work using that approach.

Look at how the match tool from pyrevit works

end of the script. it should allow you to make a selection, execute the commands needed, and then go back to asking for a selection. to exit the command, press esc after performing all the separate selections and executions

As far as I can tell, that approach doesn’t allow for a pause to move elements around independent of a Transaction in the python script. I can make it work just like the selection apps that come with pyRevit, ending the script with the pick, but then it requires reloading the app to make another one and waiting for it to spin up, which is the main thing I’m trying to avoid.

I’m looking for a way to keep the script on standby in the background while allowing the user to essentially execute an external command in moving the selected elements in the active view, then be ready to make another filtered pick when they signal it’s time, either by deselecting the one the last set or just making a different selection.

I’ve figured out an alternative solution that works well enough, though it doesn’t allow for the nudging of selected tags. This allows the user to define the shift in tag location for the current selection based on two points selected in the view, makes the move within a transaction, then is immediately set for another selection using the same filter. The try statement breaks the ‘while True’ when the user exits the selection operation and that brings the interface back up.

 def pick_elements(self, msfilter):
                
                selection_list = revit.pick_rectangle(pick_filter=msfilter)

                ids = []

                for i in selection_list:
        
                        ids.append(i.Id)

                highlight = List[ElementId](ids)

                uidoc.Selection.SetElementIds(highlight)
                
                return highlight
                
                

 def Move_Tags_Struct(self, button, eventargs):
              
              self.Get_Tags_In_Focus()  #this sets globals used to filter selection based on boxes checked in the interface
              msfilter = MassSelectionFilter_Struct_Tags()
              self.Close()
              
              selection = []

              while True:

                      try:
                              
                              if selection:

                                      pointOne = revit.pick_point("Representative taghead location?")
                                      pointTwo = revit.pick_point("Now where do want it?")

                                      shift = pointTwo - pointOne
                                      
                                      t = Transaction(doc, "Relocate Tags. . ." )
                                      t.Start()

                                      ElementTransformUtils.MoveElements(doc, selection, shift)
                                      
                                      selection = []

                                      t.Commit()


                              else:
                                      selection = self.pick_elements(msfilter)

                      except Exception:

                              Window_Generator_Struct()

                              break