Next button node or script

Hi, I am here to get help. I have run into an issue that I am unable to solve. Here we go: I have several elements with their IDs. I have used a bounding box node to get a 3D view of each element. My question is, can I view each element one by one through some “TASKDIAGO NEXT” button, so I can see each element, modify it, and then press “Next” to see the next element?

Not really. Dynamo executes the full graph all at once (more or less). Think of the graph execution as a single command in Revit. You can’t interrupt the command to do other things and then continue the command. If you’re going to modify each element manually then just create a set of 3D views for each element and then browse through each view as you modify the elements.

If you can script the modification of elements, even better.

Only real way to do that in dynamo is to interject a dialog box at each loop and then use that dialog to modify the information of the element. It won’t give you full access to all the Revit tools, but it would allow basic parameters to be changed.

  • Get Elements

    • For e in elements:
    • Zoom to element.
    • Isolate element
    • Get existing info to be changed.
    • Present dialog with info to be changed.
    • Pass info to dialog back to element.
    • Rinse
    • Repeat.
  • List item

The biggest problem with most coding isn’t making the computer do its thing. But managing the wildcard with his fingers on the keyboard.

With straight API, you could do this - but t is more involved.

3 Likes

Fair point to suggest injecting a UI to handle the modification within the process itself, but I’d think anything that can just be read and then written to based on value could just be scripted from the beginning. Definitely a possibility though.

1 Like

Hi,

a Python solution using IExternalEventHandler and a ListView in a non-modal Window(wpf)

zoomtodoors

code Python (compatible with PythonNet3 and IronPython3)
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
from System import  EventHandler, Uri
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

clr.AddReference('RevitAPIUI')
import Autodesk.Revit.UI as RUI
from Autodesk.Revit.UI import *

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)

clr.AddReference("System.Xml")
clr.AddReference("PresentationFramework")
clr.AddReference("System.Xml")
clr.AddReference("PresentationCore")
clr.AddReference("System.Windows")
import System.Windows.Controls 
from System.Windows.Controls import *
from System.IO import StringReader
from System.Xml import XmlReader
from System.Windows import LogicalTreeHelper 
from System.Windows.Markup import XamlReader, XamlWriter
from System.Windows import Window, Application

import time
import traceback

class MainForm(Window):
    string_xaml = '''
    <Window 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            Title="My WPF Window" Height="500" Width="350">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <!-- ListView -->
                <ListView x:Name="ItemsListView" Grid.Row="0" Margin="10" >
                    <ListView.View>
                        <GridView>
                            <GridViewColumn Header="Elements (Double Click to Zoom)" Width="300" DisplayMemberBinding="{Binding Name}"/>
                        </GridView>
                    </ListView.View>
                </ListView>
        
                <!-- Button -->
                <Button x:Name="ButtonOK" Grid.Row="1" Content="OK" Margin="10" Height="30" Width="100" HorizontalAlignment="Center"/>
            </Grid>
        </Window>'''
  
    def __init__(self, lst_elem):
        super().__init__() # remove or comment this line with IronPython2
        self.lst_elem = lst_elem
        xr = XmlReader.Create(StringReader(MainForm.string_xaml))
        root= XamlReader.Load(xr) 
        for prop_info in root.GetType().GetProperties():
            if prop_info.CanWrite:
                try:
                    setattr(self, prop_info.Name, prop_info.GetValue(root)) 
                except Exception as ex:
                    print(ex, prop_info.Name) # synthax modified for all engine IronPython2, IronPython3, PythonNet3
        #
        self.ext_eventZoom = None
        self.elem = None
        self.bbx_to_Zoom = None
        self.InitializeComponent()
        
    def InitializeComponent(self):
        self.ListViewElement = LogicalTreeHelper.FindLogicalNode(self, "ItemsListView")
        self.ListViewElement.ItemsSource  = self.lst_elem
        self.ListViewElement.MouseDoubleClick += self.ListView_MouseDoubleClick
        #
        self.ButtonOK = LogicalTreeHelper.FindLogicalNode(self, "ButtonOK") 
        self.ButtonOK.Click += self.ButtonOkClick

    def ListView_MouseDoubleClick(self, sender, e):
        try:
            self.elem = e.OriginalSource.DataContext
            print(self.elem)
            self.bbx_to_Zoom = self.elem.get_BoundingBox(None)
            self.ext_eventZoom.Raise()
        except Exception as ex:
            print(traceback.format_exc())
        
    def ButtonOkClick(self, sender, e):
        try:
            self.ext_eventZoom.Dispose()
            self.Close()
        except Exception as ex:
            print(traceback.format_exc())
            
class ModExternalEventZoom(IExternalEventHandler):
    __namespace__ = "ModExternalEventZoom_SDy8" 
    def __init__(self, objform):
        super().__init__() # remove or comment this line with IronPython2
        self.objform = objform
        
    def Execute(self, _uiap):        
        _uidoc = _uiap.ActiveUIDocument
        _doc = _uidoc.Document
        _app = _uiap.Application
        _view = _doc.ActiveView
        filterFunc = lambda x : not x.IsTemplate and x.ViewType == ViewType.ThreeD and (x.Name == "{3D - " + _app.Username  + "}" or x.Name == "{3D}")
        threeViewD = FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_Views).WhereElementIsNotElementType()\
                                                    .FirstOrDefault(System.Func[DB.Element, System.Boolean](filterFunc))
        #processing
        if  self.objform.bbx_to_Zoom is not None:    
            pt1 = self.objform.bbx_to_Zoom.Min
            pt2 = self.objform.bbx_to_Zoom.Max    
            tx = Transaction(_doc)
            tx.Start("MyEventZoom")                
                    
            try:
                threeViewD.SetSectionBox(self.objform.bbx_to_Zoom)
                _uidoc.Selection.SetElementIds(List[ElementId]([self.objform.elem.Id]))
            except Exception as ex:
                print(traceback.format_exc()) 
            tx.Commit()     
            _uidoc.RequestViewChange(threeViewD)
            try:
                uiviews = _uidoc.GetOpenUIViews()
                uiview = next((x for x in uiviews if x.ViewId == threeViewD.Id), None)
                if uiview is not None:    
                    uiview.ZoomAndCenterRectangle(pt1, pt2)
            except Exception as ex:
                print(traceback.format_exc())

    def GetName(self):
        return "ModExternalEventZoom"        
            
# create UI
lst_elem = UnwrapElement(IN[0])
TransactionManager.Instance.ForceCloseTransaction()
winObj = MainForm(lst_elem)

obj_handlerZoom = ModExternalEventZoom(winObj)    
ext_eventZoom = ExternalEvent.Create(obj_handlerZoom)	
# set attribute event
winObj.ext_eventZoom = ext_eventZoom

winObj.Show()

It will need to be adapted if
-you use ElementIds as input instead of Elements
-you use IronPython2 (I advise against using CPython3 for this example)

use AI for code comprehension if necessary

8 Likes

I tried with all three type of input its not running

I specified: It must be adapted for IronPython2,

To help you, I have just added a few comments to the code (see previous post).

Yes i tried with ironPython2 too still i got errror

post your code, and a screenshot


RevitWPFWindow.py (6.0 KB)

As I mentioned before, the code is not compatible with CPython3 (use IronPython2 with some modifications, IronPython3 or PythonNet3).

See, I ran through your code using the CPython3 engine. It ran with the UI interface, but I was unable to double-click the window run only once in my system. When I ran it again, it got an error. I tried using some AI tools to convert the script engine to IronPython2, but I still couldn’t get the result you showed. I don’t have much knowledge about Python, just a basic understanding. So, I am here to get help. Thank you

IT isn’t compatible with CPython3, so not being able to re-trigger the UI and not getting interaction in the window makes sense.

You need to install a compatible version of IronPython3 or PythonNet3 and set the Python node to use that engine, then the code should work.

Just use this code and READ the comments to adapt it to IronPython2

how, where to install compatible version ? current i am using 2022 Revit version it should be latest.

  1. Go to the package manager in Dynamo
  2. Search for the package
  3. Open the details
  4. Read the description
  5. Install the directed version based on what your Revit and Dynamo build are
3 Likes

thankyou it run successfully💕 it worked me in Ironpython3 version.

but i want to run it in lower version of Revit.

for lower versions use another independent script with IronPython2

Hello everyone! Please tell me what code on IronPython2 you mean? Excellent script, solves very necessary tasks, but unfortunately, on version 2022 and below, which I am currently using, it was not possible to run it, in version 2024 it works as shown in the video, commenting out two lines super().init() # delete or comment out this line using IronPython2. In version 2022 of Revit, the script runs, but when you double-click on the element, nothing happens. I tried updating the packages to the latest version.The code is taken from the 5th post, which is accepted as a solution. Thank you

Took the advice to look for a solution using artificial intelligence. Never used it to write code. AI suggested a working solution. Now in version 2021 and 2022 the code works as in the versions above. Many thanks to the creator of the code!
It is necessary to replace the class ModExternalEventZoom

class ModExternalEventZoom(IExternalEventHandler):  
    __namespace__ = "ModExternalEventZoom_SDy8"   
    def __init__(self, objform):  
        self.objform = objform  
        
    def Execute(self, _uiap):        
        _uidoc = _uiap.ActiveUIDocument  
        _doc = _uidoc.Document  
        
        # Check if the active view is a 3D view  
        activeView = _doc.ActiveView  
        
        if activeView.ViewType != ViewType.ThreeD:  
            print("No active 3D view.")  
            # Try to find a 3D view in the project  
            filterFunc = lambda x: not x.IsTemplate and x.ViewType == ViewType.ThreeD  
            threeViewD = FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_Views).WhereElementIsNotElementType()\
                .FirstOrDefault(System.Func[DB.Element, System.Boolean](filterFunc))  
            
            if threeViewD is None:  
                print("No 3D view found in the project.")  
                return  # Exit if no 3D view is found  

            # If the active view is not 3D, but there is a 3D view, use it  
            print("Using the first found 3D view.")  
            activeView = threeViewD  

        # Check for the presence of Bounding Box  
        if self.objform.bbx_to_Zoom is not None:    
            pt1 = self.objform.bbx_to_Zoom.Min  
            pt2 = self.objform.bbx_to_Zoom.Max    
            tx = Transaction(_doc)  
            tx.Start("MyEventZoom")                
                    
            try:  
                activeView.SetSectionBox(self.objform.bbx_to_Zoom)  
                _uidoc.Selection.SetElementIds(List[ElementId]([self.objform.elem.Id]))  
            except Exception as ex:  
                print(traceback.format_exc())   
            finally:  
                tx.Commit()     
                _uidoc.RequestViewChange(activeView)  

            try:  
                uiviews = _uidoc.GetOpenUIViews()  
                uiview = next((x for x in uiviews if x.ViewId == activeView.Id), None)  
                if uiview is not None:    
                    uiview.ZoomAndCenterRectangle(pt1, pt2)  
            except Exception as ex:  
                print(traceback.format_exc())  

    def GetName(self):  
        return "ModExternalEventZoom"      
2 Likes