is it possible to prompt a user for some input in a window, while the user is also doing stuff in Revit?
I want to make a tool that hides things in view based on what categories are selected with checkboxes in a prompt. Ideally, the user can keep the prompt open and to quickly hide/unhide categories while manipulating the view in Revit.
So far, all my prompts block out any input in Revit until the prompt is closed.
If you want to have eventhandlers that do stuff on revit events then it is slightly more complicated, and will require to unsubscribe from them on closing the window
If you don’t want to block user input during your tool’s operation, a modeless window that triggers external events is the only way to go. If you just try to use a modeless window that makes regular API calls, it will almost always crash Revit. You need to trigger your API calls within an external event.
I did so in this earlier forum post where I used a XAML UI to trigger external events. (you don’t have to read through the whole thread, just find where I posted the finished code near the bottom and study up on the code related to external events.) Ehsan has also posted a good video tutorial on the pyRevit youtube channel. It’s a lot to learn, but it works very well in the end. https://discourse.pyrevitlabs.io/t/xaml-ui-unresponsive-until-focus-is-switched-to-other-window/3162/8
This came up in the dynamo forum the other day. voltadynabim.blogspot.com posted an example and I tweaked it a bit so I could run it in Revit Python Shell. It is in IPY3, but you can look it up on the dynamo forum and see teh IPY2 comments. It’s a good working example.
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 = __revit__.uiap.ActiveUIDocument
#_doc = __revit__.uidoc.Document
#_app = __revit__.uiap.Application
#_view = _doc.ActiveView
doc = __revit__.ActiveUIDocument.Document
uidoc = __revit__.ActiveUIDocument
uiapp = __revit__
app = uiapp.Application
currview = 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"
def GetElements():
doc = __revit__.ActiveUIDocument.Document
elements = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors).WhereElementIsNotElementType()
return elements
# create UI
lst_elem = GetElements()
#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()