pyRevit Documentation Release 4.5 eirannejad Jan 20, 2019
pyRevit DocumentationRelease 4.5
eirannejad
Jan 20, 2019
Getting Started
1 Anatomy of a pyRevit Script 31.1 Script Metadata Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 pyrevit.script Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.3 Appendix A: Builtin Parameters Provided by pyRevit Engine . . . . . . . . . . . . . . . . . . . . . 81.4 Appendix B: System Category Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2 Effective Output/Input 152.1 Clickable Element Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.2 Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.3 Code Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172.4 Progress bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172.5 Standard Prompts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.6 Standard Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222.7 Base Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312.8 Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3 Keyboard Shortcuts 413.1 Shift-Click: Alternate/Config Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413.2 Ctrl-Click: Debug Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413.3 Alt-Click: Show Script file in Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423.4 Ctrl-Shift-Alt-Click: Reload Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423.5 Shift-Win-Click: pyRevit Button Context Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4 Extensions and Commmands 434.1 Why do I need an Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.2 Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.3 Command Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444.4 Group Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454.5 Advanced Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474.6 Other Extension Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5 pyRevit Configuration 51
6 Usage Logger 53
7 pyRevit Installer 55
i
8 Load Sequence, Step 1: Revit Addon 578.1 The Complex Relationship of a C# Addin and a Python Script . . . . . . . . . . . . . . . . . . . . . 578.2 pyRevit loader script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578.3 pyRevitLoader Addin Source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9 Load Sequence, Step 2: IronPython Module 59
10 pyrevit 6110.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
11 pyrevit.api 7511.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
12 pyrevit.compat 7712.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
13 pyrevit.forms 7913.1 pyrevit.forms.utils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9413.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
14 pyrevit.framework 13314.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
15 pyrevit.script 13715.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
16 pyrevit.userconfig 15316.1 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
17 pyrevit.coreutils 16317.1 pyrevit.coreutils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16317.2 pyrevit.coreutils.envvars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20017.3 pyrevit.coreutils.appdata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20217.4 pyrevit.coreutils.pyutils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20917.5 pyrevit.coreutils.mathnet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21417.6 pyrevit.coreutils.moduleutils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
18 pyrevit.output 21718.1 pyrevit.output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21718.2 pyrevit.output.linkmaker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Python Module Index 237
ii
pyRevit Documentation, Release 4.5
Note: This documentation is a work-in-progress. Thanks for your patience.
Getting Started
I suggest reading this section completely as it provides 99% of what you will need to know for developing scripts inpyRevit environment. Other sections dive deeper into pyRevit inner workings.
• Anatomy of a pyRevit Script
• Effective Output/Input
• Keyboard Shortcuts
• Extensions and Commmands
• pyRevit Configuration
• Usage Logger
• pyRevit Installer
Getting Started 1
pyRevit Documentation, Release 4.5
2 Getting Started
CHAPTER 1
Anatomy of a pyRevit Script
pyRevit provides a few basic services to python scripts that use its engine. These fuctionalities are accessible througha few high level modules. This article is a quick look at these services and their associated python modules.
1.1 Script Metadata Variables
pyRevit looks for certain global scope variables in each scripts that provide metadata about the script and follow the__<name>__ format.
1.1.1 __title__
Button Title: When using the bundle name as the button name in Revit UI is not desired, or you want to add anewline character to the command name to better display the butonn name inside Revit UI Panel, you can define the__title__ variable in your script and set it to the desired button title.
__title__ = 'Sample\\nCommand'
3
pyRevit Documentation, Release 4.5
1.1.2 __doc__
Button Tooltip: Tooltips are displayed similar to the other buttons in Revit interface. You can define the tooltip for ascript using the doscstring at the top of the script or by explicitly defining __doc__ metadata variable.
# defining tooltip as the script docstring"""You can place the docstring (tooltip) at the top of the script file.This serves both as python docstring and also button tooltip in pyRevit.You should use triple quotes for standard python docstrings."""
# defining tooltip by setting metadata variable__doc__ = 'This is the text for the button tooltip associated with this→˓script.'
1.1.3 __author__
Script Author: You can define the script author as shown below. This will show up on the button tooltip.
__author__ = 'Ehsan Iran-Nejad'
1.1.4 __helpurl__
F1 Shortcut Help Url: xx
1.1.5 __min_revit_ver__
Min Revit Version Required: xx
1.1.6 __max_revit_ver__
Max Revit Version Supported: xx
1.1.7 __beta__
Script In Beta: xx
4 Chapter 1. Anatomy of a pyRevit Script
pyRevit Documentation, Release 4.5
1.1.8 __context__
Command Availability: Revit commands use standard IExternalCommandAvailability class to let Revitknow if they are available in different contexts. For example, if a command needs to work on a set of elements, it cantell Revit to deactivate the button unless the user has selected one or more elements.
In pyRevit, command availability is set through the __context__ variable. Currently, pyRevit support three typesof command availability types.
# Tool activates when at least one element is selected__context__ = 'Selection'
# Tools are active even when there are no documents available/open in Revit__context__ = 'zerodoc'
# Tool activates when all selected elements are of the given category or categories__context__ = '<Element Category>'__context__ = ['<Element Category>', '<Element Category>']
<Element Category> can be any of the standard Revit element categories. See Appendix B: System CategoryNames for a full list of system categories. You can use the List tool under pyRevit > Spy and list the standardcategories.
Here are a few examples:
# Tool activates when all selected elements are of the given category
__context__ = 'Doors'__context__ = 'Walls'__context__ = 'Floors'__context__ = ['Space Tags', 'Spaces']
1.2 pyrevit.script Module
All pyRevit scripts should use the pyrevit.scriptmodule to access pyRevit functionality unless listed otherwise.pyRevit internals are subject to changes and accessing them directly is not suggested.
Here is a list of supported modules for pyRevit scripts. Examples of using the functionality in these modules areprovided on this page.
pyrevit.script
This module provides access to output window (pyrevit.output), logging (pyrevit.coreutils.logger), temporary files (pyrevit.coreutils.appdata), and other misc fea-tures. See the module page for usage examples and full documentation of all available functions.
1.2.1 Logging
You can get the default logger for the script using pyrevit.script.get_logger().
from pyrevt import script
logger = script.get_logger()
logger.info('Test Log Level :ok_hand_sign:')
(continues on next page)
1.2. pyrevit.script Module 5
pyRevit Documentation, Release 4.5
(continued from previous page)
logger.warning('Test Log Level')
logger.critical('Test Log Level')
Critical and warning messages are printed in color for clarity. Normally debug messages are not printed. you can holdCTRL and click on a command button to put that command in DEBUG mode and see all its debug messages
logger.debug('Yesss! Here is the debug message')
1.2.2 Controlling Output Window
Each script can control its own output window:
from pyrevit import script
output = script.get_output()
output.set_height(600)output.get_title()output.set_title('More control please!')
See Effective Output/Input for more info.
1.2.3 Script Config
Each script can save and load configuration pyRevit’s user configuration file:
See pyrevit.output for more examples.
See pyrevit.script.get_config() and pyrevit.script.save_config() for the individual func-tions used here.
from pyrevit import script
config = script.get_config()
# set a new config parameter: firstparamconfig.firstparam = True
# saving configurationsscript.save_config()
# read the config parameter valueif config.firstparam:
do_task_A()
1.2.4 Logging Results
pyRevit has a usage logging system that can record all tool usages to either a json file or to a web server. Scripts canreturn custom data to this logging system.
In example below, the script reports the amount of time it saved to the logging system:
6 Chapter 1. Anatomy of a pyRevit Script
pyRevit Documentation, Release 4.5
from pyrevit import script
results = script.get_results()results.timesaved = 10
1.2.5 Using Temporary Files
Scripts can create 3 different types of data files:
• Universal files
These files are not marked by host Revit version and could be shared between all Revit versions andinstances. These data files are saved in pyRevit’s appdata directory and are NOT cleaned up at Revitrestarts.
See pyrevit.script.get_universal_data_file()
Note: Script should take care of cleaning up these data files.
# provide a unique file id and file extension# Method will return full path of the data filefrom pyrevit import scriptscript.get_universal_data_file(file_id, file_ext)
• Data files
These files are marked by host Revit version and could be shared between instances of host Revitversion Data files are saved in pyRevit’s appdata directory and are NOT cleaned up when Revitrestarts.
See pyrevit.script.get_data_file()
Note: Script should take care of cleaning up these data files.
# provide a unique file id and file extension# Method will return full path of the data filefrom pyrevit import scriptscript.get_data_file(file_id, file_ext)
• Instance Data files
These files are marked by host Revit version and process Id and are only available to current Revitinstance. This avoids any conflicts between similar scripts running under two or more Revit instances.Data files are saved in pyRevit’s appdata directory (with extension .tmp) and ARE cleaned up whenRevit restarts.
See pyrevit.script.get_instance_data_file()
# provide a unique file id and file extension# Method will return full path of the data filefrom pyrevit import scriptscript.get_instance_data_file(file_id)
• Document Data files
1.2. pyrevit.script Module 7
pyRevit Documentation, Release 4.5
(Shared only between instances of host Revit version): These files are marked by host Revit versionand name of Active Project and could be shared between instances of host Revit version. Data filesare saved in pyRevit’s appdata directory and are NOT cleaned up when Revit restarts.
See pyrevit.script.get_document_data_file()
Note: Script should take care of cleaning up these data files.
# provide a unique file id and file extension# Method will return full path of the data filefrom pyrevit import scriptscript.get_document_data_file(file_id, file_ext)
# You can also pass a document object to get a data file for that# document (use document name in file naming)script.get_document_data_file(file_id, file_ext, doc)
1.3 Appendix A: Builtin Parameters Provided by pyRevit Engine
Variables listed below are provided for every script in pyRevit.
Note: It’s strongly advised not to read or write values from these variables unless necessary. The pyrevit moduleprovides wrappers around these variables that are safe to use.
# Revit UIApplication is accessible through:__revit__
# Command data provided to this command by Revit is accessible through:__commandData__
# selection of elements provided to this command by Revit__elements__
# pyRevit engine manager that is managing this engine__ipyenginemanager__
# This variable is True if command is being run in a cached engine__cachedengine__
# pyRevit external command object wrapping the command being run__externalcommand__
# information about the pyrevit command being run__commandpath__ # main script path__alternatecommandpath__ # alternate script path__commandname__ # command name__commandbundle__ # command bundle name__commandextension__ # command extension name__commanduniqueid__ # command unique id
# This variable is True if user CTRL-Clicks the button__forceddebugmode__
(continues on next page)
8 Chapter 1. Anatomy of a pyRevit Script
pyRevit Documentation, Release 4.5
(continued from previous page)
# This variable is True if user SHIFT-Clicks the button__shiftclick__
# results dictionary__result__
1.4 Appendix B: System Category Names
Adaptive PointsAir Terminal TagsAir TerminalsAnalysis Display StyleAnalysis ResultsAnalytical Beam TagsAnalytical BeamsAnalytical Brace TagsAnalytical BracesAnalytical Column TagsAnalytical ColumnsAnalytical Floor TagsAnalytical FloorsAnalytical Foundation SlabsAnalytical Isolated Foundation TagsAnalytical Isolated FoundationsAnalytical Link TagsAnalytical LinksAnalytical Node TagsAnalytical NodesAnalytical Slab Foundation TagsAnalytical SpacesAnalytical SurfacesAnalytical Wall Foundation TagsAnalytical Wall FoundationsAnalytical Wall TagsAnalytical WallsAnnotation Crop BoundaryArea Load TagsArea TagsAreasAssembliesAssembly TagsBoundary ConditionsBrace in Plan View SymbolsCable Tray Fitting TagsCable Tray FittingsCable Tray RunsCable Tray TagsCable TraysCallout BoundaryCallout HeadsCalloutsCamerasCasework
(continues on next page)
1.4. Appendix B: System Category Names 9
pyRevit Documentation, Release 4.5
(continued from previous page)
Casework TagsCeiling TagsCeilingsColor Fill LegendsColumnsCommunication Device TagsCommunication DevicesConduit Fitting TagsConduit FittingsConduit RunsConduit TagsConduitsConnection SymbolsContour LabelsCrop BoundariesCurtain GridsCurtain Panel TagsCurtain PanelsCurtain System TagsCurtain SystemsCurtain Wall MullionsData Device TagsData DevicesDetail Item TagsDetail ItemsDimensionsDisplacement PathDoor TagsDoorsDuct AccessoriesDuct Accessory TagsDuct Color FillDuct Color Fill LegendsDuct Fitting TagsDuct FittingsDuct Insulation TagsDuct InsulationsDuct Lining TagsDuct LiningsDuct PlaceholdersDuct SystemsDuct TagsDuctsElectrical CircuitsElectrical EquipmentElectrical Equipment TagsElectrical Fixture TagsElectrical FixturesElectrical Spare/Space CircuitsElevation MarksElevationsEntourageFilled regionFire Alarm Device TagsFire Alarm DevicesFlex Duct TagsFlex Ducts
(continues on next page)
10 Chapter 1. Anatomy of a pyRevit Script
pyRevit Documentation, Release 4.5
(continued from previous page)
Flex Pipe TagsFlex PipesFloor TagsFloorsFoundation Span Direction SymbolFurnitureFurniture System TagsFurniture SystemsFurniture TagsGeneric AnnotationsGeneric Model TagsGeneric ModelsGrid HeadsGridsGuide GridHVAC ZonesImports in FamiliesInternal Area Load TagsInternal Line Load TagsInternal Point Load TagsKeynote TagsLevel HeadsLevelsLighting Device TagsLighting DevicesLighting Fixture TagsLighting FixturesLine Load TagsLinesMasking RegionMassMass Floor TagsMass TagsMatchlineMaterial TagsMaterialsMechanical EquipmentMechanical Equipment TagsMEP Fabrication ContainmentMEP Fabrication Containment TagsMEP Fabrication DuctworkMEP Fabrication Ductwork TagsMEP Fabrication Hanger TagsMEP Fabrication HangersMEP Fabrication PipeworkMEP Fabrication Pipework TagsMulti-Category TagsMulti-Rebar AnnotationsNurse Call Device TagsNurse Call DevicesPanel Schedule GraphicsParkingParking TagsPart TagsPartsPipe AccessoriesPipe Accessory Tags
(continues on next page)
1.4. Appendix B: System Category Names 11
pyRevit Documentation, Release 4.5
(continued from previous page)
Pipe Color FillPipe Color Fill LegendsPipe Fitting TagsPipe FittingsPipe Insulation TagsPipe InsulationsPipe PlaceholdersPipe SegmentsPipe TagsPipesPiping SystemsPlan RegionPlantingPlanting TagsPlumbing Fixture TagsPlumbing FixturesPoint CloudsPoint Load TagsProject InformationProperty Line Segment TagsProperty TagsRailing TagsRailingsRampsRaster ImagesRebar Cover ReferencesRebar Set ToggleRebar ShapeReference LinesReference PlanesReference PointsRender RegionsRevision Cloud TagsRevision CloudsRoadsRoof TagsRoofsRoom TagsRoomsRouting PreferencesSchedule GraphicsScope BoxesSection BoxesSection LineSection MarksSectionsSecurity Device TagsSecurity DevicesShaft OpeningsSheetsSiteSite TagsSpace TagsSpacesSpan Direction SymbolSpecialty EquipmentSpecialty Equipment Tags
(continues on next page)
12 Chapter 1. Anatomy of a pyRevit Script
pyRevit Documentation, Release 4.5
(continued from previous page)
Spot CoordinatesSpot Elevation SymbolsSpot ElevationsSpot SlopesSprinkler TagsSprinklersStair Landing TagsStair PathsStair Run TagsStair Support TagsStair TagsStair Tread/Riser NumbersStairsStructural AnnotationsStructural Area ReinforcementStructural Area Reinforcement SymbolsStructural Area Reinforcement TagsStructural Beam System TagsStructural Beam SystemsStructural Column TagsStructural ColumnsStructural Connection TagsStructural ConnectionsStructural Fabric AreasStructural Fabric ReinforcementStructural Fabric Reinforcement SymbolsStructural Fabric Reinforcement TagsStructural Foundation TagsStructural FoundationsStructural FramingStructural Framing TagsStructural Internal LoadsStructural Load CasesStructural LoadsStructural Path ReinforcementStructural Path Reinforcement SymbolsStructural Path Reinforcement TagsStructural RebarStructural Rebar Coupler TagsStructural Rebar CouplersStructural Rebar TagsStructural Stiffener TagsStructural StiffenersStructural Truss TagsStructural TrussesSwitch SystemTelephone Device TagsTelephone DevicesText NotesTitle BlocksTopographyView ReferenceView TitlesViewportsViewsWall TagsWalls
(continues on next page)
1.4. Appendix B: System Category Names 13
pyRevit Documentation, Release 4.5
(continued from previous page)
Window TagsWindowsWire TagsWiresZone Tags
14 Chapter 1. Anatomy of a pyRevit Script
CHAPTER 2
Effective Output/Input
2.1 Clickable Element Links
work in progress
15
pyRevit Documentation, Release 4.5
2.2 Tables
work in progress
16 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
2.3 Code Output
work in progress
2.4 Progress bars
work in progress
2.3. Code Output 17
pyRevit Documentation, Release 4.5
2.5 Standard Prompts
2.5.1 Alerts
pyrevit.forms.alert(msg, title=None, sub_msg=None, expanded=None, footer=”, ok=True, can-cel=False, yes=False, no=False, retry=False, warn_icon=True, options=None,exitscript=False)
Show a task dialog with given message.
Parameters
• msg (str) – message to be displayed
• title (str, optional) – task dialog title
• ok (bool, optional) – show OK button, defaults to True
• cancel (bool, optional) – show Cancel button, defaults to False
• yes (bool, optional) – show Yes button, defaults to False
• no (bool, optional) – show NO button, defaults to False
• retry (bool, optional) – show Retry button, defaults to False
• options (list[str], optional) – list of command link titles in order
• exitscript (bool, optional) – exit if cancel or no, defaults to False
Returns True if okay, yes, or retry, otherwise False
Return type bool
Example
>>> from pyrevit import forms>>> forms.alert('Are you sure?',... ok=False, yes=True, no=True, exitscript=True)
18 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
pyrevit.forms.check_workshared(doc=None, message=’Model is not workshared.’)Verify if model is workshared and notify user if not.
Parameters
• doc (DB.Document) – target document, current of not provided
• message (str) – prompt message if returning False
Returns True if doc is workshared
Return type bool
class pyrevit.forms.WarningBar(height=32, **kwargs)Show warning bar at the top of Revit window.
Parameters title (string) – warning bar text
Example
>>> with WarningBar(title='my warning'):... # do stuff
2.5. Standard Prompts 19
pyRevit Documentation, Release 4.5
2.5.2 Command Options
class pyrevit.forms.CommandSwitchWindow(context, title, width, height, **kwargs)Standard form to select from a list of command options.
Parameters
• context (list[str]) – list of command options to choose from
• switches (list[str]) – list of on/off switches
• message (str) – window title message
• config (dict) – dictionary of config dicts for options or switches
Returns name of selected option
Return type str
Returns if switches option is used, returns a tuple of selection option name and dict of switches
Return type tuple(str, dict)
Example
This is an example with series of command options:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> forms.CommandSwitchWindow.show(ops, message='Select Option')'option2'
A more advanced example of combining command options, on/off switches, and option or switch configurationoptions:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> switches = ['switch1', 'switch2']>>> cfgs = {'option1': { 'background': '0xFF55FF'}}>>> rops, rswitches = forms.CommandSwitchWindow.show(... ops,... switches=switches... message='Select Option',... config=cfgs... )>>> rops'option2'
(continues on next page)
20 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
(continued from previous page)
>>> rswitches{'switch1': False, 'switch2': True}
2.5.3 Showing Progress
class pyrevit.forms.ProgressBar(height=32, **kwargs)Show progress bar at the top of Revit window.
Parameters
• title (string) – progress bar text, defaults to 0/100 progress format
• indeterminate (bool) – create indeterminate progress bar
• cancellable (bool) – add cancel button to progress bar
• step (int) – update progress intervals
Example
>>> from pyrevit import forms>>> count = 1>>> with forms.ProgressBar(title='my command progress message') as pb:... # do stuff... pb.update_progress(count, 100)... count += 1
Progress bar title could also be customized to show the current and total progress values. In example below, theprogress bar message will be in format “0 of 100”
>>> with forms.ProgressBar(title='{value} of {max_value}') as pb:
By default progress bar updates the progress every time the .update_progress method is called. For operationswith a large number of max steps, the gui update process time will have a significate effect on the overall
2.5. Standard Prompts 21
pyRevit Documentation, Release 4.5
execution time of the command. In these cases, set the value of step argument to something larger than 1. Inexample below, the progress bar updates once per every 10 units of progress.
>>> with forms.ProgressBar(title='message', steps=10):
Progress bar could also be set to indeterminate for operations of unknown length. In this case, the progress barwill show an infinitely running ribbon:
>>> with forms.ProgressBar(title='message', indeterminate=True):
if cancellable is set on the object, a cancel button will show on the progress bar and .cancelled attribute will beset on the ProgressBar instance if users clicks on cancel button:
>>> with forms.ProgressBar(title='message',... cancellable=True) as pb:... # do stuff... if pb.cancelled:... # wrap up and cancel operation
2.6 Standard Dialogs
2.6.1 Pick File
pyrevit.forms.pick_file(file_ext=”, files_filter=”, init_dir=”, restore_dir=True, multi_file=False,unc_paths=False)
Pick file dialog to select a destination file.
22 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
Parameters
• file_ext (str) – file extension
• files_filter (str) – file filter
• init_dir (str) – initial directory
• restore_dir (bool) – restore last directory
• multi_file (bool) – allow select multiple files
• unc_paths (bool) – return unc paths
Returns file path or list of file paths if multi_file=True
Return type str or list[str]
Example
>>> from pyrevit import forms>>> forms.pick_file(file_ext='csv')... r'C:\output\somefile.csv'
>>> forms.pick_file(file_ext='csv', multi_file=True)... [r'C:\output\somefile1.csv', r'C:\output\somefile2.csv']
>>> forms.pick_file(files_filter='All Files (*.*)|*.*|''Excel Workbook (*.xlsx)|*.xlsx|''Excel 97-2003 Workbook|*.xls',
multi_file=True)... [r'C:\output\somefile1.xlsx', r'C:\output\somefile2.xls']
pyrevit.forms.save_file(file_ext=”, files_filter=”, init_dir=”, default_name=”, restore_dir=True,unc_paths=False)
Save file dialog to select a destination file for data.
Parameters
• file_ext (str) – file extension
• files_filter (str) – file filter
• init_dir (str) – initial directory
• default_name (str) – default file name
• restore_dir (bool) – restore last directory
• unc_paths (bool) – return unc paths
Returns file path
Return type str
Example
>>> from pyrevit import forms>>> forms.save_file(file_ext='csv')... r'C:\output\somefile.csv'
2.6. Standard Dialogs 23
pyRevit Documentation, Release 4.5
2.6.2 Pick Folder
pyrevit.forms.pick_folder(title=None)Show standard windows pick folder dialog.
Parameters title (str, optional) – title for the window
Returns folder path
Return type str
24 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
2.6.3 Select Views
pyrevit.forms.select_views(title=’Select Views’, button_name=’Select’, width=500, multi-ple=True, filterfunc=None, doc=None)
Standard form for selecting views.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for views; defaults to active doc-ument
Returns list of selected views
Return type list[DB.View]
Example
>>> from pyrevit import forms>>> forms.select_views()
(continues on next page)
2.6. Standard Dialogs 25
pyRevit Documentation, Release 4.5
(continued from previous page)
... [<Autodesk.Revit.DB.View object>,
... <Autodesk.Revit.DB.View object>]
2.6.4 Select Sheets
pyrevit.forms.select_sheets(title=’Select Sheets’, button_name=’Select’, width=500, multi-ple=True, filterfunc=None, doc=None)
Standard form for selecting sheets.
Sheets are grouped into sheet sets and sheet set can be selected from a drop down box at the top of window.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for sheets; defaults to active doc-ument
Returns list of selected sheets
Return type list[DB.ViewSheet]
26 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit import forms>>> forms.select_sheets()... [<Autodesk.Revit.DB.ViewSheet object>,... <Autodesk.Revit.DB.ViewSheet object>]
2.6.5 Select Revisions
pyrevit.forms.select_revisions(title=’Select Revision’, button_name=’Select’, width=500, mul-tiple=True, filterfunc=None, doc=None)
Standard form for selecting revisions.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for revisions; defaults to activedocument
Returns list of selected revisions
2.6. Standard Dialogs 27
pyRevit Documentation, Release 4.5
Return type list[DB.Revision]
Example
>>> from pyrevit import forms>>> forms.select_revisions()... [<Autodesk.Revit.DB.Revision object>,... <Autodesk.Revit.DB.Revision object>]
2.6.6 Select From Open Documents
pyrevit.forms.select_open_docs(title=’Select Open Documents’, button_name=’OK’, width=500,multiple=True, filterfunc=None)
Standard form for selecting open documents.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
Returns list of selected documents
28 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
Return type list[DB.Document]
Example
>>> from pyrevit import forms>>> forms.select_open_docs()... [<Autodesk.Revit.DB.Document object>,... <Autodesk.Revit.DB.Document object>]
2.6.7 Select From List (Single, Multiple)
2.6. Standard Dialogs 29
pyRevit Documentation, Release 4.5
class pyrevit.forms.SelectFromList(context, title, width, height, **kwargs)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 TemplateListItem. This class provides the necessary mechanism to make this form work both forselecting items from a list, and from a list of checkboxes. See the list of arguments below for additional optionsand features.
Parameters
• context (list[str] or dict[list[str]]) – list of items to be selected fromOR dict of list of items to be selected from. use dict when input items need to be groupede.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). defaultsto False
• return_all (bool, optional) – return all items. This is handly when some inputitems have states and the script needs to check the state changes on all items. This optionsworks in multiselect mode only. defaults to False
• filterfunc (function) – filter function to be applied to context items.
• group_selector_title (str) – title for list group selector. defaults to ‘List Group’
30 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
• default_group (str) – name of defautl group to be selected
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')
This module also provides a wrapper base class TemplateListItem for when the checkbox option is wrap-ping another element, e.g. a Revit ViewSheet. Derive from this base class and define the name property tocustomize 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]
class pyrevit.forms.TemplateListItem(orig_item, checkable=True, name_attr=None)Base class for checkbox option wrapping another object.
2.7 Base Forms
2.7.1 Input Dialog for pyRevit Search
2.7. Base Forms 31
pyRevit Documentation, Release 4.5
pyrevit.forms.SearchPrompt(search_db, width, height, **kwargs)Standard prompt for pyRevit search.
Parameters
• search_db (list) – list of possible search targets
• search_tip (str) – text to show in grayscale when search box is empty
• switches (str) – list of switches
• width (int) – width of search prompt window
• height (int) – height of search prompt window
Returns matched strings, and dict of switches if provided str: matched string if switches are notprovided.
Return type str, dict
Example
>>> from pyrevit import forms>>> # assume search input of '/switch1 target1'>>> matched_str, args, switches = forms.SearchPrompt.show(... search_db=['target1', 'target2', 'target3', 'target4'],... switches=['/switch1', '/switch2'],... search_tip='pyRevit Search'... )... matched_str'target1'... args['--help', '--branch', 'branchname']... switches{'/switch1': True, '/switch2': False}
2.7.2 Generic Forms
class pyrevit.forms.TemplatePromptBar(height=32, **kwargs)Template context-manager class for creating prompt bars.
Prompt bars are show at the top of the active Revit window and are designed for better prompt visibility.
Parameters
• height (int) – window height
• **kwargs – other arguments to be passed to _setup()
class pyrevit.forms.TemplateUserInputWindow(context, title, width, height, **kwargs)Base class for pyRevit user input standard forms.
Parameters
• context (any) – window context element(s)
• title (str) – window title
• width (int) – window width
• height (int) – window height
32 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
• **kwargs – other arguments to be passed to _setup()
class pyrevit.forms.WPFWindow(xaml_source, literal_string=False, handle_esc=True)WPF Window base class for all pyRevit forms.
Parameters
• xaml_source (str) – xaml source filepath or xaml content
• literal_string (bool) – xaml_source contains xaml content, not filepath
• handle_esc (bool) – handle Escape button and close the window
Example
>>> from pyrevit import forms>>> layout = '<Window ShowInTaskbar="False" ResizeMode="NoResize" ' \>>> 'WindowStartupLocation="CenterScreen" ' \>>> 'HorizontalContentAlignment="Center">' \>>> '</Window>'>>> w = forms.WPFWindow(layout, literal_string=True)>>> w.show()
2.8 Graphs
Step 1: Create a chart object for the chart type that you want. We’ll add data to this later. . .
from pyrevit import script
output = script.get_output()
# Line chartchart = output.make_line_chart()# Bar chartchart = output.make_bar_chart()# Bubble chartchart = output.make_bubble_chart()# Radar chartchart = output.make_radar_chart()# Polar chartchart = output.make_polar_chart()# Pie chartchart = output.make_pie_chart()# Doughnut chartchart = output.make_doughnut_chart()
Step 1-a: Optional: Setup the chart title, and other options. the full list of options for every chart is available onCharts.js Documentation page. Some of the properties have their own sub-properties, for example the title optionfor the charts has multiple sub-properties as shown below. The value for these type of properties should be a dictionaryof the sub-properties you’d like to set. All this is explained clearly in the Charts.js Documentation
chart.set_style('height:150px')
chart.options.title = {'display': True,'text':'Chart Title',
(continues on next page)
2.8. Graphs 33
pyRevit Documentation, Release 4.5
(continued from previous page)
'fontSize': 18,'fontColor': '#000','fontStyle': 'bold'}
Step 2: Now let’s add data to the chart. Every chart object has a data property chart.data that we can interactwith to add datasets to the chart. Different types of charts need different types of data sets in terms of how data isorganized, so the chart can present multiple data sets correctly. I’m providing two examples here, one for a simple linechart (showing 3 different data sets) and another for a radial chart (also showing 3 different data sets within the samechart). They’re all very similar to each other though.
# setting the charts x line data labelschart.data.labels = ['Monday', 'Tuesday',
'Wednesday', 'Thursday','Friday', 'Saturday', 'Sunday']
# Let's add the first dataset to the chart object# we'll give it a name: set_aset_a = chart.data.new_dataset('set_a')# And let's add data to it.# These are the data for the Y axis of the graph# The data length should match the length of data for the X axisset_a.data = [12, 19, 3, 17, 6, 3, 7]# Set the color for this graphset_a.set_color(0xFF, 0x8C, 0x8D, 0.8)
Step 3: The last step is to ask the chart object to draw itself.
# Before drawing the chart you can randomize the colors# if you have not added any color to the datasets.chart.randomize_colors()
# Finally let's draw the chartchart.draw()
2.8.1 Line charts
See the comments in the script for more info
# get line chart objectchart = output.make_line_chart()
# this is a list of labels for the X axis of the line graphchart.data.labels = ['Monday', 'Tuesday',
'Wednesday', 'Thursday','Friday', 'Saturday', 'Sunday']
# Let's add the first dataset to the chart object# we'll give it a name: set_aset_a = chart.data.new_dataset('set_a')# And let's add data to it.# These are the data for the Y axis of the graph# The data length should match the length of data for the X axisset_a.data = [12, 19, 3, 17, 6, 3, 7]# Set the color for this graph
(continues on next page)
34 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
(continued from previous page)
set_a.set_color(0xFF, 0x8C, 0x8D, 0.8)# You can also set custom options for this graph# See the Charts.js documentation for all the optionsset_b.fill = False
# Same as above for a new data set: set_bset_b = chart.data.new_dataset('set_b')# Obviously a different set of data and a different colorset_b.data = [2, 29, 5, 5, 2, 3, 10]set_b.set_color(0xFF, 0xCE, 0x56, 0.8)
# Same as above for a new data set: set_cset_c = chart.data.new_dataset('set_c')# Obviously a different set of data and a different colorset_c.data = [55, 12, 2, 20,→˓18, 6, 22]set_c.set_color(0x36, 0xA2, 0xEB, 0.8)
And here is the result:
2.8.2 Pie charts
See the comments in the script for more info
# get pie chart objectchart = output.make_pie_chart()
# Set the labels for the circumference axischart.data.labels = ['A', 'B', 'C']
# Create new data setsset_a = chart.data.new_dataset('set_a')set_a.data = [100, 20, 50]# You can set a different color for each pie of the chart
(continues on next page)
2.8. Graphs 35
pyRevit Documentation, Release 4.5
(continued from previous page)
set_a.backgroundColor = ["#560764", "#1F6CB0", "#F98B60"]
set_b = chart.data.new_dataset('set_b')set_b.data = [50, 30, 80]set_b.backgroundColor = ["#913175", "#70A3C4", "#FFC057"]
set_c = chart.data.new_dataset('set_c')set_c.data = [40, 20, 10]set_c.backgroundColor = ["#DD5B82", "#E7E8F5", "#FFE084"]
And here is the result:
2.8.3 Bar charts
See the comments in the script for more info
# get bar chart objectchart = output.make_bar_chart()
And here is the result:
36 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
2.8.4 Bubble charts
See the comments in the script for more info
# get bubble chart objectchart = output.make_bubble_chart()
And here is the result:
2.8.5 Radar charts
See the comments in the script for more info
2.8. Graphs 37
pyRevit Documentation, Release 4.5
# get radar chart objectchart = output.make_radar_chart()
And here is the result:
2.8.6 Polar Area charts
See the comments in the script for more info
# get polar chart objectchart = output.make_polar_chart()
And here is the result:
38 Chapter 2. Effective Output/Input
pyRevit Documentation, Release 4.5
2.8.7 Doghnut charts
See the comments in the script for more info
# get doughnut chart objectchart = output.make_doughnut_chart()
And here is the result:
2.8. Graphs 39
pyRevit Documentation, Release 4.5
2.8.8 Charts engine
Here is a little info on how the charts engine works: the pyRevit charts module is pyrevit.coreutils.charts.This is the module that the output window interacts with to create the charts.
The charts module provides the chart object and handles the creation of datasets. The first thing it does when drawingthe graph is to create a html <canvas> element and assign a unique id to it:
<canvas id="chart123456"></canvas>
Then it parses the input data and creates a JSON representation of the data. The JSON string (json_data) will beinserted into a template javascript. This javascript creates a Chart object from the Chart.js library:
var ctx = document.getElementById('{}').getContext('2d');var chart = new Chart(ctx, json_data);
and finally, the pyRevit chart object, injects this dynamically created javascript into the <head> of the output windowWebBrowser component:
output.inject_script(js_code)
40 Chapter 2. Effective Output/Input
CHAPTER 3
Keyboard Shortcuts
3.1 Shift-Click: Alternate/Config Script
Each pyRevit command bundle can contain two scripts:
*script.py is the main script.
*config.py is the Alternale/Config script.
SHIFT-clicking on a ui button will run the alternate/config script. This alternate script is generally used to configurethe main tool. Try Shift clicking on the Match tool in pyRevit > Modify panel and see the configuration window. Thentry Shift clicking on the Settings tool in pyRevit panel slide-out and see what it does.
If you don’t define the configuration script, you can check the value of __shiftclick__ in your scripts to changescript behaviour. This is the method that the Settings command is using to open the config file location in explorer:
if __shiftclick__:do_task_A()
else:do_task_B()
3.2 Ctrl-Click: Debug Mode
CTRL-clicking on a ui button will run the script in DEBUG mode and will allow the script to print all debug messages.You can check the value of __forceddebugmode__ variable to see if the script is running in Debug mode to changescript behaviour if neccessary.
if __forceddebugmode__:do_task_A()
else:do_task_B()
41
pyRevit Documentation, Release 4.5
3.3 Alt-Click: Show Script file in Explorer
ALT-clicking on a ui button will show the associated script file in windows explorer.
3.4 Ctrl-Shift-Alt-Click: Reload Engine
If you’re using pyRevit Rocket mode, this keyboard combination will force pyRevit to discard the cached engine forthis command and use a new fresh engine. If you are developing scripts for pyRevit and using external modules, you’llneed to use this keyboard combination after changes to the imported module source codes. Since the modules arealready imported in the cached engine, you’d need a new fresh engine to reload the modules.
3.5 Shift-Win-Click: pyRevit Button Context Menu
Shows the context menu for the pyRevit command. See image below:
42 Chapter 3. Keyboard Shortcuts
CHAPTER 4
Extensions and Commmands
4.1 Why do I need an Extension
pyRevit’s extensions system has evolved again to be more flexible and easier to work with. We’ll dive right into howyou can add your own extension, but first let’s answer one important question:
Q: Why would I need to create a separate extension? Why Can’t I just add my scripts to the current pyRevit tools?
A: Because pyRevit is a git repository and the real benefit of that is that you can keep it always updated without theneed to unsinstall and install the newer versions. To keep this system running without issues, I highly recommendnot to mess with the pyRevit git repository folders and contents and pyRevit makes it really easy to add your ownextensions. You can even add tools to the standard pyRevit tab in your own extensions. I’ll show you how.
Besides, by creating a separate extension, you’ll have all your precious scripts and tools in a safe place and away fromthe changes being made to the core pyRevit. They can even live somewhere on your company shared drives and beshared between your teams.
4.2 Extensions
Each extension is a group of tools, organized in bundles to be easily accessible through the user interface.
Extensions are organized in a folder bundle with .extension postfix.
Like this one:
43
pyRevit Documentation, Release 4.5
There are two steps that you need to follow to create your own extensions:
• Setup external extension folder:
First, is to create a separate folder for all your custom extensions and tell pyRevit to load yourextensions from this folder. This is done in the Settings window, under the Custom Extension folderssection. This way your precious extensions will stay out of the pyRevit installation and are safe.
• Create custom extension bundle:
Next, create your <your extension name>.extension folder under your custom exten-sions folder. Read the sections below on how to create bundles for your commands and the userinterface.
4.3 Command Bundles
A bundle is a folder named in the format bundle_name.bundle_type.
Like these:
44 Chapter 4. Extensions and Commmands
pyRevit Documentation, Release 4.5
The most basic bundle is a command bundle. There are more than one type of command bundles but a .pushbuttonbundle explained here covers 90% of the use cases.
4.3.1 Pushbutton Bundle
Each command bundle needs to include a script either in python or C#:
script.py:
The first script file under the bundle that ends with script.py will be used as the script for this com-mand bundle.
Examples: BuildWall_script.py Analyse-script.py
config.py:
This for python commands configuration. If this script is provided then Shift-Clicking on the button willrun this command instead. Also a black dot will be added to the button name in the user interface to showthat this command has a custom configuration tool. See Shift-Click: Alternate/Config Script
script.cs:
This for C# commands and works similarly to python scripts. This C# script will be compiled in runtime.
icon.png:
Command bundles can include an icon for their user interface.
lib/:
Bundles can define a python library (a sub-folder named lib inside the bundle will do). This library willbe accessible to the python script in this bundle. This organizes all the python modules that are necessaryfor this python script to work into one folder.
This is how a command bundle looks like:
And this is a more advanced command bundle with a configuration script and configuration window definition file:
4.4 Group Bundles
Now that we have explained the command bundles, we need a way to organize these commands into a user-friendlyinterface. Let’s introduce Group Bundles
A group bundle is a bundle that can contain command bundles and other group bundles. They come in all differentshapes and sizes but they have a few features in common:
• They can contain command bundles and other group bundles. (But I’ve already said that)
4.4. Group Bundles 45
pyRevit Documentation, Release 4.5
• icon.png: Bundle can include an icon for their user interface.
• lib/: The can define a python library (a sub-folder named lib inside the bundle will do). This library will beaccessible to all the commands in this bundle and other child group bundles. This folder can contain all thepython modules that are being shared between the child commands.
• _layout: This is a text file inside the bundle that defines the order in which the bundle contents should be createdin the user interface. The contents of this file should be the names of the component in the order that they shouldbe created in the user interface.
Here is _layout file example. This is a layout file for a Group Bundle that has a series of push buttons and othergroup bundles under itself:
PushButton APushButton BPullDown A---PullDown BStack3 A>>>PushButton CPullDown C
Oh, and also:
• --- This line will add a separator to the interface (You can use more than 3 - characters. For example---------- still works as a separator)
• >>> Any bundle after this line will be created inside a slide-out. This works for panel bundles only. (You canuse more than 3 > characters. For example >>>>>>>>> still works as a slide-out)
And this is how a typical Group Bundle looks like:
Now let’s talk about the different Group Bundles:
4.4.1 Tab Bundle
This bundle creates a Tab in the Ribbon with the bundle name.
Example Can ContainpyRevit.tab Only .panel Group Bundles.
46 Chapter 4. Extensions and Commmands
pyRevit Documentation, Release 4.5
4.4.2 Panel Bundle
This bundle creates a Panel in a Ribbon Tab with the bundle name.
Example Can ContainpyRevit.panel Any other bundle type
4.4.3 PullDown Bundle
This bundle creates a Pulldown Button in a Ribbon Panel or a Stack, with the bundle name and icon.
Example Can ContainpyRevit.pulldown Only command bundles
4.4.4 SplitButton Bundle
This bundle creates a Split Button button in a Ribbon Panel or a Stack, with the bundle name and icon.
Example Can ContainpyRevit.splitbutton Only command bundles
4.4.5 SplitPushButton Bundle
This bundle creates a Split Push Button button (The sticky split button) in a Ribbon Panel or a Stack, with the bundlename and icon.
Example Can ContainpyRevit.splitpushbutton Only command bundles
4.4.6 Stack Bundle: Two Buttons
This bundle creates a stack of 2 buttons in a panel.
Example Can ContainpyRevit.stack2 Can contain: .pulldown .splitbutton .splitpushbutton
4.4.7 Stack Bundle: Three Buttons
Just like the .stack2 bundle but with 3 buttons instead.
4.5 Advanced Bundles
There are a few more advanced bundle types in pyRevit as well. Here is some quick intro on these bundles.
4.5. Advanced Bundles 47
pyRevit Documentation, Release 4.5
4.5.1 Smart Button Bundle
ExamplepyRevit.smartbutton
Smart buttons are python scripts that are written like modules. They should define __selfinit__ function asshown below. This function gets executed at startup time to give a chance to the button to initialize itself (e.g set itsicon based on its state).
The __selfinit__ must return True if the initialization is successful and False if it is not. pyRevit will notcreate the button if the initialization returns False and is unsuccessful.
def __selfinit__(script_cmp, ui_button_cmp, __rvt__):"""Args:
script_cmp: script component that contains info on this scriptui_button_cmp: this is the UI button component__rvt__: Revit UIApplication
Returns:bool: Return True if successful, False if not
"""
run_self_initialization()
4.5.2 No Button Bundle
ExamplepyRevit.nobutton
No-Button bundles are just like Pushbutton bundles except that they will never show up inside Revit UI and thus don’tneed any icons. The only method to run these commands is through pyRevit Search tool. These commands are meantfor more advanced commands that not every user needs.
4.5.3 Panel Button Bundle
ExamplepyRevit.panelbutton
Panle Button bundles are just like Pushbutton bundles except that they will be set as the panel configuration button(small arrow at the corner of UI Panels). These bundles do not need to have an icon as the standard Small arrow iconis used for panel configuration buttons by default. These commands work just like any pyRevit command but theirprimary purpose should be to configure set of related tools in a panel.
4.5.4 Link Button Bundle
ExamplepyRevit.linkbutton
48 Chapter 4. Extensions and Commmands
pyRevit Documentation, Release 4.5
Link buttons can call a function from another Addin. To make a link button define the parameters below in the bundlesscript.py:
Note: For this button to work properly, the target addin must be already loaded when this button is being created,otherwise Revit can not tie the UI button to an assembly that is not loaded.
__assembly__ = 'Addin assembly name'__commandclass__ = 'Class name for the command'
For example to call the Interactive Python Shell from RevitPythonShell addin:
__assembly__ = 'RevitPythonShell'__commandclass__ = 'IronPythonConsoleCommand'
4.6 Other Extension Types
4.6.1 Library Extensions
Library extensions are created to share IronPython modules between all extensions. They’re in essence IronPythonmodule packages. Some users might decide to develop an IronPython library (e.g. RevitPythonWrapper Library) thatother users can use in their tools and benefit from.
Library extensions are identified by .lib postfix. The library extension folder address will be added to the sys.path of all the other extensions by the loader.
4.6. Other Extension Types 49
pyRevit Documentation, Release 4.5
50 Chapter 4. Extensions and Commmands
CHAPTER 5
pyRevit Configuration
work in progress
51
pyRevit Documentation, Release 4.5
52 Chapter 5. pyRevit Configuration
CHAPTER 6
Usage Logger
work in progress
53
pyRevit Documentation, Release 4.5
54 Chapter 6. Usage Logger
CHAPTER 7
pyRevit Installer
work in progress
pyRevit Core
• Load Sequence, Step 1: Revit Addon
• Load Sequence, Step 2: IronPython Module
55
pyRevit Documentation, Release 4.5
56 Chapter 7. pyRevit Installer
CHAPTER 8
Load Sequence, Step 1: Revit Addon
8.1 The Complex Relationship of a C# Addin and a Python Script
Let’s talk basics:
• Revit Addons are written in C# and are windows .dll files.
• pyRevit is written as an IronPython module. (actually a bit more complex than that)
• Revit doesn’t have an option to run external python scripts.
Thus, we need a way to teach Revit how to run a python script when it’s starting up.
The solution was to create a custom C# addin to create a python engine and run a script. We’ll call this addinpyRevitLoader.dll. I wanted to keep this addin as simple as possible since it’s the only statically-compiledpiece of code in this project. The rest of the task of loading pyRevit were assigned to a loader python script that isbeing run by the loader addin.
So:
• pyRevitLoader.dll is a simple C# addin for Revit that runs python scripts
• pyRevitLoader.dll loads pyRevitLoader.py at startup.
• pyRevitLoader.py sets up the environment and loads pyRevit.
It’s that simple really. See the sources below.
From here on, the documentation page for the pyrevit.loader module will take you through all the steps of parsingextensions, making dll assemblies and creating the user interface for the parsed extensions.
8.2 pyRevit loader script
Here is the full source of pyRevitLoader.py. The docstring explains how it works.
57
pyRevit Documentation, Release 4.5
# -*- coding: utf-8 -*-#pylint: disable=C0103,W1401,E0401,E0602"""
This is the starting point for pyRevit. At Revit loads the PyRevitLoader.dlladdon at startup. This dll then creates an ironpython engine and runspyRevitLoader.py (this script). It's the job of this script to setup theenvironment for the pyrevit module (pyrevitlib\pyrevit) and load a new pyRevitsession. This script needs to add the directory path of the pyrevit lib folderso the pyrevit module can be imported and used.
"""
import sysimport os.path as op
# add the library location to the system search pathsrepo_path = op.dirname(op.dirname(op.dirname(__file__)))sys.path.append(op.join(repo_path, 'pyrevitlib'))
# now pyrevit can be importedfrom pyrevit.loader import sessionmgr
# ask sessionmgr to start a new sessionsessionmgr.load_session()
8.3 pyRevitLoader Addin Source
The source code for pyRevitLoader addin is under: pyrevitlib/pyrevit/addin/<loader version>/Source
58 Chapter 8. Load Sequence, Step 1: Revit Addon
CHAPTER 9
Load Sequence, Step 2: IronPython Module
work in progress
Modules
• pyrevit
• pyrevit.api
• pyrevit.compat
• pyrevit.forms
• pyrevit.framework
• pyrevit.script
• pyrevit.userconfig
• pyrevit.coreutils
• pyrevit.output
59
pyRevit Documentation, Release 4.5
60 Chapter 9. Load Sequence, Step 2: IronPython Module
CHAPTER 10
pyrevit
pyRevit root level config for all pyrevit sub-modules.
Examples
>>> from pyrevit import DB, UI>>> from pyrevit import PyRevitException, PyRevitIOError
>>> # pyrevit module has global instance of the>>> # _HostAppPostableCommand and _ExecutorParams classes already created>>> # import and use them like below>>> from pyrevit import HOST_APP>>> from pyrevit import EXEC_PARAMS
class pyrevit.PyRevitExceptionBase class for all pyRevit Exceptions.
Parameters args and message are derived from Exception class.
class pyrevit.PyRevitIOErrorGeneric IO error in pyRevit.
class pyrevit._HostAppPostableCommand(name, key, id, rvtobj)Private namedtuple for passing information about a PostableCommand
namestr – Postable command name
keystr – Postable command key string
idint – Postable command id
rvtobjRevitCommandId – Postable command Id Object
61
pyRevit Documentation, Release 4.5
class pyrevit._HostApplication(host_uiapp)Private Wrapper for Current Instance of Revit.
Provides version info and comparison functionality, alongside providing info on the active screen, active docu-ment and ui-document, available postable commands, and other functionality.
Parameters host_uiapp (UIApplication) – Instance of running host.
Example
>>> hostapp = _HostApplication(__revit__)>>> hostapp.is_newer_than(2017)
activeviewReturn view that is active (UIDocument.ActiveView).
appReturn Application provided to the running command.
available_serversReturn list of available Revit server names.
buildstr – Return build number (e.g. ‘20170927_1515(x64)’).
docReturn active Document.
docsReturn list of open Document objects.
get_postable_commands()Return list of postable commands.
Returns list of _HostAppPostableCommand
is_exactly(version)bool: Return True if host app is equal to provided version.
Parameters version (str or int) – version to check against.
is_newer_than(version, or_equal=False)bool: Return True if host app is newer than provided version.
Parameters version (str or int) – version to check against.
is_older_than(version)bool: Return True if host app is older than provided version.
Parameters version (str or int) – version to check against.
procSystem.Diagnostics.Process – Return current process object.
proc_idint – Return current process id.
proc_namestr – Return current process name.
proc_pathstr – Return file path for the current process main module.
62 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
proc_screenintptr – Return handle to screen hosting current process.
proc_screen_scalefactorfloat – Return scaling for screen hosting current process.
proc_screen_workareaSystem.Drawing.Rectangle – Return screen working area.
subversionstr – Return subversion number (e.g. ‘2018.3’).
uiappReturn UIApplication provided to the running command.
uidocReturn active UIDocument.
usernamestr – Return the username from Revit API (Application.Username).
versionstr – Return version number (e.g. ‘2018’).
version_namestr – Return version name (e.g. ‘Autodesk Revit 2018’).
class pyrevit._ExecutorParamsPrivate Wrapper that provides runtime environment info.
command_alt_pathstr – Return current command alternate script path.
command_bundlestr – Return current command bundle name.
command_dataExternalCommandData – Return current command data.
command_extensionstr – Return current command extension name.
command_modebool – Check if pyrevit is running in pyrevit command context.
command_namestr – Return current command name.
command_pathstr – Return current command path.
command_uniqueidstr – Return current command unique id.
doc_modebool – Check if pyrevit is running by doc generator.
engine_mgrPyRevitBaseClasses.EngineManager – Return engine manager.
engine_verstr – Return PyRevitLoader.ScriptExecutor hardcoded version.
63
pyRevit Documentation, Release 4.5
executed_from_uibool – Check if command was executed from ui.
first_loadbool – Check whether pyrevit is not running in pyrevit command.
forced_debug_modebool – Check if command is in debug mode.
pyrevit_commandPyRevitBaseClasses.PyRevitCommandRuntime – Return command.
result_dictDictionary<String, String> – Return results dict for logging.
window_handlePyRevitBaseClasses.ScriptOutput – Return output window.
10.1 Implementation
"""pyRevit root level config for all pyrevit sub-modules.
Examples:>>> from pyrevit import DB, UI>>> from pyrevit import PyRevitException, PyRevitIOError
>>> # pyrevit module has global instance of the>>> # _HostAppPostableCommand and _ExecutorParams classes already created>>> # import and use them like below>>> from pyrevit import HOST_APP>>> from pyrevit import EXEC_PARAMS
"""#pylint: disable=W0703,C0302,C0103,C0413import sysimport osimport os.path as opfrom collections import namedtupleimport tracebackimport clr #pylint: disable=E0401
try:clr.AddReference('PyRevitLoader')
except Exception:# probably older IronPython engine not being able to# resolve to an already loaded assembly.# PyRevitLoader is executing this script so it should be referabe.pass
try:import PyRevitLoader
except ImportError:# this means that pyRevit is _not_ being loaded from a pyRevit engine# e.g. when importing from RevitPythonShellPyRevitLoader = None
(continues on next page)
64 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
(continued from previous page)
PYREVIT_ADDON_NAME = 'pyRevit'VERSION_MAJOR = 4VERSION_MINOR = 6BUILD_METADATA = '.12'
# -----------------------------------------------------------------------------# config environment paths# -----------------------------------------------------------------------------# main pyrevit repo foldertry:
# 3 steps back for <home>/Lib/pyrevitHOME_DIR = op.dirname(op.dirname(op.dirname(__file__)))
except NameError:raise Exception('Critical Error. Can not find home directory.')
# BIN directoryBIN_DIR = op.join(HOME_DIR, 'bin')
# main pyrevit lib foldersMAIN_LIB_DIR = op.join(HOME_DIR, 'pyrevitlib')MISC_LIB_DIR = op.join(HOME_DIR, 'site-packages')
# path to pyrevit moduleMODULE_DIR = op.join(MAIN_LIB_DIR, 'pyrevit')
# loader directoryLOADER_DIR = op.join(MODULE_DIR, 'loader')
# addin directoryADDIN_DIR = op.join(LOADER_DIR, 'addin')
# if loader module is available means pyRevit is being executed by Revit.if PyRevitLoader:
ENGINES_DIR = \op.join(BIN_DIR, 'engines', PyRevitLoader.ScriptExecutor.EngineVersion)
ADDIN_RESOURCE_DIR = op.join(BIN_DIR, 'engines','Source', 'pyRevitLoader', 'Resources')
# otherwise it might be under test, or documentation processing.# so let's keep the symbols but set to None (fake the symbols)else:
ENGINES_DIR = ADDIN_RESOURCE_DIR = None
# add the framework dll path to the search pathssys.path.append(BIN_DIR)sys.path.append(ADDIN_DIR)sys.path.append(ENGINES_DIR)
# now we can start importing stufffrom pyrevit.compat import safe_strtypefrom pyrevit.framework import Processfrom pyrevit.framework import Windowsfrom pyrevit.framework import Formsfrom pyrevit.api import DB, UI
# -----------------------------------------------------------------------------# Base Exceptions
(continues on next page)
10.1. Implementation 65
pyRevit Documentation, Release 4.5
(continued from previous page)
# -----------------------------------------------------------------------------TRACEBACK_TITLE = 'Traceback:'
# General Exceptionsclass PyRevitException(Exception):
"""Base class for all pyRevit Exceptions.
Parameters args and message are derived from Exception class."""
@propertydef msg(self):
"""Return exception message."""if self.args:
return self.args[0] #pylint: disable=E1136else:
return ''
def __str__(self):"""Process stack trace and prepare report for output window."""sys.exc_type, sys.exc_value, sys.exc_traceback = sys.exc_info()try:
tb_report = traceback.format_tb(sys.exc_traceback)[0]if self.msg:
return '{}\n\n{}\n{}'.format(self.msg,TRACEBACK_TITLE,tb_report)
else:return '{}\n{}'.format(TRACEBACK_TITLE, tb_report)
except Exception:return Exception.__str__(self)
class PyRevitIOError(PyRevitException):"""Generic IO error in pyRevit."""
pass
# -----------------------------------------------------------------------------# Wrapper for __revit__ builtin parameter set in scope by C# Script Executor# -----------------------------------------------------------------------------# namedtuple for passing information about a PostableCommand_HostAppPostableCommand = namedtuple('_HostAppPostableCommand',
['name', 'key', 'id', 'rvtobj'])"""Private namedtuple for passing information about a PostableCommand
Attributes:name (str): Postable command namekey (str): Postable command key stringid (int): Postable command idrvtobj (``RevitCommandId``): Postable command Id Object
"""
class _HostApplication(object):(continues on next page)
66 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Private Wrapper for Current Instance of Revit.
Provides version info and comparison functionality, alongside providinginfo on the active screen, active document and ui-document, availablepostable commands, and other functionality.
Args:host_uiapp (``UIApplication``): Instance of running host.
Example:>>> hostapp = _HostApplication(__revit__)>>> hostapp.is_newer_than(2017)
"""
def __init__(self, host_uiapp):self._uiapp = host_uiappself._postable_cmds = []
@propertydef uiapp(self):
"""Return UIApplication provided to the running command."""return self._uiapp
@propertydef app(self):
"""Return Application provided to the running command."""return self.uiapp.Application
@propertydef uidoc(self):
"""Return active UIDocument."""return getattr(self.uiapp, 'ActiveUIDocument', None)
@propertydef doc(self):
"""Return active Document."""return getattr(self.uidoc, 'Document', None)
@propertydef activeview(self):
"""Return view that is active (UIDocument.ActiveView)."""return getattr(self.uidoc, 'ActiveView', None)
@activeview.setterdef activeview(self, value):
"""Set the active view in user interface."""setattr(self.uidoc, 'ActiveView', value)
@propertydef docs(self):
"""Return :obj:`list` of open :obj:`Document` objects."""return getattr(self.app, 'Documents', None)
@propertydef available_servers(self):
"""Return :obj:`list` of available Revit server names."""return list(self.app.GetRevitServerNetworkHosts())
(continues on next page)
10.1. Implementation 67
pyRevit Documentation, Release 4.5
(continued from previous page)
@propertydef version(self):
"""str: Return version number (e.g. '2018')."""return self.app.VersionNumber
@propertydef subversion(self):
"""str: Return subversion number (e.g. '2018.3')."""return self.app.SubVersionNumber
@propertydef version_name(self):
"""str: Return version name (e.g. 'Autodesk Revit 2018')."""return self.app.VersionName
@propertydef build(self):
"""str: Return build number (e.g. '20170927_1515(x64)')."""return self.app.VersionBuild
@propertydef username(self):
"""str: Return the username from Revit API (Application.Username)."""uname = self.app.Usernameuname = uname.split('@')[0] # if username is email# removing dots since username will be used in file naminguname = uname.replace('.', '')return uname
@propertydef proc(self):
"""System.Diagnostics.Process: Return current process object."""return Process.GetCurrentProcess()
@propertydef proc_id(self):
"""int: Return current process id."""return Process.GetCurrentProcess().Id
@propertydef proc_name(self):
"""str: Return current process name."""return Process.GetCurrentProcess().ProcessName
@propertydef proc_path(self):
"""str: Return file path for the current process main module."""return Process.GetCurrentProcess().MainModule.FileName
@propertydef proc_screen(self):
"""``intptr``: Return handle to screen hosting current process."""return Forms.Screen.FromHandle(
Process.GetCurrentProcess().MainWindowHandle)
@property(continues on next page)
68 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
(continued from previous page)
def proc_screen_workarea(self):"""``System.Drawing.Rectangle``: Return screen working area."""screen = HOST_APP.proc_screenif screen:
return screen.WorkingArea
@propertydef proc_screen_scalefactor(self):
"""float: Return scaling for screen hosting current process."""screen = HOST_APP.proc_screenif screen:
actual_wdith = Windows.SystemParameters.PrimaryScreenWidthscaled_width = screen.PrimaryScreen.WorkingArea.Widthreturn abs(scaled_width / actual_wdith)
def is_newer_than(self, version, or_equal=False):"""bool: Return True if host app is newer than provided version.
Args:version (str or int): version to check against.
"""if or_equal:
return int(self.version) >= int(version)else:
return int(self.version) > int(version)
def is_older_than(self, version):"""bool: Return True if host app is older than provided version.
Args:version (str or int): version to check against.
"""return int(self.version) < int(version)
def is_exactly(self, version):"""bool: Return True if host app is equal to provided version.
Args:version (str or int): version to check against.
"""return int(self.version) == int(version)
def get_postable_commands(self):"""Return list of postable commands.
Returns::obj:`list` of :obj:`_HostAppPostableCommand`
"""# if list of postable commands is _not_ already created# make the list and store in instance parameterif not self._postable_cmds:
for pc in UI.PostableCommand.GetValues(UI.PostableCommand):try:
rcid = UI.RevitCommandId.LookupPostableCommandId(pc)self._postable_cmds.append(
# wrap postable command info in custom namedtuple_HostAppPostableCommand(name=safe_strtype(pc),
(continues on next page)
10.1. Implementation 69
pyRevit Documentation, Release 4.5
(continued from previous page)
key=rcid.Name,id=rcid.Id,rvtobj=rcid)
)except Exception:
# if any error occured when querying postable command# or its info, pass silentlypass
return self._postable_cmds
try:# Create an intance of host application wrapper# making sure __revit__ is availableHOST_APP = _HostApplication(__revit__) #pylint: disable=E0602
except Exception:raise Exception('Critical Error: Host software is not supported. '
'(__revit__ handle is not available)')
# -----------------------------------------------------------------------------# Wrapper to access builtin parameters set in scope by C# Script Executor# -----------------------------------------------------------------------------class _ExecutorParams(object):
"""Private Wrapper that provides runtime environment info."""
@property # read-onlydef engine_mgr(self):
"""``PyRevitBaseClasses.EngineManager``: Return engine manager."""try:
return __ipyenginemanager__except NameError:
raise AttributeError()
@property # read-onlydef engine_ver(self):
"""str: Return PyRevitLoader.ScriptExecutor hardcoded version."""if PyRevitLoader:
return PyRevitLoader.ScriptExecutor.EngineVersion
@property # read-onlydef first_load(self):
"""bool: Check whether pyrevit is not running in pyrevit command."""# if no output window is set by the executor, it means that pyRevit# is loading at Revit startup (not reloading)return True if EXEC_PARAMS.window_handle is None else False
@property # read-onlydef pyrevit_command(self):
"""``PyRevitBaseClasses.PyRevitCommandRuntime``: Return command."""try:
return __externalcommand__except NameError:
return None
@property # read-only(continues on next page)
70 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
(continued from previous page)
def forced_debug_mode(self):"""bool: Check if command is in debug mode."""if self.pyrevit_command:
return self.pyrevit_command.DebugModeelse:
return False
@property # read-onlydef executed_from_ui(self):
"""bool: Check if command was executed from ui."""if self.pyrevit_command:
return self.pyrevit_command.ExecutedFromUIelse:
return False
@property # readdef window_handle(self):
"""``PyRevitBaseClasses.ScriptOutput``: Return output window."""if self.pyrevit_command:
return self.pyrevit_command.OutputWindow
@property # read-onlydef command_path(self):
"""str: Return current command path."""if '__commandpath__' in __builtins__ \
and __builtins__['__commandpath__']:return __builtins__['__commandpath__']
elif self.pyrevit_command:return op.dirname(self.pyrevit_command.ScriptSourceFile)
@property # read-onlydef command_alt_path(self):
"""str: Return current command alternate script path."""if '__alternatecommandpath__' in __builtins__ \
and __builtins__['__alternatecommandpath__']:return __builtins__['__alternatecommandpath__']
elif self.pyrevit_command:return op.dirname(self.pyrevit_command.AlternateScriptSourceFile)
@property # read-onlydef command_name(self):
"""str: Return current command name."""if '__commandname__' in __builtins__ \
and __builtins__['__commandname__']:return __builtins__['__commandname__']
elif self.pyrevit_command:return self.pyrevit_command.CommandName
@property # read-onlydef command_bundle(self):
"""str: Return current command bundle name."""if '__commandbundle__' in __builtins__ \
and __builtins__['__commandbundle__']:return __builtins__['__commandbundle__']
elif self.pyrevit_command:return self.pyrevit_command.CommandBundle
(continues on next page)
10.1. Implementation 71
pyRevit Documentation, Release 4.5
(continued from previous page)
@property # read-onlydef command_extension(self):
"""str: Return current command extension name."""if '__commandextension__' in __builtins__ \
and __builtins__['__commandextension__']:return __builtins__['__commandextension__']
elif self.pyrevit_command:return self.pyrevit_command.CommandExtension
@property # read-onlydef command_uniqueid(self):
"""str: Return current command unique id."""if '__commanduniqueid__' in __builtins__ \
and __builtins__['__commanduniqueid__']:return __builtins__['__commanduniqueid__']
elif self.pyrevit_command:return self.pyrevit_command.CommandUniqueId
@propertydef command_data(self):
"""``ExternalCommandData``: Return current command data."""if self.pyrevit_command:
return self.pyrevit_command.CommandData
@propertydef doc_mode(self):
"""bool: Check if pyrevit is running by doc generator."""try:
return __sphinx__except NameError:
return False
@propertydef command_mode(self):
"""bool: Check if pyrevit is running in pyrevit command context."""return self.pyrevit_command is not None
@propertydef result_dict(self):
"""``Dictionary<String, String>``: Return results dict for logging."""if self.pyrevit_command:
return self.pyrevit_command.GetResultsDictionary()
# create an instance of _ExecutorParams wrapping current runtime.EXEC_PARAMS = _ExecutorParams()
# -----------------------------------------------------------------------------# config user environment paths# -----------------------------------------------------------------------------# user env pathsif EXEC_PARAMS.doc_mode:
ALLUSER_PROGRAMDATA = USER_ROAMING_DIR = USER_SYS_TEMP = USER_DESKTOP = \EXTENSIONS_DEFAULT_DIR = THIRDPARTY_EXTENSIONS_DEFAULT_DIR = ' '
else:ALLUSER_PROGRAMDATA = os.getenv('programdata')
(continues on next page)
72 Chapter 10. pyrevit
pyRevit Documentation, Release 4.5
(continued from previous page)
USER_ROAMING_DIR = os.getenv('appdata')USER_SYS_TEMP = os.getenv('temp')USER_DESKTOP = op.expandvars('%userprofile%\\desktop')
# verify directory per issue #369if not USER_DESKTOP or not op.exists(USER_DESKTOP):
USER_DESKTOP = USER_SYS_TEMP
# default extensions directoryEXTENSIONS_DEFAULT_DIR = op.join(HOME_DIR, 'extensions')THIRDPARTY_EXTENSIONS_DEFAULT_DIR = \
op.join(USER_ROAMING_DIR, PYREVIT_ADDON_NAME, 'Extensions')
# create paths for pyrevit filesif EXEC_PARAMS.doc_mode:
PYREVIT_ALLUSER_APP_DIR = PYREVIT_APP_DIR = PYREVIT_VERSION_APP_DIR = ' 'else:
# pyrevit file directoryPYREVIT_ALLUSER_APP_DIR = op.join(ALLUSER_PROGRAMDATA, PYREVIT_ADDON_NAME)PYREVIT_APP_DIR = op.join(USER_ROAMING_DIR, PYREVIT_ADDON_NAME)PYREVIT_VERSION_APP_DIR = op.join(PYREVIT_APP_DIR, HOST_APP.version)
# add runtime paths to sys.paths# this will allow importing any dynamically compiled DLLs that# would be placed under this paths.for pyrvt_app_dir in [PYREVIT_APP_DIR,
PYREVIT_VERSION_APP_DIR,THIRDPARTY_EXTENSIONS_DEFAULT_DIR]:
if not op.isdir(pyrvt_app_dir):try:
os.mkdir(pyrvt_app_dir)sys.path.append(pyrvt_app_dir)
except Exception as err:raise PyRevitException('Can not access pyRevit '
'folder at: {} | {}'.format(pyrvt_app_dir, err))
else:sys.path.append(pyrvt_app_dir)
# -----------------------------------------------------------------------------# standard prefixes for naming pyrevit files (config, appdata and temp files)# -----------------------------------------------------------------------------if EXEC_PARAMS.doc_mode:
PYREVIT_FILE_PREFIX_UNIVERSAL = PYREVIT_FILE_PREFIX = \PYREVIT_FILE_PREFIX_STAMPED = None
PYREVIT_FILE_PREFIX_UNIVERSAL_USER = PYREVIT_FILE_PREFIX_USER = \PYREVIT_FILE_PREFIX_STAMPED_USER = None
else:# e.g. pyRevit_PYREVIT_FILE_PREFIX_UNIVERSAL = '{}_'.format(PYREVIT_ADDON_NAME)PYREVIT_FILE_PREFIX_UNIVERSAL_REGEX = \
r'^' + PYREVIT_ADDON_NAME + r'_(?P<fname>.+)'
# e.g. pyRevit_2018_PYREVIT_FILE_PREFIX = '{}_{}_'.format(PYREVIT_ADDON_NAME,
HOST_APP.version)(continues on next page)
10.1. Implementation 73
pyRevit Documentation, Release 4.5
(continued from previous page)
PYREVIT_FILE_PREFIX_REGEX = \r'^' + PYREVIT_ADDON_NAME + r'_(?P<version>\d{4})_(?P<fname>.+)'
# e.g. pyRevit_2018_14422_PYREVIT_FILE_PREFIX_STAMPED = '{}_{}_{}_'.format(PYREVIT_ADDON_NAME,
HOST_APP.version,HOST_APP.proc_id)
PYREVIT_FILE_PREFIX_STAMPED_REGEX = \r'^' + PYREVIT_ADDON_NAME \+ r'_(?P<version>\d{4})_(?P<pid>\d+)_(?P<fname>.+)'
# e.g. pyRevit_eirannejad_PYREVIT_FILE_PREFIX_UNIVERSAL_USER = '{}_{}_'.format(PYREVIT_ADDON_NAME,
HOST_APP.username)PYREVIT_FILE_PREFIX_UNIVERSAL_USER_REGEX = \
r'^' + PYREVIT_ADDON_NAME + r'_(?P<user>.+)_(?P<fname>.+)'
# e.g. pyRevit_2018_eirannejad_PYREVIT_FILE_PREFIX_USER = '{}_{}_{}_'.format(PYREVIT_ADDON_NAME,
HOST_APP.version,HOST_APP.username)
PYREVIT_FILE_PREFIX_USER_REGEX = \r'^' + PYREVIT_ADDON_NAME \+ r'_(?P<version>\d{4})_(?P<user>.+)_(?P<fname>.+)'
# e.g. pyRevit_2018_eirannejad_14422_PYREVIT_FILE_PREFIX_STAMPED_USER = '{}_{}_{}_{}_'.format(PYREVIT_ADDON_NAME,
HOST_APP.version,HOST_APP.username,HOST_APP.proc_id)
PYREVIT_FILE_PREFIX_STAMPED_USER_REGEX = \r'^' + PYREVIT_ADDON_NAME \+ r'_(?P<version>\d{4})_(?P<user>.+)_(?P<pid>\d+)_(?P<fname>.+)'
74 Chapter 10. pyrevit
CHAPTER 11
pyrevit.api
Provide access to Revit API.
Example
>>> from pyrevit.api import AdWindows>>> from pyrevit.api import NSJson
11.1 Implementation
"""Provide access to Revit API.
Example:>>> from pyrevit.api import AdWindows>>> from pyrevit.api import NSJson
"""
#pylint: disable=E0401,W0611,W0703,C0413from pyrevit.framework import clr
clr.AddReference('RevitAPI')clr.AddReference('RevitAPIUI')clr.AddReference('AdWindows')clr.AddReference('UIFramework')clr.AddReference('UIFrameworkServices')
import UIFrameworkimport UIFrameworkServices
import Autodesk.Internal as AdInternalimport Autodesk.Private as AdPrivate
(continues on next page)
75
pyRevit Documentation, Release 4.5
(continued from previous page)
import Autodesk.Windows as AdWindows
from Autodesk.Revit import Attributesfrom Autodesk.Revit import DBfrom Autodesk.Revit import UI
# try loading some utility modules shipped with revittry:
clr.AddReference('Newtonsoft.Json')import Newtonsoft.Json as NSJson
except Exception:pass
76 Chapter 11. pyrevit.api
CHAPTER 12
pyrevit.compat
python engine compatibility module.
Example
>>> from pyrevit.compat import IRONPY277>>> from pyrevit.compat import safe_strtype
12.1 Implementation
"""python engine compatibility module.
Example:>>> from pyrevit.compat import IRONPY277>>> from pyrevit.compat import safe_strtype
"""
import sys
PY2 = sys.version_info[0] == 2PY3 = sys.version_info[0] == 3IRONPY273 = sys.version_info[:3] == (2, 7, 3)IRONPY277 = sys.version_info[:3] == (2, 7, 7)
#pylint: disable=C0103safe_strtype = str
if PY2:safe_strtype = unicode #pylint: disable=E0602
77
pyRevit Documentation, Release 4.5
78 Chapter 12. pyrevit.compat
CHAPTER 13
pyrevit.forms
Reusable WPF forms for pyRevit.
Example
>>> from pyrevit.forms import WPFWindow
class pyrevit.forms.CommandSwitchWindow(context, title, width, height, **kwargs)Standard form to select from a list of command options.
Parameters
• context (list[str]) – list of command options to choose from
• switches (list[str]) – list of on/off switches
• message (str) – window title message
• config (dict) – dictionary of config dicts for options or switches
Returns name of selected option
Return type str
Returns if switches option is used, returns a tuple of selection option name and dict of switches
Return type tuple(str, dict)
Example
This is an example with series of command options:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> forms.CommandSwitchWindow.show(ops, message='Select Option')'option2'
79
pyRevit Documentation, Release 4.5
A more advanced example of combining command options, on/off switches, and option or switch configurationoptions:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> switches = ['switch1', 'switch2']>>> cfgs = {'option1': { 'background': '0xFF55FF'}}>>> rops, rswitches = forms.CommandSwitchWindow.show(... ops,... switches=switches... message='Select Option',... config=cfgs... )>>> rops'option2'>>> rswitches{'switch1': False, 'switch2': True}
_setup(**kwargs)Private method to be overriden by subclasses for window setup.
handle_click(sender, args)Handle mouse click.
handle_input_key(sender, args)Handle keyboard inputs.
process_option(sender, args)Handle click on command option button.
search_txt_changed(sender, args)Handle text change in search box.
class pyrevit.forms.GetValueWindow(context, title, width, height, **kwargs)Standard form to get simple values from user.
Args:
Example
>>> from pyrevit import forms>>> items = ['item1', 'item2', 'item3']>>> forms.SelectFromList.show(items, button_name='Select Item')>>> ['item1']
_setup(**kwargs)Private method to be overriden by subclasses for window setup.
class pyrevit.forms.ProgressBar(height=32, **kwargs)Show progress bar at the top of Revit window.
Parameters
• title (string) – progress bar text, defaults to 0/100 progress format
• indeterminate (bool) – create indeterminate progress bar
• cancellable (bool) – add cancel button to progress bar
• step (int) – update progress intervals
80 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit import forms>>> count = 1>>> with forms.ProgressBar(title='my command progress message') as pb:... # do stuff... pb.update_progress(count, 100)... count += 1
Progress bar title could also be customized to show the current and total progress values. In example below, theprogress bar message will be in format “0 of 100”
>>> with forms.ProgressBar(title='{value} of {max_value}') as pb:
By default progress bar updates the progress every time the .update_progress method is called. For operationswith a large number of max steps, the gui update process time will have a significate effect on the overallexecution time of the command. In these cases, set the value of step argument to something larger than 1. Inexample below, the progress bar updates once per every 10 units of progress.
>>> with forms.ProgressBar(title='message', steps=10):
Progress bar could also be set to indeterminate for operations of unknown length. In this case, the progress barwill show an infinitely running ribbon:
>>> with forms.ProgressBar(title='message', indeterminate=True):
if cancellable is set on the object, a cancel button will show on the progress bar and .cancelled attribute will beset on the ProgressBar instance if users clicks on cancel button:
>>> with forms.ProgressBar(title='message',... cancellable=True) as pb:... # do stuff... if pb.cancelled:... # wrap up and cancel operation
_setup(**kwargs)Private method to be overriden by subclasses for prompt setup.
clicked_cancel(sender, args)Handler for cancel button clicked event.
indeterminateProgress bar indeterminate state.
reset()Reset progress value to 0.
titleProgress bar title.
update_progress(new_value, max_value=1)Update progress bar state with given min, max values.
Parameters
• new_value (float) – current progress value
• max_value (float) – total progress value
81
pyRevit Documentation, Release 4.5
wait_async(func, args=())Call a method asynchronosely and show progress.
class pyrevit.forms.RevisionOption(revision_element)Revision wrapper for select_revisions().
nameRevision name (description).
class pyrevit.forms.SearchPrompt(search_db, width, height, **kwargs)Standard prompt for pyRevit search.
Parameters
• search_db (list) – list of possible search targets
• search_tip (str) – text to show in grayscale when search box is empty
• switches (str) – list of switches
• width (int) – width of search prompt window
• height (int) – height of search prompt window
Returns matched strings, and dict of switches if provided str: matched string if switches are notprovided.
Return type str, dict
Example
>>> from pyrevit import forms>>> # assume search input of '/switch1 target1'>>> matched_str, args, switches = forms.SearchPrompt.show(... search_db=['target1', 'target2', 'target3', 'target4'],... switches=['/switch1', '/switch2'],... search_tip='pyRevit Search'... )... matched_str'target1'... args['--help', '--branch', 'branchname']... switches{'/switch1': True, '/switch2': False}
find_direct_match(input_text)Find direct text matches in search term.
find_word_match(input_text)Find direct word matches in search term.
handle_kb_key(sender, args)Handle keyboard input event.
search_inputCurrent search input.
search_input_partsCurrent cleaned up search term.
search_matchesList of matches for the given search term.
82 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
search_termCurrent cleaned up search term.
search_term_argsFind arguments in search term.
search_term_mainCurrent cleaned up search term without the listed switches.
search_term_switchesFind matching switches in search term.
search_txt_changed(sender, args)Handle text changed event.
set_search_results(*args)Set search results for returning.
classmethod show(search_db, width=600, height=100, **kwargs)Show search prompt.
update_results_display(fill_match=False)Update search prompt results based on current input text.
class pyrevit.forms.SelectFromList(context, title, width, height, **kwargs)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 TemplateListItem. This class provides the necessary mechanism to make this form work both forselecting items from a list, and from a list of checkboxes. See the list of arguments below for additional optionsand features.
Parameters
• context (list[str] or dict[list[str]]) – list of items to be selected fromOR dict of list of items to be selected from. use dict when input items need to be groupede.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). defaultsto False
• return_all (bool, optional) – return all items. This is handly when some inputitems have states and the script needs to check the state changes on all items. This optionsworks in multiselect mode only. defaults to False
• filterfunc (function) – filter function to be applied to context items.
• group_selector_title (str) – title for list group selector. defaults to ‘List Group’
• default_group (str) – name of defautl group to be selected
83
pyRevit Documentation, Release 4.5
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')
This module also provides a wrapper base class TemplateListItem for when the checkbox option is wrap-ping another element, e.g. a Revit ViewSheet. Derive from this base class and define the name property tocustomize 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]
_setup(**kwargs)Private method to be overriden by subclasses for window setup.
button_select(sender, args)Handle select button click.
check_all(sender, args)Handle check all button to mark all check boxes as checked.
check_selected(sender, args)Mark selected checkboxes as checked.
clear_search(sender, args)Clear search box.
search_txt_changed(sender, args)Handle text change in search box.
84 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
toggle_all(sender, args)Handle toggle all button to toggle state of all check boxes.
uncheck_all(sender, args)Handle uncheck all button to mark all check boxes as un-checked.
uncheck_selected(sender, args)Mark selected checkboxes as unchecked.
class pyrevit.forms.SheetOption(sheet_element)Sheet wrapper for select_sheets().
nameSheet name.
numberSheet number.
class pyrevit.forms.TemplateListItem(orig_item, checkable=True, name_attr=None)Base class for checkbox option wrapping another object.
checkableList Item CheckBox Visibility.
classmethod is_checkbox(item)Check if the object has all necessary attribs for a checkbox.
nameName property.
unwrap()Unwrap and return wrapped object.
class pyrevit.forms.TemplatePromptBar(height=32, **kwargs)Template context-manager class for creating prompt bars.
Prompt bars are show at the top of the active Revit window and are designed for better prompt visibility.
Parameters
• height (int) – window height
• **kwargs – other arguments to be passed to _setup()
_setup(**kwargs)Private method to be overriden by subclasses for prompt setup.
update_window()Update the prompt bar to match Revit window.
class pyrevit.forms.TemplateUserInputWindow(context, title, width, height, **kwargs)Base class for pyRevit user input standard forms.
Parameters
• context (any) – window context element(s)
• title (str) – window title
• width (int) – window width
• height (int) – window height
• **kwargs – other arguments to be passed to _setup()
85
pyRevit Documentation, Release 4.5
_setup(**kwargs)Private method to be overriden by subclasses for window setup.
classmethod show(context, title=’User Input’, width=500, height=400, **kwargs)Show user input window.
Parameters
• context (any) – window context element(s)
• title (str) – window title
• width (str) – window width
• height (str) – window height
• **kwargs (any) – other arguments to be passed to window
class pyrevit.forms.ViewOption(view_element)View wrapper for select_views().
nameView name.
class pyrevit.forms.WPFWindow(xaml_source, literal_string=False, handle_esc=True)WPF Window base class for all pyRevit forms.
Parameters
• xaml_source (str) – xaml source filepath or xaml content
• literal_string (bool) – xaml_source contains xaml content, not filepath
• handle_esc (bool) – handle Escape button and close the window
Example
>>> from pyrevit import forms>>> layout = '<Window ShowInTaskbar="False" ResizeMode="NoResize" ' \>>> 'WindowStartupLocation="CenterScreen" ' \>>> 'HorizontalContentAlignment="Center">' \>>> '</Window>'>>> w = forms.WPFWindow(layout, literal_string=True)>>> w.show()
static disable_element(*wpf_elements)Enable elements.
Parameters *wpf_elements – WPF framework elements to be enabled
static enable_element(*wpf_elements)Enable elements.
Parameters *wpf_elements – WPF framework elements to be enabled
handle_input_key(sender, args)Handle keyboard input and close the window on Escape.
static hide_element(*wpf_elements)Collapse elements.
Parameters *wpf_elements – WPF framework elements to be collaped
86 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
set_image_source(wpf_element, image_file)Set source file for image element.
Parameters
• element_name (System.Windows.Controls.Image) – xaml image element
• image_file (str) – image file path
setup_icon()Setup default window icon.
show(modal=False)Show window.
show_dialog()Show modal window.
static show_element(*wpf_elements)Show collapsed elements.
Parameters *wpf_elements – WPF framework elements to be set to visible.
static toggle_element(*wpf_elements)Toggle visibility of elements.
Parameters *wpf_elements – WPF framework elements to be toggled.
class pyrevit.forms.WarningBar(height=32, **kwargs)Show warning bar at the top of Revit window.
Parameters title (string) – warning bar text
Example
>>> with WarningBar(title='my warning'):... # do stuff
_setup(**kwargs)Private method to be overriden by subclasses for prompt setup.
pyrevit.forms.alert(msg, title=None, sub_msg=None, expanded=None, footer=”, ok=True, can-cel=False, yes=False, no=False, retry=False, warn_icon=True, options=None,exitscript=False)
Show a task dialog with given message.
Parameters
• msg (str) – message to be displayed
• title (str, optional) – task dialog title
• ok (bool, optional) – show OK button, defaults to True
• cancel (bool, optional) – show Cancel button, defaults to False
• yes (bool, optional) – show Yes button, defaults to False
• no (bool, optional) – show NO button, defaults to False
• retry (bool, optional) – show Retry button, defaults to False
• options (list[str], optional) – list of command link titles in order
87
pyRevit Documentation, Release 4.5
• exitscript (bool, optional) – exit if cancel or no, defaults to False
Returns True if okay, yes, or retry, otherwise False
Return type bool
Example
>>> from pyrevit import forms>>> forms.alert('Are you sure?',... ok=False, yes=True, no=True, exitscript=True)
pyrevit.forms.alert_ifnot(condition, msg, *args, **kwargs)Show a task dialog with given message if condition is NOT met.
Parameters
• condition (bool) – condition to test
• msg (str) – message to be displayed
• title (str, optional) – task dialog title
• ok (bool, optional) – show OK button, defaults to True
• cancel (bool, optional) – show Cancel button, defaults to False
• yes (bool, optional) – show Yes button, defaults to False
• no (bool, optional) – show NO button, defaults to False
• retry (bool, optional) – show Retry button, defaults to False
• exitscript (bool, optional) – exit if cancel or no, defaults to False
Returns True if okay, yes, or retry, otherwise False
Return type bool
Example
>>> from pyrevit import forms>>> forms.alert_ifnot(value > 12,... 'Are you sure?',... ok=False, yes=True, no=True, exitscript=True)
pyrevit.forms.check_familydoc(doc=None, family_cat=None, exitscript=False)Verify document is a Family and notify user of not.
Parameters
• doc (DB.Document) – target document, current of not provided
• family_cat (str) – family category name
• exitscript (bool) – exit script if returning False
Returns True if doc is a Family and of provided category
Return type bool
88 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit import forms>>> forms.check_familydoc(doc=revit.doc, family_cat='Data Devices')... True
pyrevit.forms.check_selection(exitscript=False, message=’At least one element must be se-lected.’)
Verify if selection is not empty notify user if it is.
Parameters
• exitscript (bool) – exit script if returning False
• message (str) – prompt message if returning False
Returns True if selection has at least one item
Return type bool
pyrevit.forms.check_workshared(doc=None, message=’Model is not workshared.’)Verify if model is workshared and notify user if not.
Parameters
• doc (DB.Document) – target document, current of not provided
• message (str) – prompt message if returning False
Returns True if doc is workshared
Return type bool
pyrevit.forms.pick_excel_file(save=False)File pick/save dialog for an excel file.
Parameters save (bool) – show file save dialog, instead of file pick dialog
Returns file path
Return type str
pyrevit.forms.pick_file(file_ext=”, files_filter=”, init_dir=”, restore_dir=True, multi_file=False,unc_paths=False)
Pick file dialog to select a destination file.
Parameters
• file_ext (str) – file extension
• files_filter (str) – file filter
• init_dir (str) – initial directory
• restore_dir (bool) – restore last directory
• multi_file (bool) – allow select multiple files
• unc_paths (bool) – return unc paths
Returns file path or list of file paths if multi_file=True
Return type str or list[str]
89
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit import forms>>> forms.pick_file(file_ext='csv')... r'C:\output\somefile.csv'
>>> forms.pick_file(file_ext='csv', multi_file=True)... [r'C:\output\somefile1.csv', r'C:\output\somefile2.csv']
>>> forms.pick_file(files_filter='All Files (*.*)|*.*|''Excel Workbook (*.xlsx)|*.xlsx|''Excel 97-2003 Workbook|*.xls',
multi_file=True)... [r'C:\output\somefile1.xlsx', r'C:\output\somefile2.xls']
pyrevit.forms.pick_folder(title=None)Show standard windows pick folder dialog.
Parameters title (str, optional) – title for the window
Returns folder path
Return type str
pyrevit.forms.save_excel_file()File save dialog for an excel file.
Returns file path
Return type str
pyrevit.forms.save_file(file_ext=”, files_filter=”, init_dir=”, default_name=”, restore_dir=True,unc_paths=False)
Save file dialog to select a destination file for data.
Parameters
• file_ext (str) – file extension
• files_filter (str) – file filter
• init_dir (str) – initial directory
• default_name (str) – default file name
• restore_dir (bool) – restore last directory
• unc_paths (bool) – return unc paths
Returns file path
Return type str
Example
>>> from pyrevit import forms>>> forms.save_file(file_ext='csv')... r'C:\output\somefile.csv'
pyrevit.forms.select_image(images, title=’Select Image’, button_name=’Select’)Standard form for selecting an image.
90 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
Parameters
• images (list[str] | list[framework.Imaging.BitmapImage]) – list ofimage file paths or bitmaps
• title (str, optional) – swatch list window title
• button_name (str, optional) – swatch list window button caption
Returns path of the selected image
Return type str
Example
>>> from pyrevit import forms>>> forms.select_image(['C:/path/to/image1.png',
'C:/path/to/image2.png'],title="Select Variation")
... 'C:/path/to/image1.png'
pyrevit.forms.select_open_docs(title=’Select Open Documents’, button_name=’OK’, width=500,multiple=True, filterfunc=None)
Standard form for selecting open documents.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
Returns list of selected documents
Return type list[DB.Document]
Example
>>> from pyrevit import forms>>> forms.select_open_docs()... [<Autodesk.Revit.DB.Document object>,... <Autodesk.Revit.DB.Document object>]
pyrevit.forms.select_revisions(title=’Select Revision’, button_name=’Select’, width=500, mul-tiple=True, filterfunc=None, doc=None)
Standard form for selecting revisions.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
91
pyRevit Documentation, Release 4.5
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for revisions; defaults to activedocument
Returns list of selected revisions
Return type list[DB.Revision]
Example
>>> from pyrevit import forms>>> forms.select_revisions()... [<Autodesk.Revit.DB.Revision object>,... <Autodesk.Revit.DB.Revision object>]
pyrevit.forms.select_sheets(title=’Select Sheets’, button_name=’Select’, width=500, multi-ple=True, filterfunc=None, doc=None)
Standard form for selecting sheets.
Sheets are grouped into sheet sets and sheet set can be selected from a drop down box at the top of window.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for sheets; defaults to active doc-ument
Returns list of selected sheets
Return type list[DB.ViewSheet]
Example
>>> from pyrevit import forms>>> forms.select_sheets()... [<Autodesk.Revit.DB.ViewSheet object>,... <Autodesk.Revit.DB.ViewSheet object>]
pyrevit.forms.select_swatch(title=’Select Color Swatch’, button_name=’Select’)Standard form for selecting a color swatch.
Parameters
• title (str, optional) – swatch list window title
• button_name (str, optional) – swatch list window button caption
92 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
Returns rgb color
Return type pyrevit.coreutils.colors.RGB
Example
>>> from pyrevit import forms>>> forms.select_swatch(title="Select Text Color")... <RGB #CD8800>
pyrevit.forms.select_titleblocks(title=’Select Titleblock’, button_name=’Select’,no_tb_option=’No Title Block’, width=500, multiple=False,filterfunc=None, doc=None)
Standard form for selecting a titleblock.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• no_tb_option (str, optional) – name of option for no title block
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
• doc (DB.Document, optional) – source document for titleblocks; defaults to activedocument
Returns selected titleblock id.
Return type DB.ElementId
Example
>>> from pyrevit import forms>>> forms.select_titleblocks()... <Autodesk.Revit.DB.ElementId object>
pyrevit.forms.select_views(title=’Select Views’, button_name=’Select’, width=500, multi-ple=True, filterfunc=None, doc=None)
Standard form for selecting views.
Parameters
• title (str, optional) – list window title
• button_name (str, optional) – list window button caption
• width (int, optional) – width of list window
• multiselect (bool, optional) – allow multi-selection (uses check boxes). defaultsto True
• filterfunc (function) – filter function to be applied to context items.
93
pyRevit Documentation, Release 4.5
• doc (DB.Document, optional) – source document for views; defaults to active doc-ument
Returns list of selected views
Return type list[DB.View]
Example
>>> from pyrevit import forms>>> forms.select_views()... [<Autodesk.Revit.DB.View object>,... <Autodesk.Revit.DB.View object>]
pyrevit.forms.toast(message, title=’pyRevit’, appid=’pyRevit’, icon=None, click=None, ac-tions=None)
Show a Windows 10 notification.
Parameters
• message (str) – notification message
• title (str) – notification title
• appid (str) – app name (will show under message)
• icon (str) – file path to icon .ico file (defaults to pyRevit icon)
• click (str) – click action commands string
• actions (dict) – dictionary of button names and action strings
Example
>>> script.toast("Hello World!",... title="My Script",... appid="MyAPP",... click="https://eirannejad.github.io/pyRevit/",... actions={... "Open Google":"https://google.com",... "Open Toast64":"https://github.com/go-toast/toast"... })
13.1 pyrevit.forms.utils
Utility functions to support forms module.
pyrevit.forms.utils.bitmap_from_file(bitmap_file)Create BitmapImage from a bitmap file.
Parameters bitmap_file (str) – path to bitmap file
Returns bitmap image object
Return type BitmapImage
94 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
13.2 Implementation
"""Reusable WPF forms for pyRevit.
Example:>>> from pyrevit.forms import WPFWindow
"""
import sysimport osimport os.path as opimport stringfrom collections import OrderedDictimport threadingfrom functools import wrapsimport datetime
from pyrevit import HOST_APP, EXEC_PARAMS, BIN_DIRfrom pyrevit.compat import safe_strtypefrom pyrevit import coreutilsfrom pyrevit.coreutils.logger import get_loggerfrom pyrevit.coreutils import colorsfrom pyrevit import frameworkfrom pyrevit.framework import Systemfrom pyrevit.framework import Threadingfrom pyrevit.framework import Interopfrom pyrevit.framework import Inputfrom pyrevit.framework import wpf, Forms, Controls, Mediafrom pyrevit.api import AdWindowsfrom pyrevit import revit, UI, DBfrom pyrevit.forms import utilsfrom pyrevit.forms import toasterfrom pyrevit import versionmgr
#pylint: disable=W0703,C0302,C0103mlogger = get_logger(__name__)
DEFAULT_CMDSWITCHWND_WIDTH = 600DEFAULT_SEARCHWND_WIDTH = 600DEFAULT_SEARCHWND_HEIGHT = 100DEFAULT_INPUTWINDOW_WIDTH = 500DEFAULT_INPUTWINDOW_HEIGHT = 400
WPF_HIDDEN = framework.Windows.Visibility.HiddenWPF_COLLAPSED = framework.Windows.Visibility.CollapsedWPF_VISIBLE = framework.Windows.Visibility.Visible
class WPFWindow(framework.Windows.Window):r"""WPF Window base class for all pyRevit forms.
Args:xaml_source (str): xaml source filepath or xaml contentliteral_string (bool): xaml_source contains xaml content, not filepath
(continues on next page)
13.2. Implementation 95
pyRevit Documentation, Release 4.5
(continued from previous page)
handle_esc (bool): handle Escape button and close the window
Example:>>> from pyrevit import forms>>> layout = '<Window ShowInTaskbar="False" ResizeMode="NoResize" ' \>>> 'WindowStartupLocation="CenterScreen" ' \>>> 'HorizontalContentAlignment="Center">' \>>> '</Window>'>>> w = forms.WPFWindow(layout, literal_string=True)>>> w.show()
"""
def __init__(self, xaml_source, literal_string=False, handle_esc=True):"""Initialize WPF window and resources."""# self.Parent = selfwih = Interop.WindowInteropHelper(self)wih.Owner = AdWindows.ComponentManager.ApplicationWindow
if not literal_string:if not op.exists(xaml_source):
wpf.LoadComponent(self,os.path.join(EXEC_PARAMS.command_path,
xaml_source))
else:wpf.LoadComponent(self, xaml_source)
else:wpf.LoadComponent(self, framework.StringReader(xaml_source))
if handle_esc:self.PreviewKeyDown += self.handle_input_key #pylint: disable=E1101
self.setup_icon()
#2c3e50self.Resources['pyRevitDarkColor'] = \
Media.Color.FromArgb(0xFF, 0x2c, 0x3e, 0x50)
#23303dself.Resources['pyRevitDarkerDarkColor'] = \
Media.Color.FromArgb(0xFF, 0x23, 0x30, 0x3d)
#ffffffself.Resources['pyRevitButtonColor'] = \
Media.Color.FromArgb(0xFF, 0xff, 0xff, 0xff)
#f39c12self.Resources['pyRevitAccentColor'] = \
Media.Color.FromArgb(0xFF, 0xf3, 0x9c, 0x12)
self.Resources['pyRevitDarkBrush'] = \Media.SolidColorBrush(self.Resources['pyRevitDarkColor'])
self.Resources['pyRevitAccentBrush'] = \Media.SolidColorBrush(self.Resources['pyRevitAccentColor'])
self.Resources['pyRevitDarkerDarkBrush'] = \Media.SolidColorBrush(self.Resources['pyRevitDarkerDarkColor'])
(continues on next page)
96 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
self.Resources['pyRevitButtonForgroundBrush'] = \Media.SolidColorBrush(self.Resources['pyRevitButtonColor'])
def handle_input_key(self, sender, args): #pylint: disable=W0613"""Handle keyboard input and close the window on Escape."""if args.Key == Input.Key.Escape:
self.Close()
def setup_icon(self):"""Setup default window icon."""iconpath = op.join(BIN_DIR, 'window_icon.png')self.Icon = utils.bitmap_from_file(iconpath)
def show(self, modal=False):"""Show window."""if modal:
return self.ShowDialog()self.Show()
def show_dialog(self):"""Show modal window."""return self.ShowDialog()
def set_image_source(self, wpf_element, image_file):"""Set source file for image element.
Args:element_name (System.Windows.Controls.Image): xaml image elementimage_file (str): image file path
"""if not op.exists(image_file):
wpf_element.Source = \utils.bitmap_from_file(
os.path.join(EXEC_PARAMS.command_path,image_file)
)else:
wpf_element.Source = utils.bitmap_from_file(image_file)
@propertydef pyrevit_version(self):
return 'pyRevit {}'.format(versionmgr.get_pyrevit_version().get_formatted())
@staticmethoddef hide_element(*wpf_elements):
"""Collapse elements.
Args:
*wpf_elements: WPF framework elements to be collaped"""for wpfel in wpf_elements:
wpfel.Visibility = WPF_COLLAPSED
@staticmethod(continues on next page)
13.2. Implementation 97
pyRevit Documentation, Release 4.5
(continued from previous page)
def show_element(*wpf_elements):"""Show collapsed elements.
Args:
*wpf_elements: WPF framework elements to be set to visible."""for wpfel in wpf_elements:
wpfel.Visibility = WPF_VISIBLE
@staticmethoddef toggle_element(*wpf_elements):
"""Toggle visibility of elements.
Args:
*wpf_elements: WPF framework elements to be toggled."""for wpfel in wpf_elements:
if wpfel.Visibility == WPF_VISIBLE:WPFWindow.hide_element(wpfel)
elif wpfel.Visibility == WPF_COLLAPSED:WPFWindow.show_element(wpfel)
@staticmethoddef disable_element(*wpf_elements):
"""Enable elements.
Args:
*wpf_elements: WPF framework elements to be enabled"""for wpfel in wpf_elements:
wpfel.IsEnabled = False
@staticmethoddef enable_element(*wpf_elements):
"""Enable elements.
Args:
*wpf_elements: WPF framework elements to be enabled"""for wpfel in wpf_elements:
wpfel.IsEnabled = True
class TemplateUserInputWindow(WPFWindow):"""Base class for pyRevit user input standard forms.
Args:context (any): window context element(s)title (str): window titlewidth (int): window widthheight (int): window height
**kwargs: other arguments to be passed to :func:`_setup`"""
xaml_source = 'BaseWindow.xaml'
def __init__(self, context, title, width, height, **kwargs):(continues on next page)
98 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Initialize user input window."""WPFWindow.__init__(self,
op.join(op.dirname(__file__), self.xaml_source),handle_esc=True)
self.Title = title or 'pyRevit'self.Width = widthself.Height = height
self._context = contextself.response = None
# parent window?owner = kwargs.get('owner', None)if owner:
# set wpf windows directlyself.Owner = ownerself.WindowStartupLocation = \
framework.Windows.WindowStartupLocation.CenterOwner
self._setup(**kwargs)
def _setup(self, **kwargs):"""Private method to be overriden by subclasses for window setup."""pass
@classmethoddef show(cls, context, #pylint: disable=W0221
title='User Input',width=DEFAULT_INPUTWINDOW_WIDTH,height=DEFAULT_INPUTWINDOW_HEIGHT, **kwargs):
"""Show user input window.
Args:context (any): window context element(s)title (str): window titlewidth (str): window widthheight (str): window height
**kwargs (any): other arguments to be passed to window"""dlg = cls(context, title, width, height, **kwargs)dlg.ShowDialog()return dlg.response
class TemplateListItem(object):"""Base class for checkbox option wrapping another object."""
def __init__(self, orig_item, checkable=True, name_attr=None):"""Initialize the checkbox option and wrap given obj.
Args:orig_item (any): Object to wrap (must have name property
or be convertable to string with str()checkable (bool): Use checkbox for itemsname_attr (str): Get this attribute of wrapped object as name
"""self.item = orig_item
(continues on next page)
13.2. Implementation 99
pyRevit Documentation, Release 4.5
(continued from previous page)
self.state = Falseself._nameattr = name_attrself._checkable = checkable
def __nonzero__(self):return self.state
def __str__(self):return self.name or str(self.item)
def __contains__(self, value):return value in self.name
def __getattr__(self, param_name):return getattr(self.item, param_name)
@propertydef name(self):
"""Name property."""# get custom attr, or name or just str reprif self._nameattr:
return str(getattr(self.item, self._nameattr))elif hasattr(self.item, 'name'):
return getattr(self.item, 'name', '')else:
return safe_strtype(self.item)
def unwrap(self):"""Unwrap and return wrapped object."""return self.item
@propertydef checkable(self):
"""List Item CheckBox Visibility."""return WPF_VISIBLE if self._checkable \
else WPF_COLLAPSED
@checkable.setterdef checkable(self, value):
self._checkable = value
@classmethoddef is_checkbox(cls, item):
"""Check if the object has all necessary attribs for a checkbox."""return isinstance(item, TemplateListItem)
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 classwraps the objects passed to context, in :obj:`TemplateListItem`.This class provides the necessary mechanism to make this form work bothfor selecting items from a list, and from a list of checkboxes. See thelist of arguments below for additional options and features.
Args:(continues on next page)
100 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
context (list[str] or dict[list[str]]):list of items to be selected fromORdict of list of items to be selected from.use dict when input items need to be groupede.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 Falsereturn_all (bool, optional):
return all items. This is handly when some input items have statesand 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.
group_selector_title (str):title for list group selector. defaults to 'List Group'
default_group (str): name of defautl group to be selected
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')
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 thename property to customize how the checkbox is named on the dialog.
>>> from pyrevit import forms>>> class MyOption(forms.TemplateListItem)... @property... def name(self):
(continues on next page)
13.2. Implementation 101
pyRevit Documentation, Release 4.5
(continued from previous page)
... 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'
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 = Trueself.list_lb.SelectionMode = Controls.SelectionMode.Extendedself.show_element(self.checkboxbuttons_g)
else:self.multiselect = Falseself.list_lb.SelectionMode = Controls.SelectionMode.Singleself.hide_element(self.checkboxbuttons_g)
# return checked items only?self.return_all = kwargs.get('return_all', False)
# filter function?self.filter_func = kwargs.get('filterfunc', None)
# 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)
# check for custom templatesitems_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"] = \(continues on next page)
102 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
item_template
# nicely wrap and prepare context for presentation, then presentself._prepare_context()
# list options nowself._list_options()
# setup search and filter fieldsself.hide_element(self.clrsearch_b)self.clear_search(None, None)
def _prepare_context_items(self, ctx_items):new_ctx = []# filter context if necessaryif self.filter_func:
ctx_items = filter(self.filter_func, ctx_items)
for item in ctx_items:if TemplateListItem.is_checkbox(item):
item.checkable = self.multiselectnew_ctx.append(item)
else:new_ctx.append(
TemplateListItem(item,checkable=self.multiselect,name_attr=self._nameattr)
)
return new_ctx
def _prepare_context(self):if isinstance(self._context, dict) and self._context.keys():
self._update_ctx_groups(self._context.keys())new_ctx = {}for ctx_grp, ctx_items in self._context.items():
new_ctx[ctx_grp] = self._prepare_context_items(ctx_items)else:
new_ctx = self._prepare_context_items(self._context)
self._context = new_ctx
def _update_ctx_groups(self, ctx_group_names):self.show_element(self.ctx_groups_dock)self.ctx_groups_selector_cb.ItemsSource = ctx_group_namesif self.ctx_groups_active in ctx_group_names:
self.ctx_groups_selector_cb.SelectedIndex = \ctx_group_names.index(self.ctx_groups_active)
else:self.ctx_groups_selector_cb.SelectedIndex = 0
def _get_active_ctx_group(self):return self.ctx_groups_selector_cb.SelectedItem
def _get_active_ctx(self):if isinstance(self._context, dict):
return self._context[self._get_active_ctx_group()](continues on next page)
13.2. Implementation 103
pyRevit Documentation, Release 4.5
(continued from previous page)
else:return self._context
def _list_options(self, option_filter=None):if option_filter:
self.checkall_b.Content = 'Check'self.uncheckall_b.Content = 'Uncheck'self.toggleall_b.Content = 'Toggle'# get a match score for every item and sort high to lowfuzzy_matches = sorted(
[(x, coreutils.fuzzy_search_ratio(x.name, option_filter))for x in self._get_active_ctx()],
key=lambda x: x[1],reverse=True)
# filter out any match with score less than 80self.list_lb.ItemsSource = \
[x[0] for x in fuzzy_matches if x[1] >= 80]else:
self.checkall_b.Content = 'Check All'self.uncheckall_b.Content = 'Uncheck All'self.toggleall_b.Content = 'Toggle All'self.list_lb.ItemsSource = [x for x in self._get_active_ctx()]
@staticmethoddef _unwrap_options(options):
unwrapped = []for optn in options:
if isinstance(optn, TemplateListItem):unwrapped.append(optn.unwrap())
else:unwrapped.append(optn)
return unwrapped
def _get_options(self):if self.multiselect:
if self.return_all:return [x for x in self._get_active_ctx()]
else:return self._unwrap_options(
[x for x in self._get_active_ctx()if x.state or x in self.list_lb.SelectedItems]
)else:
return self._unwrap_options([self.list_lb.SelectedItem])[0]
def _set_states(self, state=True, flip=False, selected=False):all_items = self.list_lb.ItemsSourceif selected:
current_list = self.list_lb.SelectedItemselse:
current_list = self.list_lb.ItemsSourcefor checkbox in current_list:
if flip:checkbox.state = not checkbox.state
else:checkbox.state = state
(continues on next page)
104 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
# push list view to redrawself.list_lb.ItemsSource = Noneself.list_lb.ItemsSource = all_items
def toggle_all(self, sender, args): #pylint: disable=W0613"""Handle toggle all button to toggle state of all check boxes."""self._set_states(flip=True)
def check_all(self, sender, args): #pylint: disable=W0613"""Handle check all button to mark all check boxes as checked."""self._set_states(state=True)
def uncheck_all(self, sender, args): #pylint: disable=W0613"""Handle uncheck all button to mark all check boxes as un-checked."""self._set_states(state=False)
def check_selected(self, sender, args): #pylint: disable=W0613"""Mark selected checkboxes as checked."""self._set_states(state=True, selected=True)
def uncheck_selected(self, sender, args): #pylint: disable=W0613"""Mark selected checkboxes as unchecked."""self._set_states(state=False, selected=True)
def button_select(self, sender, args): #pylint: disable=W0613"""Handle select button click."""self.response = self._get_options()self.Close()
def search_txt_changed(self, sender, args): #pylint: disable=W0613"""Handle text change in search box."""if self.search_tb.Text == '':
self.hide_element(self.clrsearch_b)else:
self.show_element(self.clrsearch_b)
self._list_options(option_filter=self.search_tb.Text)
def clear_search(self, sender, args): #pylint: disable=W0613"""Clear search box."""self.search_tb.Text = ' 'self.search_tb.Clear()self.search_tb.Focus()
class CommandSwitchWindow(TemplateUserInputWindow):"""Standard form to select from a list of command options.
Args:context (list[str]): list of command options to choose fromswitches (list[str]): list of on/off switchesmessage (str): window title messageconfig (dict): dictionary of config dicts for options or switches
Returns:str: name of selected option
(continues on next page)
13.2. Implementation 105
pyRevit Documentation, Release 4.5
(continued from previous page)
Returns:tuple(str, dict): if ``switches`` option is used, returns a tupleof selection option name and dict of switches
Example:This is an example with series of command options:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> forms.CommandSwitchWindow.show(ops, message='Select Option')'option2'
A more advanced example of combining command options, on/off switches,and option or switch configuration options:
>>> from pyrevit import forms>>> ops = ['option1', 'option2', 'option3', 'option4']>>> switches = ['switch1', 'switch2']>>> cfgs = {'option1': { 'background': '0xFF55FF'}}>>> rops, rswitches = forms.CommandSwitchWindow.show(... ops,... switches=switches... message='Select Option',... config=cfgs... )>>> rops'option2'>>> rswitches{'switch1': False, 'switch2': True}
"""
xaml_source = 'CommandSwitchWindow.xaml'
def _setup(self, **kwargs):self.selected_switch = ''self.Width = DEFAULT_CMDSWITCHWND_WIDTHself.Title = 'Command Options'
message = kwargs.get('message', None)self._switches = kwargs.get('switches', [])if not isinstance(self._switches, dict):
self._switches = dict.fromkeys(self._switches)
configs = kwargs.get('config', None)
self.message_label.Content = \message if message else 'Pick a command option:'
# creates the switches firstfor switch, state in self._switches.items():
my_togglebutton = framework.Controls.Primitives.ToggleButton()my_togglebutton.Content = switchmy_togglebutton.IsChecked = state if state else Falseif configs and 'option' in configs:
self._set_config(my_togglebutton, configs[switch])self.button_list.Children.Add(my_togglebutton)
(continues on next page)
106 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
for option in self._context:my_button = framework.Controls.Button()my_button.Content = optionmy_button.Click += self.process_optionif configs and option in configs:
self._set_config(my_button, configs[option])self.button_list.Children.Add(my_button)
self._setup_response()self.search_tb.Focus()self._filter_options()
@staticmethoddef _set_config(item, config_dict):
bg = config_dict.get('background', None)if bg:
bg = bg.replace('0x', '#')item.Background = Media.BrushConverter().ConvertFrom(bg)
def _setup_response(self, response=None):if self._switches:
switches = [x for x in self.button_list.Childrenif hasattr(x, 'IsChecked')]
self.response = response, {x.Content: x.IsCheckedfor x in switches}
else:self.response = response
def _filter_options(self, option_filter=None):if option_filter:
self.search_tb.Tag = ''option_filter = option_filter.lower()for button in self.button_list.Children:
if option_filter not in button.Content.lower():button.Visibility = WPF_COLLAPSED
else:button.Visibility = WPF_VISIBLE
else:self.search_tb.Tag = \
'Type to Filter / Tab to Select / Enter or Click to Run'for button in self.button_list.Children:
button.Visibility = WPF_VISIBLE
def _get_active_button(self):buttons = []for button in self.button_list.Children:
if button.Visibility == WPF_VISIBLE:buttons.append(button)
if len(buttons) == 1:return buttons[0]
else:for x in buttons:
if x.IsFocused:return x
def handle_click(self, sender, args): #pylint: disable=W0613(continues on next page)
13.2. Implementation 107
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Handle mouse click."""self.Close()
def handle_input_key(self, sender, args):"""Handle keyboard inputs."""if args.Key == Input.Key.Escape:
if self.search_tb.Text:self.search_tb.Text = ''
else:self.Close()
elif args.Key == Input.Key.Enter:self.process_option(self._get_active_button(), None)
elif args.Key != Input.Key.Tab \and args.Key != Input.Key.Space\and args.Key != Input.Key.LeftShift\and args.Key != Input.Key.RightShift:
self.search_tb.Focus()
def search_txt_changed(self, sender, args): #pylint: disable=W0613"""Handle text change in search box."""self._filter_options(option_filter=self.search_tb.Text)
def process_option(self, sender, args): #pylint: disable=W0613"""Handle click on command option button."""self.Close()if sender:
self._setup_response(response=sender.Content)
class GetValueWindow(TemplateUserInputWindow):"""Standard form to get simple values from user.
Args:
Example:>>> from pyrevit import forms>>> items = ['item1', 'item2', 'item3']>>> forms.SelectFromList.show(items, button_name='Select Item')>>> ['item1']
"""
xaml_source = 'GetValueWindow.xaml'
def _setup(self, **kwargs):self.Width = 400# determine value typeself.value_type = kwargs.get('value_type', 'string')value_prompt = kwargs.get('prompt', None)value_default = kwargs.get('default', None)self.reserved_values = kwargs.get('reserved_values', [])
# customize window based on typeif self.value_type == 'string':
self.show_element(self.stringPanel_dp)self.stringValue_tb.Text = value_default if value_default else ''self.stringValue_tb.Focus()
(continues on next page)
108 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
self.stringValue_tb.SelectAll()self.stringPrompt.Text = \
value_prompt if value_prompt else 'Enter string:'if self.reserved_values:
self.string_value_changed(None, None)elif self.value_type == 'dropdown':
self.show_element(self.dropdownPanel_db)self.dropdownPrompt.Text = \
value_prompt if value_prompt else 'Pick one value:'self.dropdown_cb.ItemsSource = self._contextif value_default:
self.dropdown_cb.SelectedItem = value_defaultelif self.value_type == 'date':
self.show_element(self.datePanel_dp)self.datePrompt.Text = \
value_prompt if value_prompt else 'Pick date:'
def string_value_changed(self, sender, args):filtered_rvalues = \
sorted([x for x in self.reserved_valuesif self.stringValue_tb.Text in str(x)],reverse=True)
if filtered_rvalues:self.reservedValuesList.ItemsSource = filtered_rvaluesself.show_element(self.reservedValuesListPanel)self.okayButton.IsEnabled = \
self.stringValue_tb.Text not in filtered_rvalueselse:
self.reservedValuesList.ItemsSource = []self.hide_element(self.reservedValuesListPanel)self.okayButton.IsEnabled = True
def select(self, sender, args): #pylint: disable=W0613self.Close()if self.value_type == 'string':
self.response = self.stringValue_tb.Textelif self.value_type == 'dropdown':
self.response = self.dropdown_cb.SelectedItemelif self.value_type == 'date':
if self.datePicker.SelectedDate:datestr = self.datePicker.SelectedDate.ToString("MM/dd/yyyy")self.response = datetime.datetime.strptime(datestr, r'%m/%d/%Y')
else:self.response = None
class TemplatePromptBar(WPFWindow):"""Template context-manager class for creating prompt bars.
Prompt bars are show at the top of the active Revit window and aredesigned for better prompt visibility.
Args:height (int): window height
**kwargs: other arguments to be passed to :func:`_setup`"""
(continues on next page)
13.2. Implementation 109
pyRevit Documentation, Release 4.5
(continued from previous page)
xaml_source = 'TemplatePromptBar.xaml'
def __init__(self, height=32, **kwargs):"""Initialize user prompt window."""WPFWindow.__init__(self,
op.join(op.dirname(__file__), self.xaml_source))
self.user_height = heightself.update_window()self._setup(**kwargs)
def update_window(self):"""Update the prompt bar to match Revit window."""screen_area = HOST_APP.proc_screen_workareascale_factor = 1.0 / HOST_APP.proc_screen_scalefactortop = left = width = height = 0
window_rect = revit.get_window_rectangle()
# set width and heightwidth = window_rect.Right - window_rect.Leftheight = self.user_height
top = window_rect.Top# in maximized window, the top might be off the active screen# due to windows thicker window frames# lets cut the height and re-adjust the toptop_diff = abs(screen_area.Top - top)if 10 > top_diff > 0 and top_diff < height:
height -= top_difftop = screen_area.Top
left = window_rect.Left# in maximized window, Left also might be off the active screen# due to windows thicker window frames# let's fix the width to accomodate the extra pixels as wellleft_diff = abs(screen_area.Left - left)if 10 > left_diff > 0 and left_diff < width:
# deduct two times the left negative offset since this extra# offset happens on both left and right sidewidth -= left_diff * 2left = screen_area.Left
self.Top = top * scale_factorself.Left = left * scale_factorself.Width = width * scale_factorself.Height = height
def _setup(self, **kwargs):"""Private method to be overriden by subclasses for prompt setup."""pass
def __enter__(self):self.Show()return self
def __exit__(self, exception, exception_value, traceback):(continues on next page)
110 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
self.Close()
class WarningBar(TemplatePromptBar):"""Show warning bar at the top of Revit window.
Args:title (string): warning bar text
Example:>>> with WarningBar(title='my warning'):... # do stuff
"""
xaml_source = 'WarningBar.xaml'
def _setup(self, **kwargs):self.message_tb.Text = kwargs.get('title', '')
class ProgressBar(TemplatePromptBar):"""Show progress bar at the top of Revit window.
Args:title (string): progress bar text, defaults to 0/100 progress formatindeterminate (bool): create indeterminate progress barcancellable (bool): add cancel button to progress barstep (int): update progress intervals
Example:>>> from pyrevit import forms>>> count = 1>>> with forms.ProgressBar(title='my command progress message') as pb:... # do stuff... pb.update_progress(count, 100)... count += 1
Progress bar title could also be customized to show the current andtotal progress values. In example below, the progress bar messagewill be in format "0 of 100"
>>> with forms.ProgressBar(title='{value} of {max_value}') as pb:
By default progress bar updates the progress every time the.update_progress method is called. For operations with a large numberof max steps, the gui update process time will have a significateeffect on the overall execution time of the command. In these cases,set the value of step argument to something larger than 1. In examplebelow, the progress bar updates once per every 10 units of progress.
>>> with forms.ProgressBar(title='message', steps=10):
Progress bar could also be set to indeterminate for operations ofunknown length. In this case, the progress bar will show an infinitelyrunning ribbon:
>>> with forms.ProgressBar(title='message', indeterminate=True):(continues on next page)
13.2. Implementation 111
pyRevit Documentation, Release 4.5
(continued from previous page)
if cancellable is set on the object, a cancel button will show on theprogress bar and .cancelled attribute will be set on the ProgressBarinstance if users clicks on cancel button:
>>> with forms.ProgressBar(title='message',... cancellable=True) as pb:... # do stuff... if pb.cancelled:... # wrap up and cancel operation
"""
xaml_source = 'ProgressBar.xaml'
def _setup(self, **kwargs):self.max_value = 1self.new_value = 0self.step = kwargs.get('step', 0)
self.cancelled = Falsehas_cancel = kwargs.get('cancellable', False)if has_cancel:
self.show_element(self.cancel_b)
self.pbar.IsIndeterminate = kwargs.get('indeterminate', False)self._title = kwargs.get('title', '{value}/{max_value}')
def _update_pbar(self):self.update_window()self.pbar.Maximum = self.max_valueself.pbar.Value = self.new_value
# updating titletitle_text = \
string.Formatter().vformat(self._title,(),coreutils.SafeDict(
{'value': self.new_value,'max_value': self.max_value}
))
self.pbar_text.Text = title_text
def _donothing(self):pass
def _dispatch_updater(self):# ask WPF dispatcher for gui updateself.pbar.Dispatcher.Invoke(System.Action(self._update_pbar),
Threading.DispatcherPriority.Background)# give it a little free time to update uiself.pbar.Dispatcher.Invoke(System.Action(self._donothing),
Threading.DispatcherPriority.Background)
@staticmethoddef _make_return_getter(f, ret):
# WIP(continues on next page)
112 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
@wraps(f)def wrapped_f(*args, **kwargs):
ret.append(f(*args, **kwargs))return wrapped_f
@propertydef title(self):
"""Progress bar title."""return self._title
@title.setterdef title(self, value):
if isinstance(value, str):self._title = value #pylint: disable=W0201
@propertydef indeterminate(self):
"""Progress bar indeterminate state."""return self.pbar.IsIndeterminate
@indeterminate.setterdef indeterminate(self, value):
self.pbar.IsIndeterminate = value
def clicked_cancel(self, sender, args): #pylint: disable=W0613"""Handler for cancel button clicked event."""self.cancel_b.Content = 'Cancelling...'self.cancelled = True #pylint: disable=W0201
def wait_async(self, func, args=()):"""Call a method asynchronosely and show progress."""returns = []self.indeterminate = Truergfunc = self._make_return_getter(func, returns)t = threading.Thread(target=rgfunc, args=args)t.start()while t.is_alive():
self._dispatch_updater()
return returns[0] if returns else None
def reset(self):"""Reset progress value to 0."""self.update_progress(0, 1)
def update_progress(self, new_value, max_value=1):"""Update progress bar state with given min, max values.
Args:new_value (float): current progress valuemax_value (float): total progress value
"""self.max_value = max_value #pylint: disable=W0201self.new_value = new_value #pylint: disable=W0201if self.new_value == 0:
self._dispatch_updater()elif self.step > 0:
(continues on next page)
13.2. Implementation 113
pyRevit Documentation, Release 4.5
(continued from previous page)
if self.new_value % self.step == 0:self._dispatch_updater()
else:self._dispatch_updater()
class SearchPrompt(WPFWindow):"""Standard prompt for pyRevit search.
Args:search_db (list): list of possible search targetssearch_tip (str): text to show in grayscale when search box is emptyswitches (str): list of switcheswidth (int): width of search prompt windowheight (int): height of search prompt window
Returns:str, dict: matched strings, and dict of switches if providedstr: matched string if switches are not provided.
Example:>>> from pyrevit import forms>>> # assume search input of '/switch1 target1'>>> matched_str, args, switches = forms.SearchPrompt.show(... search_db=['target1', 'target2', 'target3', 'target4'],... switches=['/switch1', '/switch2'],... search_tip='pyRevit Search'... )... matched_str'target1'... args['--help', '--branch', 'branchname']... switches{'/switch1': True, '/switch2': False}
"""def __init__(self, search_db, width, height, **kwargs):
"""Initialize search prompt window."""WPFWindow.__init__(self,
op.join(op.dirname(__file__), 'SearchPrompt.xaml'))self.Width = widthself.MinWidth = self.Widthself.Height = height
self.search_tip = kwargs.get('search_tip', '')
self._search_db = sorted(search_db)self._switches = kwargs.get('switches', [])self._setup_response()
self.search_tb.Focus()self.hide_element(self.tab_icon)self.hide_element(self.return_icon)self.search_tb.Text = ''self.set_search_results()
def _setup_response(self, response=None):switch_dict = dict.fromkeys(self._switches)
(continues on next page)
114 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
for switch in self.search_term_switches:switch_dict[switch] = True
arguments = self.search_term_args# remove first arg which is command nameif len(arguments) >= 1:
arguments = arguments[1:]
self.response = response, arguments, switch_dict
@propertydef search_input(self):
"""Current search input."""return self.search_tb.Text
@search_input.setterdef search_input(self, value):
self.search_tb.Text = valueself.search_tb.CaretIndex = len(value)
@propertydef search_input_parts(self):
"""Current cleaned up search term."""return self.search_input.strip().split()
@propertydef search_term(self):
"""Current cleaned up search term."""return self.search_input.lower().strip()
@propertydef search_term_switches(self):
"""Find matching switches in search term."""switches = set()for stpart in self.search_input_parts:
if stpart.lower() in self._switches:switches.add(stpart)
return switches
@propertydef search_term_args(self):
"""Find arguments in search term."""args = []switches = self.search_term_switchesfor spart in self.search_input_parts:
if spart.lower() not in switches:args.append(spart)
return args
@propertydef search_term_main(self):
"""Current cleaned up search term without the listed switches."""if len(self.search_term_args) >= 1:
return self.search_term_args[0]else:
return ''
@property(continues on next page)
13.2. Implementation 115
pyRevit Documentation, Release 4.5
(continued from previous page)
def search_matches(self):"""List of matches for the given search term."""# remove duplicates while keeping order# results = list(set(self._search_results))return OrderedDict.fromkeys(self._search_results).keys()
def update_results_display(self, fill_match=False):"""Update search prompt results based on current input text."""self.directmatch_tb.Text = ''self.wordsmatch_tb.Text = ''
results = self.search_matchesres_cout = len(results)
mlogger.debug('unique results count: %s', res_cout)mlogger.debug('unique results: %s', results)
if res_cout > 1:self.show_element(self.tab_icon)self.hide_element(self.return_icon)
elif res_cout == 1:self.hide_element(self.tab_icon)self.show_element(self.return_icon)
else:self.hide_element(self.tab_icon)self.hide_element(self.return_icon)
if self._result_index >= res_cout:self._result_index = 0 #pylint: disable=W0201
if self._result_index < 0:self._result_index = res_cout - 1 #pylint: disable=W0201
if not self.search_input:self.directmatch_tb.Text = self.search_tipreturn
if results:input_term = self.search_termcur_res = results[self._result_index]mlogger.debug('current result: %s', cur_res)if fill_match:
self.search_input = cur_reselse:
if cur_res.lower().startswith(input_term):self.directmatch_tb.Text = \
self.search_input + cur_res[len(input_term):]mlogger.debug('directmatch_tb.Text: %s',
self.directmatch_tb.Text)else:
self.wordsmatch_tb.Text = '- {}'.format(cur_res)mlogger.debug('wordsmatch_tb.Text: %s',
self.wordsmatch_tb.Text)
self._setup_response(response=cur_res)return True
(continues on next page)
116 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
self._setup_response()return False
def set_search_results(self, *args):"""Set search results for returning."""self._result_index = 0self._search_results = []
mlogger.debug('search input: %s', self.search_input)mlogger.debug('search term: %s', self.search_term)mlogger.debug('search term (main): %s', self.search_term_main)mlogger.debug('search term (parts): %s', self.search_input_parts)mlogger.debug('search term (args): %s', self.search_term_args)mlogger.debug('search term (switches): %s', self.search_term_switches)
for resultset in args:mlogger.debug('result set: %s}', resultset)self._search_results.extend(sorted(resultset))
mlogger.debug('results: %s', self._search_results)
def find_direct_match(self, input_text):"""Find direct text matches in search term."""results = []if input_text:
for cmd_name in self._search_db:if cmd_name.lower().startswith(input_text):
results.append(cmd_name)
return results
def find_word_match(self, input_text):"""Find direct word matches in search term."""results = []if input_text:
cur_words = input_text.split(' ')for cmd_name in self._search_db:
if all([x in cmd_name.lower() for x in cur_words]):results.append(cmd_name)
return results
def search_txt_changed(self, sender, args): #pylint: disable=W0613"""Handle text changed event."""input_term = self.search_term_maindmresults = self.find_direct_match(input_term)wordresults = self.find_word_match(input_term)self.set_search_results(dmresults, wordresults)self.update_results_display()
def handle_kb_key(self, sender, args): #pylint: disable=W0613"""Handle keyboard input event."""shiftdown = Input.Keyboard.IsKeyDown(Input.Key.LeftShift) \
or Input.Keyboard.IsKeyDown(Input.Key.RightShift)# Escape: set response to none and closeif args.Key == Input.Key.Escape:
self._setup_response()(continues on next page)
13.2. Implementation 117
pyRevit Documentation, Release 4.5
(continued from previous page)
self.Close()# Enter: close, returns matched response automaticallyelif args.Key == Input.Key.Enter:
self.Close()# Shift+Tab, Tab: Cycle through matcheselif args.Key == Input.Key.Tab and shiftdown:
self._result_index -= 1self.update_results_display()
elif args.Key == Input.Key.Tab:self._result_index += 1self.update_results_display()
# Up, Down: Cycle through matcheselif args.Key == Input.Key.Up:
self._result_index -= 1self.update_results_display()
elif args.Key == Input.Key.Down:self._result_index += 1self.update_results_display()
# Right, End: Autocomplete with displayed matchelif args.Key in [Input.Key.Right,
Input.Key.End]:self.update_results_display(fill_match=True)
@classmethoddef show(cls, search_db, #pylint: disable=W0221
width=DEFAULT_SEARCHWND_WIDTH,height=DEFAULT_SEARCHWND_HEIGHT, **kwargs):
"""Show search prompt."""dlg = cls(search_db, width, height, **kwargs)dlg.ShowDialog()return dlg.response
class RevisionOption(TemplateListItem):"""Revision wrapper for :func:`select_revisions`."""def __init__(self, revision_element):
super(RevisionOption, self).__init__(revision_element)
@propertydef name(self):
"""Revision name (description)."""revnum = self.item.SequenceNumberif hasattr(self.item, 'RevisionNumber'):
revnum = self.item.RevisionNumberreturn '{}-{}-{}'.format(revnum,
self.item.Description,self.item.RevisionDate)
class SheetOption(TemplateListItem):"""Sheet wrapper for :func:`select_sheets`."""def __init__(self, sheet_element):
super(SheetOption, self).__init__(sheet_element)
@propertydef name(self):
"""Sheet name."""(continues on next page)
118 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
return '{} - {}{}' \.format(self.item.SheetNumber,
self.item.Name,' (placeholder)' if self.item.IsPlaceholder else '')
@propertydef number(self):
"""Sheet number."""return self.item.SheetNumber
class ViewOption(TemplateListItem):"""View wrapper for :func:`select_views`."""def __init__(self, view_element):
super(ViewOption, self).__init__(view_element)
@propertydef name(self):
"""View name."""return '{} ({})'.format(revit.query.get_name(self.item),
self.item.ViewType)
def select_revisions(title='Select Revision',button_name='Select',width=DEFAULT_INPUTWINDOW_WIDTH,multiple=True,filterfunc=None,doc=None):
"""Standard form for selecting revisions.
Args:title (str, optional): list window titlebutton_name (str, optional): list window button captionwidth (int, optional): width of list windowmultiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to Truefilterfunc (function):
filter function to be applied to context items.doc (DB.Document, optional):
source document for revisions; defaults to active document
Returns:list[DB.Revision]: list of selected revisions
Example:>>> from pyrevit import forms>>> forms.select_revisions()... [<Autodesk.Revit.DB.Revision object>,... <Autodesk.Revit.DB.Revision object>]
"""revisions = sorted(revit.query.get_revisions(doc=doc),
key=lambda x: x.SequenceNumber)
if filterfunc:revisions = filter(filterfunc, revisions)
(continues on next page)
13.2. Implementation 119
pyRevit Documentation, Release 4.5
(continued from previous page)
# ask user for revisionsselected_revs = SelectFromList.show(
[RevisionOption(x) for x in revisions],title=title,button_name=button_name,width=width,multiselect=multiple,checked_only=True)
return selected_revs
def select_sheets(title='Select Sheets',button_name='Select',width=DEFAULT_INPUTWINDOW_WIDTH,multiple=True,filterfunc=None,doc=None):
"""Standard form for selecting sheets.
Sheets are grouped into sheet sets and sheet set can be selected froma drop down box at the top of window.
Args:title (str, optional): list window titlebutton_name (str, optional): list window button captionwidth (int, optional): width of list windowmultiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to Truefilterfunc (function):
filter function to be applied to context items.doc (DB.Document, optional):
source document for sheets; defaults to active document
Returns:list[DB.ViewSheet]: list of selected sheets
Example:>>> from pyrevit import forms>>> forms.select_sheets()... [<Autodesk.Revit.DB.ViewSheet object>,... <Autodesk.Revit.DB.ViewSheet object>]
"""doc = doc or HOST_APP.docall_ops = {}all_sheets = DB.FilteredElementCollector(doc) \
.OfClass(DB.ViewSheet) \
.WhereElementIsNotElementType() \
.ToElements()
if filterfunc:all_sheets = filter(filterfunc, all_sheets)
all_sheets_ops = sorted([SheetOption(x) for x in all_sheets],key=lambda x: x.number)
all_ops['All Sheets'] = all_sheets_ops(continues on next page)
120 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
sheetsets = revit.query.get_sheet_sets(doc)for sheetset in sheetsets:
sheetset_sheets = \[x for x in sheetset.Views if isinstance(x, DB.ViewSheet)]
if filterfunc:sheetset_sheets = filter(filterfunc, sheetset_sheets)
sheetset_ops = sorted([SheetOption(x) for x in sheetset_sheets],key=lambda x: x.number)
all_ops[sheetset.Name] = sheetset_ops
# ask user for multiple sheetsselected_sheets = SelectFromList.show(
all_ops,title=title,group_selector_title='Sheet Sets:',button_name=button_name,width=width,multiselect=multiple,checked_only=True)
return selected_sheets
def select_views(title='Select Views',button_name='Select',width=DEFAULT_INPUTWINDOW_WIDTH,multiple=True,filterfunc=None,doc=None):
"""Standard form for selecting views.
Args:title (str, optional): list window titlebutton_name (str, optional): list window button captionwidth (int, optional): width of list windowmultiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to Truefilterfunc (function):
filter function to be applied to context items.doc (DB.Document, optional):
source document for views; defaults to active document
Returns:list[DB.View]: list of selected views
Example:>>> from pyrevit import forms>>> forms.select_views()... [<Autodesk.Revit.DB.View object>,... <Autodesk.Revit.DB.View object>]
"""all_graphviews = revit.query.get_all_views(doc=doc)
if filterfunc:all_graphviews = filter(filterfunc, all_graphviews)
(continues on next page)
13.2. Implementation 121
pyRevit Documentation, Release 4.5
(continued from previous page)
selected_views = SelectFromList.show(sorted([ViewOption(x) for x in all_graphviews],
key=lambda x: x.name),title=title,button_name=button_name,width=width,multiselect=multiple,checked_only=True)
return selected_views
def select_open_docs(title='Select Open Documents',button_name='OK',width=DEFAULT_INPUTWINDOW_WIDTH, #pylint: disable=W0613multiple=True,filterfunc=None):
"""Standard form for selecting open documents.
Args:title (str, optional): list window titlebutton_name (str, optional): list window button captionwidth (int, optional): width of list windowmultiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to Truefilterfunc (function):
filter function to be applied to context items.
Returns:list[DB.Document]: list of selected documents
Example:>>> from pyrevit import forms>>> forms.select_open_docs()... [<Autodesk.Revit.DB.Document object>,... <Autodesk.Revit.DB.Document object>]
"""# find open documents other than the active docopen_docs = [d for d in revit.docs if not d.IsLinked] #pylint: disable=E1101open_docs.remove(revit.doc) #pylint: disable=E1101
if not open_docs:alert('Only one active document is found. '
'At least two documents must be open. ''Operation cancelled.')
return
return SelectFromList.show(open_docs,name_attr='Title',multiselect=multiple,title=title,button_name=button_name,filterfunc=filterfunc)
(continues on next page)
122 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
def select_titleblocks(title='Select Titleblock',button_name='Select',no_tb_option='No Title Block',width=DEFAULT_INPUTWINDOW_WIDTH,multiple=False,filterfunc=None,doc=None):
"""Standard form for selecting a titleblock.
Args:title (str, optional): list window titlebutton_name (str, optional): list window button captionno_tb_option (str, optional): name of option for no title blockwidth (int, optional): width of list windowmultiselect (bool, optional):
allow multi-selection (uses check boxes). defaults to Truefilterfunc (function):
filter function to be applied to context items.doc (DB.Document, optional):
source document for titleblocks; defaults to active document
Returns:DB.ElementId: selected titleblock id.
Example:>>> from pyrevit import forms>>> forms.select_titleblocks()... <Autodesk.Revit.DB.ElementId object>
"""doc = doc or HOST_APP.doctitleblocks = DB.FilteredElementCollector(doc)\
.OfCategory(DB.BuiltInCategory.OST_TitleBlocks)\
.WhereElementIsElementType()\
.ToElements()
tblock_dict = {'{}: {}'.format(tb.FamilyName,revit.query.get_name(tb)): tb.Id
for tb in titleblocks}tblock_dict[no_tb_option] = DB.ElementId.InvalidElementIdselected_titleblocks = SelectFromList.show(sorted(tblock_dict.keys()),
title=title,button_name=button_name,width=width,multiselect=multiple,filterfunc=filterfunc)
if selected_titleblocks:if multiple:
return [tblock_dict[x] for x in selected_titleblocks]else:
return tblock_dict[selected_titleblocks]
def select_swatch(title='Select Color Swatch', button_name='Select'):"""Standard form for selecting a color swatch.
(continues on next page)
13.2. Implementation 123
pyRevit Documentation, Release 4.5
(continued from previous page)
Args:title (str, optional): swatch list window titlebutton_name (str, optional): swatch list window button caption
Returns:pyrevit.coreutils.colors.RGB: rgb color
Example:>>> from pyrevit import forms>>> forms.select_swatch(title="Select Text Color")... <RGB #CD8800>
"""itemplate_xaml_file = \
os.path.join(op.dirname(__file__), "SwatchContainerStyle.xaml")itemplate = \
wpf.LoadComponent(Controls.ControlTemplate(), itemplate_xaml_file)swatch = SelectFromList.show(
colors.COLORS.values(),title=title,button_name=button_name,width=300,multiselect=False,item_template=itemplate)
return swatch
def select_image(images, title='Select Image', button_name='Select'):"""Standard form for selecting an image.
Args:images (list[str] | list[framework.Imaging.BitmapImage]):
list of image file paths or bitmapstitle (str, optional): swatch list window titlebutton_name (str, optional): swatch list window button caption
Returns:str : path of the selected image
Example:>>> from pyrevit import forms>>> forms.select_image(['C:/path/to/image1.png',
'C:/path/to/image2.png'],title="Select Variation")
... 'C:/path/to/image1.png'"""ptemplate_xaml_file = \
os.path.join(op.dirname(__file__), "ImageListPanelStyle.xaml")ptemplate = \
wpf.LoadComponent(Controls.ItemsPanelTemplate(), ptemplate_xaml_file)
itemplate_xaml_file = \os.path.join(op.dirname(__file__), "ImageListContainerStyle.xaml")
itemplate = \wpf.LoadComponent(Controls.ControlTemplate(), itemplate_xaml_file)
(continues on next page)
124 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
bitmap_images = {}for imageobj in images:
if isinstance(imageobj, str):img = utils.bitmap_from_file(imageobj)if img:
bitmap_images[img] = imageobjelif isinstance(imageobj, framework.Imaging.BitmapImage):
bitmap_images[imageobj] = imageobj
selected_image = SelectFromList.show(sorted(bitmap_images.keys(), key=lambda x: x.UriSource.AbsolutePath),title=title,button_name=button_name,width=500,multiselect=False,item_template=itemplate,items_panel_template=ptemplate)
return bitmap_images.get(selected_image, None)
def alert(msg, title=None, sub_msg=None, expanded=None, footer='',ok=True, cancel=False, yes=False, no=False, retry=False,warn_icon=True, options=None, exitscript=False):
"""Show a task dialog with given message.
Args:msg (str): message to be displayedtitle (str, optional): task dialog titleok (bool, optional): show OK button, defaults to Truecancel (bool, optional): show Cancel button, defaults to Falseyes (bool, optional): show Yes button, defaults to Falseno (bool, optional): show NO button, defaults to Falseretry (bool, optional): show Retry button, defaults to Falseoptions(list[str], optional): list of command link titles in orderexitscript (bool, optional): exit if cancel or no, defaults to False
Returns:bool: True if okay, yes, or retry, otherwise False
Example:>>> from pyrevit import forms>>> forms.alert('Are you sure?',... ok=False, yes=True, no=True, exitscript=True)
"""# BUILD DIALOGcmd_name = EXEC_PARAMS.command_nameif not title:
title = cmd_name if cmd_name else 'pyRevit'tdlg = UI.TaskDialog(title)
options = options or []# add command links if anyif options:
clinks = coreutils.get_enum_values(UI.TaskDialogCommandLinkId)max_clinks = len(clinks)
(continues on next page)
13.2. Implementation 125
pyRevit Documentation, Release 4.5
(continued from previous page)
for idx, cmd in enumerate(options):if idx < max_clinks:
tdlg.AddCommandLink(clinks[idx], cmd)# otherwise add buttonselse:
buttons = coreutils.get_enum_none(UI.TaskDialogCommonButtons)if yes:
buttons |= UI.TaskDialogCommonButtons.Yeselif ok:
buttons |= UI.TaskDialogCommonButtons.Ok
if cancel:buttons |= UI.TaskDialogCommonButtons.Cancel
if no:buttons |= UI.TaskDialogCommonButtons.No
if retry:buttons |= UI.TaskDialogCommonButtons.Retry
tdlg.CommonButtons = buttons
# set textstdlg.MainInstruction = msgtdlg.MainContent = sub_msgtdlg.ExpandedContent = expandedif footer:
footer = footer.strip() + '\n'tdlg.FooterText = footer + 'pyRevit {}'.format(
versionmgr.get_pyrevit_version().get_formatted())
tdlg.TitleAutoPrefix = False
# set icontdlg.MainIcon = \
UI.TaskDialogIcon.TaskDialogIconWarning \if warn_icon else UI.TaskDialogIcon.TaskDialogIconNone
# tdlg.VerificationText = 'verif'
# SHOW DIALOGres = tdlg.Show()
# PROCESS REPONSES# positive responseif res == UI.TaskDialogResult.Ok \
or res == UI.TaskDialogResult.Yes \or res == UI.TaskDialogResult.Retry:
if not exitscript:return True
else:sys.exit()
# negative responseelif res == coreutils.get_enum_none(UI.TaskDialogResult) \
or res == UI.TaskDialogResult.Cancel \or res == UI.TaskDialogResult.No:
if not exitscript:return False
else:sys.exit()
(continues on next page)
126 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
# command link responseelif 'CommandLink' in str(res):
tdresults = sorted([x for x in coreutils.get_enum_values(UI.TaskDialogResult)if 'CommandLink' in str(x)]
)residx = tdresults.index(res)return options[residx]
else:if not exitscript:
return Falseelse:
sys.exit()
def alert_ifnot(condition, msg, *args, **kwargs):"""Show a task dialog with given message if condition is NOT met.
Args:condition (bool): condition to testmsg (str): message to be displayedtitle (str, optional): task dialog titleok (bool, optional): show OK button, defaults to Truecancel (bool, optional): show Cancel button, defaults to Falseyes (bool, optional): show Yes button, defaults to Falseno (bool, optional): show NO button, defaults to Falseretry (bool, optional): show Retry button, defaults to Falseexitscript (bool, optional): exit if cancel or no, defaults to False
Returns:bool: True if okay, yes, or retry, otherwise False
Example:>>> from pyrevit import forms>>> forms.alert_ifnot(value > 12,... 'Are you sure?',... ok=False, yes=True, no=True, exitscript=True)
"""if not condition:
return alert(msg, *args, **kwargs)
def pick_folder(title=None):"""Show standard windows pick folder dialog.
Args:title (str, optional): title for the window
Returns:str: folder path
"""fb_dlg = Forms.FolderBrowserDialog()if title:
fb_dlg.Description = titleif fb_dlg.ShowDialog() == Forms.DialogResult.OK:
return fb_dlg.SelectedPath
(continues on next page)
13.2. Implementation 127
pyRevit Documentation, Release 4.5
(continued from previous page)
def pick_file(file_ext='', files_filter='', init_dir='',restore_dir=True, multi_file=False, unc_paths=False):
r"""Pick file dialog to select a destination file.
Args:file_ext (str): file extensionfiles_filter (str): file filterinit_dir (str): initial directoryrestore_dir (bool): restore last directorymulti_file (bool): allow select multiple filesunc_paths (bool): return unc paths
Returns:str or list[str]: file path or list of file paths if multi_file=True
Example:>>> from pyrevit import forms>>> forms.pick_file(file_ext='csv')... r'C:\output\somefile.csv'
>>> forms.pick_file(file_ext='csv', multi_file=True)... [r'C:\output\somefile1.csv', r'C:\output\somefile2.csv']
>>> forms.pick_file(files_filter='All Files (*.*)|*.*|''Excel Workbook (*.xlsx)|*.xlsx|''Excel 97-2003 Workbook|*.xls',
multi_file=True)... [r'C:\output\somefile1.xlsx', r'C:\output\somefile2.xls']
"""of_dlg = Forms.OpenFileDialog()if files_filter:
of_dlg.Filter = files_filterelse:
of_dlg.Filter = '|*.{}'.format(file_ext)of_dlg.RestoreDirectory = restore_dirof_dlg.Multiselect = multi_fileif init_dir:
of_dlg.InitialDirectory = init_dirif of_dlg.ShowDialog() == Forms.DialogResult.OK:
if multi_file:if unc_paths:
return [coreutils.dletter_to_unc(x)for x in of_dlg.FileNames]
return of_dlg.FileNameselse:
if unc_paths:return coreutils.dletter_to_unc(of_dlg.FileName)
return of_dlg.FileName
def save_file(file_ext='', files_filter='', init_dir='', default_name='',restore_dir=True, unc_paths=False):
r"""Save file dialog to select a destination file for data.
Args:file_ext (str): file extension
(continues on next page)
128 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
files_filter (str): file filterinit_dir (str): initial directorydefault_name (str): default file namerestore_dir (bool): restore last directoryunc_paths (bool): return unc paths
Returns:str: file path
Example:>>> from pyrevit import forms>>> forms.save_file(file_ext='csv')... r'C:\output\somefile.csv'
"""sf_dlg = Forms.SaveFileDialog()if files_filter:
sf_dlg.Filter = files_filterelse:
sf_dlg.Filter = '|*.{}'.format(file_ext)sf_dlg.RestoreDirectory = restore_dirif init_dir:
sf_dlg.InitialDirectory = init_dir
# setting default filenamesf_dlg.FileName = default_name
if sf_dlg.ShowDialog() == Forms.DialogResult.OK:if unc_paths:
return coreutils.dletter_to_unc(sf_dlg.FileName)return sf_dlg.FileName
def pick_excel_file(save=False):"""File pick/save dialog for an excel file.
Args:save (bool): show file save dialog, instead of file pick dialog
Returns:str: file path
"""if save:
return save_file(file_ext='xlsx')return pick_file(files_filter='All Files (*.*)|*.*|'
'Excel Workbook (*.xlsx)|*.xlsx|''Excel 97-2003 Workbook|*.xls')
def save_excel_file():"""File save dialog for an excel file.
Returns:str: file path
"""return pick_excel_file(save=True)
(continues on next page)
13.2. Implementation 129
pyRevit Documentation, Release 4.5
(continued from previous page)
def check_workshared(doc=None, message='Model is not workshared.'):"""Verify if model is workshared and notify user if not.
Args:doc (DB.Document): target document, current of not providedmessage (str): prompt message if returning False
Returns:bool: True if doc is workshared
"""doc = doc or HOST_APP.docif not doc.IsWorkshared:
alert(message, warn_icon=True)return False
return True
def check_selection(exitscript=False,message='At least one element must be selected.'):
"""Verify if selection is not empty notify user if it is.
Args:exitscript (bool): exit script if returning Falsemessage (str): prompt message if returning False
Returns:bool: True if selection has at least one item
"""if revit.get_selection().is_empty:
alert(message, exitscript=exitscript)return False
return True
def check_familydoc(doc=None, family_cat=None, exitscript=False):"""Verify document is a Family and notify user of not.
Args:doc (DB.Document): target document, current of not providedfamily_cat (str): family category nameexitscript (bool): exit script if returning False
Returns:bool: True if doc is a Family and of provided category
Example:>>> from pyrevit import forms>>> forms.check_familydoc(doc=revit.doc, family_cat='Data Devices')... True
"""doc = doc or HOST_APP.docfamily_cat = revit.query.get_category(family_cat)if doc.IsFamilyDocument and family_cat:
if doc.OwnerFamily.FamilyCategory.Id == family_cat.Id:return True
elif doc.IsFamilyDocument and not family_cat:return True
(continues on next page)
130 Chapter 13. pyrevit.forms
pyRevit Documentation, Release 4.5
(continued from previous page)
family_type_msg = ' of type {}'\.format(family_cat.Name) if family_cat else''
alert('Active document must be a Family document{}.'.format(family_type_msg), exitscript=exitscript)
return False
def toast(message, title='pyRevit', appid='pyRevit',icon=None, click=None, actions=None):
"""Show a Windows 10 notification.
Args:message (str): notification messagetitle (str): notification titleappid (str): app name (will show under message)icon (str): file path to icon .ico file (defaults to pyRevit icon)click (str): click action commands stringactions (dict): dictionary of button names and action strings
Example:>>> script.toast("Hello World!",... title="My Script",... appid="MyAPP",... click="https://eirannejad.github.io/pyRevit/",... actions={... "Open Google":"https://google.com",... "Open Toast64":"https://github.com/go-toast/toast"... })
"""toaster.send_toast(
message,title=title,appid=appid,icon=icon,click=click,actions=actions)
def ask_for_string(default=None, prompt=None, title=None, **kwargs):return GetValueWindow.show(
None,value_type='string',default=default,prompt=prompt,title=title,
**kwargs)
def ask_for_unique_string(reserved_values,default=None, prompt=None, title=None, **kwargs):
return GetValueWindow.show(None,value_type='string',default=default,prompt=prompt,
(continues on next page)
13.2. Implementation 131
pyRevit Documentation, Release 4.5
(continued from previous page)
title=title,reserved_values=reserved_values,
**kwargs)
def ask_for_one_item(items, default=None, prompt=None, title=None, **kwargs):return GetValueWindow.show(
items,value_type='dropdown',default=default,prompt=prompt,title=title,
**kwargs)
def ask_for_date(default=None, prompt=None, title=None, **kwargs):return GetValueWindow.show(
None,value_type='date',default=default,prompt=prompt,title=title,
**kwargs)
def inform_wip():alert("Work in progress.", exitscript=True)
132 Chapter 13. pyrevit.forms
CHAPTER 14
pyrevit.framework
Provide access to DotNet Framework.
Example
>>> from pyrevit.framework import Assembly, Windows
pyrevit.framework.get_dll_file(assembly_name)Return path to given assembly name.
pyrevit.framework.get_type(fw_object)Return CLR type of an object.
Parameters fw_object – Dotnet Framework Object Instance
14.1 Implementation
"""Provide access to DotNet Framework.
Example:>>> from pyrevit.framework import Assembly, Windows
"""
#pylint: disable=W0703,C0302,C0103,W0614,E0401,W0611,C0413import os.path as op
import clrimport System
clr.AddReference("System.Core")clr.AddReference('System.Management')
(continues on next page)
133
pyRevit Documentation, Release 4.5
(continued from previous page)
clr.AddReferenceByPartialName('System.Windows.Forms')clr.AddReferenceByPartialName('System.Drawing')clr.AddReference('PresentationCore')clr.AddReference('PresentationFramework')clr.AddReference('System.Xml.Linq')clr.AddReferenceByPartialName('WindowsBase')
# add linq extensions?clr.ImportExtensions(System.Linq)
from System import AppDomain, Versionfrom System import Typefrom System import Uri, Guidfrom System import EventHandlerfrom System import Array, IntPtr, Enumfrom System import Convertfrom System.Text import Encodingfrom System.Collections import IEnumerator, IEnumerablefrom System.Collections.Generic import List, Dictionaryfrom System import DateTime, DateTimeOffset
from System import Diagnosticsfrom System.Diagnostics import Processfrom System.Diagnostics import Stopwatch
from System import Reflectionfrom System.Reflection import Assembly, AssemblyNamefrom System.Reflection import TypeAttributes, MethodAttributesfrom System.Reflection import CallingConventionsfrom System.Reflection import BindingFlagsfrom System.Reflection.Emit import AssemblyBuilderAccessfrom System.Reflection.Emit import CustomAttributeBuilder, OpCodes
from System import IOfrom System.IO import IOException, DriveInfo, Path, StringReader
from System import Netfrom System.Net import WebClient, WebRequest, WebProxy
from System import ComponentModelfrom System import Drawingfrom System import Windowsfrom System.Windows import Formsfrom System.Windows.Forms import Clipboardfrom System.Windows import Controlsfrom System.Windows import Mediafrom System.Windows import Threadingfrom System.Windows import Interopfrom System.Windows import Inputfrom System.Windows.Media import Imaging, SolidColorBrush, Colorfrom System import Mathfrom System.CodeDom import Compilerfrom System.Management import ManagementObjectSearcherfrom System.Runtime.Serialization import FormatterServices
from Microsoft.CSharp import CSharpCodeProvider
(continues on next page)
134 Chapter 14. pyrevit.framework
pyRevit Documentation, Release 4.5
(continued from previous page)
clr.AddReference('IronPython.Wpf')import wpf
from pyrevit import BIN_DIR
def get_type(fw_object):"""Return CLR type of an object.
Args:fw_object: Dotnet Framework Object Instance
"""return clr.GetClrType(fw_object)
def get_dll_file(assembly_name):"""Return path to given assembly name."""addin_file = op.join(BIN_DIR, assembly_name + '.dll')if op.exists(addin_file):
return addin_file
14.1. Implementation 135
pyRevit Documentation, Release 4.5
136 Chapter 14. pyrevit.framework
CHAPTER 15
pyrevit.script
See pyrevit.script Module
Provide basic utilities for pyRevit scripts.
Example
>>> from pyrevit import script>>> script.clipboard_copy('some text')>>> data = script.journal_read('data-key')>>> script.exit()
pyrevit.script.clipboard_copy(string_to_copy)Copy string to Windows Clipboard.
pyrevit.script.exit()Stop the script execution and exit.
pyrevit.script.get_all_buttons()Find and return all ui buttons matching current script command name.
Sometimes tools are duplicated across extensions for user access control so this would help smart buttons to findall the loaded buttons and make icon adjustments.
Returns list of ui button objects
Return type list(pyrevit.coreutils.ribbon._PyRevitRibbonButton)
pyrevit.script.get_alt_script_path()Return alternate script path of the current pyRevit command.
Returns alternate script path
Return type str
pyrevit.script.get_bundle_file(file_name)Return full path to file under current script bundle.
137
pyRevit Documentation, Release 4.5
Parameters file_name (str) – bundle file name
Returns full bundle file path
Return type str
pyrevit.script.get_bundle_files(sub_path=None)Return full path to all file under current script bundle.
Returns list of bundle file paths
Return type list[str]
pyrevit.script.get_bundle_name()Return bundle name of the current pyRevit command.
Returns bundle name (e.g. MyButton.pushbutton)
Return type str
pyrevit.script.get_button()Find and return current script ui button.
Returns ui button object
Return type pyrevit.coreutils.ribbon._PyRevitRibbonButton
pyrevit.script.get_config(section=None)Create and return config section parser object for current script.
Parameters section (str, optional) – config section name
Returns Config section parser object
Return type pyrevit.coreutils.configparser.PyRevitConfigSectionParser
pyrevit.script.get_data_file(file_id, file_ext, add_cmd_name=False)Return filename to be used by a user script to store data.
File name is generated in this format: pyRevit_{Revit Version}_{file_id}.{file_ext}
Example
>>> script.get_data_file('mydata', 'data')'.../pyRevit_2018_mydata.data'>>> script.get_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_2018_Command Name_mydata.data'
Data files are not cleaned up at pyRevit startup. Script should manage cleaning up these files.
Parameters
• file_id (str) – unique id for the filename
• file_ext (str) – file extension
• add_cmd_name (bool, optional) – add command name to file name
Returns full file path
Return type str
138 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
pyrevit.script.get_document_data_file(file_id, file_ext, add_cmd_name=False)Return filename to be used by a user script to store data.
File name is generated in this format: pyRevit_{Revit Version}_{file_id}_{Project Name}.{file_ext}
Example
>>> script.get_document_data_file('mydata', 'data')'.../pyRevit_2018_mydata_Project1.data'>>> script.get_document_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_2018_Command Name_mydata_Project1.data'
Document data files are not cleaned up at pyRevit startup. Script should manage cleaning up these files.
Parameters
• file_id (str) – unique id for the filename
• file_ext (str) – file extension
• add_cmd_name (bool, optional) – add command name to file name
Returns full file path
Return type str
pyrevit.script.get_envvar(envvar)Return value of give pyRevit environment variable.
The environment variable system is used to retain small values in memory between script runs (e.g. ac-tive/inactive state for toggle tools). Do not store large objects in memory using this method. List of currentlyset environment variables could be sees in pyRevit settings window.
Parameters envvar (str) – name of environment variable
Returns type of object stored in environment variable
Return type any
Example
>>> script.get_envvar('ToolActiveState')True
pyrevit.script.get_extension_name()Return extension name of the current pyRevit command.
Returns extension name (e.g. MyExtension.extension)
Return type str
pyrevit.script.get_info()Return info on current pyRevit command.
Returns Command info object
Return type pyrevit.extensions.genericcomps.GenericUICommand
139
pyRevit Documentation, Release 4.5
pyrevit.script.get_instance_data_file(file_id, add_cmd_name=False)Return filename to be used by a user script to store data.
File name is generated in this format: pyRevit_{Revit Version}_{Process Id}_{file_id}.{file_ext}
Example
>>> script.get_instance_data_file('mydata')'.../pyRevit_2018_6684_mydata.tmp'>>> script.get_instance_data_file('mydata', add_cmd_name=True)'.../pyRevit_2018_6684_Command Name_mydata.tmp'
Instance data files are cleaned up at pyRevit startup.
Parameters
• file_id (str) – unique id for the filename
• add_cmd_name (bool, optional) – add command name to file name
Returns full file path
Return type str
pyrevit.script.get_logger()Create and return logger named for current script.
Returns Logger object
Return type pyrevit.coreutils.logger.LoggerWrapper
pyrevit.script.get_output()Return object wrapping output window for current script.
Returns Output wrapper object
Return type pyrevit.output.PyRevitOutputWindow
pyrevit.script.get_pyrevit_version()Return pyRevit version.
Returns pyRevit version provider
Return type pyrevit.versionmgr.PyRevitVersion
pyrevit.script.get_results()Return command results dictionary for logging.
Returns Command results dict
Return type pyrevit.usagelog.record.CommandCustomResults
pyrevit.script.get_script_path()Return script path of the current pyRevit command.
Returns script path
Return type str
pyrevit.script.get_unique_id()Return unique id of the current pyRevit command.
Returns command unique id
140 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
Return type str
pyrevit.script.get_universal_data_file(file_id, file_ext, add_cmd_name=False)Return filename to be used by a user script to store data.
File name is generated in this format: pyRevit_{file_id}.{file_ext}
Example
>>> script.get_universal_data_file('mydata', 'data')'.../pyRevit_mydata.data'>>> script.get_universal_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_Command Name_mydata.data'
Universal data files are not cleaned up at pyRevit startup. Script should manage cleaning up these files.
Parameters
• file_id (str) – unique id for the filename
• file_ext (str) – file extension
• add_cmd_name (bool, optional) – add command name to file name
Returns full file path
Return type str
pyrevit.script.journal_read(data_key)Read value for provided key from active Revit journal.
Parameters data_key (str) – data key
Returns data value string
Return type str
pyrevit.script.journal_write(data_key, msg)Write key and value to active Revit journal for current command.
Parameters
• data_key (str) – data key
• msg (str) – data value string
pyrevit.script.load_index(index_file=’index.html’)Load html file into output window.
This method expects index.html file in the current command bundle, unless full path to an html file is provided.
Parameters index_file (str, optional) – full path of html file.
pyrevit.script.open_url(url)Open url in a new tab in default webbrowser.
pyrevit.script.reset_config(section=None)Reset pyRevit config.
Script should call this to reset any save configuration by removing section related to current script.
Parameters section (str, optional) – config section name
141
pyRevit Documentation, Release 4.5
pyrevit.script.save_config()Save pyRevit config.
Scripts should call this to save any changes they have done to their config section object received fromscript.get_config() method.
pyrevit.script.set_envvar(envvar, value)Set value of give pyRevit environment variable.
The environment variable system is used to retain small values in memory between script runs (e.g. ac-tive/inactive state for toggle tools). Do not store large objects in memory using this method. List of currentlyset environment variables could be sees in pyRevit settings window.
Parameters
• envvar (str) – name of environment variable
• value (any) – value of environment variable
Example
>>> script.set_envvar('ToolActiveState', False)>>> script.get_envvar('ToolActiveState')False
pyrevit.script.show_file_in_explorer(file_path)Show file in Windows Explorer.
pyrevit.script.toggle_icon(new_state, on_icon_path=None, off_icon_path=None)Set the state of button icon (on or off).
This method expects on.png and off.png in command bundle for on and off icon states, unless full path of iconstates are provided.
Parameters
• new_state (bool) – state of the ui button icon.
• on_icon_path (str, optional) – full path of icon for on state. default=’on.png’
• off_icon_path (str, optional) – full path of icon for off state. default=’off.png’
15.1 Implementation
"""Provide basic utilities for pyRevit scripts.
Example:>>> from pyrevit import script>>> script.clipboard_copy('some text')>>> data = script.journal_read('data-key')>>> script.exit()
"""
import sysimport osimport os.path as opimport warnings
(continues on next page)
142 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
(continued from previous page)
from pyrevit import EXEC_PARAMS, PyRevitExceptionfrom pyrevit.coreutils import loggerfrom pyrevit.coreutils import appdatafrom pyrevit.coreutils import envvarsfrom pyrevit import frameworkfrom pyrevit import revitfrom pyrevit import outputfrom pyrevit import versionmgrfrom pyrevit.labs import TargetApps
# suppress any warning generated by native or third-party moduleswarnings.filterwarnings("ignore")
#pylint: disable=W0703,C0302,C0103,W0614mlogger = logger.get_logger(__name__)
def get_info():"""Return info on current pyRevit command.
Returns::obj:`pyrevit.extensions.genericcomps.GenericUICommand`:
Command info object"""from pyrevit.extensions.extensionmgr import get_command_from_pathreturn get_command_from_path(EXEC_PARAMS.command_path)
def get_script_path():"""Return script path of the current pyRevit command.
Returns:str: script path
"""return EXEC_PARAMS.command_path
def get_alt_script_path():"""Return alternate script path of the current pyRevit command.
Returns:str: alternate script path
"""return EXEC_PARAMS.command_alt_path
def get_bundle_name():"""Return bundle name of the current pyRevit command.
Returns:str: bundle name (e.g. MyButton.pushbutton)
"""return EXEC_PARAMS.command_bundle
(continues on next page)
15.1. Implementation 143
pyRevit Documentation, Release 4.5
(continued from previous page)
def get_extension_name():"""Return extension name of the current pyRevit command.
Returns:str: extension name (e.g. MyExtension.extension)
"""return EXEC_PARAMS.command_extension
def get_unique_id():"""Return unique id of the current pyRevit command.
Returns:str: command unique id
"""return EXEC_PARAMS.command_uniqueid
def get_results():"""Return command results dictionary for logging.
Returns::obj:`pyrevit.usagelog.record.CommandCustomResults`:
Command results dict"""from pyrevit.usagelog.record import CommandCustomResultsreturn CommandCustomResults()
def get_pyrevit_version():"""Return pyRevit version.
Returns::obj:`pyrevit.versionmgr.PyRevitVersion`: pyRevit version provider
"""return versionmgr.get_pyrevit_version()
def get_logger():"""Create and return logger named for current script.
Returns::obj:`pyrevit.coreutils.logger.LoggerWrapper`: Logger object
"""return logger.get_logger(EXEC_PARAMS.command_name)
def get_output():"""Return object wrapping output window for current script.
Returns::obj:`pyrevit.output.PyRevitOutputWindow`: Output wrapper object
"""return output.get_output()
def get_config(section=None):(continues on next page)
144 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Create and return config section parser object for current script.
Args:section (str, optional): config section name
Returns::obj:`pyrevit.coreutils.configparser.PyRevitConfigSectionParser`:
Config section parser object"""from pyrevit.userconfig import user_configif not section:
script_cfg_postfix = 'config'section = EXEC_PARAMS.command_name + script_cfg_postfix
try:return user_config.get_section(section)
except Exception:return user_config.add_section(section)
def save_config():"""Save pyRevit config.
Scripts should call this to save any changes they have done to theirconfig section object received from script.get_config() method."""from pyrevit.userconfig import user_configuser_config.save_changes()
def reset_config(section=None):"""Reset pyRevit config.
Script should call this to reset any save configuration by removingsection related to current script.
Args:section (str, optional): config section name
"""from pyrevit.userconfig import user_configif not section:
script_cfg_postfix = 'config'section = EXEC_PARAMS.command_name + script_cfg_postfix
elif section in [TargetApps.Revit.PyRevitConsts.ConfigsCoreSection]:raise PyRevitException('Can not remove internal config section: {}'
.format(section))
try:user_config.remove_section(section)user_config.save_changes()
except Exception:mlogger.debug('Failed resetting config for %s (%s)',
EXEC_PARAMS.command_name, section)
def get_universal_data_file(file_id, file_ext, add_cmd_name=False):"""Return filename to be used by a user script to store data.
(continues on next page)
15.1. Implementation 145
pyRevit Documentation, Release 4.5
(continued from previous page)
File name is generated in this format:``pyRevit_{file_id}.{file_ext}``
Example:>>> script.get_universal_data_file('mydata', 'data')'.../pyRevit_mydata.data'>>> script.get_universal_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_Command Name_mydata.data'
Universal data files are not cleaned up at pyRevit startup.Script should manage cleaning up these files.
Args:file_id (str): unique id for the filenamefile_ext (str): file extensionadd_cmd_name (bool, optional): add command name to file name
Returns:str: full file path
"""if add_cmd_name:
script_file_id = '{}_{}'.format(EXEC_PARAMS.command_name, file_id)else:
script_file_id = file_id
return appdata.get_universal_data_file(script_file_id, file_ext)
def get_data_file(file_id, file_ext, add_cmd_name=False):"""Return filename to be used by a user script to store data.
File name is generated in this format:``pyRevit_{Revit Version}_{file_id}.{file_ext}``
Example:>>> script.get_data_file('mydata', 'data')'.../pyRevit_2018_mydata.data'>>> script.get_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_2018_Command Name_mydata.data'
Data files are not cleaned up at pyRevit startup.Script should manage cleaning up these files.
Args:file_id (str): unique id for the filenamefile_ext (str): file extensionadd_cmd_name (bool, optional): add command name to file name
Returns:str: full file path
"""if add_cmd_name:
script_file_id = '{}_{}'.format(EXEC_PARAMS.command_name, file_id)else:
script_file_id = file_id
(continues on next page)
146 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
(continued from previous page)
return appdata.get_data_file(script_file_id, file_ext)
def get_instance_data_file(file_id, add_cmd_name=False):"""Return filename to be used by a user script to store data.
File name is generated in this format:``pyRevit_{Revit Version}_{Process Id}_{file_id}.{file_ext}``
Example:>>> script.get_instance_data_file('mydata')'.../pyRevit_2018_6684_mydata.tmp'>>> script.get_instance_data_file('mydata', add_cmd_name=True)'.../pyRevit_2018_6684_Command Name_mydata.tmp'
Instance data files are cleaned up at pyRevit startup.
Args:file_id (str): unique id for the filenameadd_cmd_name (bool, optional): add command name to file name
Returns:str: full file path
"""if add_cmd_name:
script_file_id = '{}_{}'.format(EXEC_PARAMS.command_name, file_id)else:
script_file_id = file_id
return appdata.get_instance_data_file(script_file_id)
def get_document_data_file(file_id, file_ext, add_cmd_name=False):"""Return filename to be used by a user script to store data.
File name is generated in this format:``pyRevit_{Revit Version}_{file_id}_{Project Name}.{file_ext}``
Example:>>> script.get_document_data_file('mydata', 'data')'.../pyRevit_2018_mydata_Project1.data'>>> script.get_document_data_file('mydata', 'data', add_cmd_name=True)'.../pyRevit_2018_Command Name_mydata_Project1.data'
Document data files are not cleaned up at pyRevit startup.Script should manage cleaning up these files.
Args:file_id (str): unique id for the filenamefile_ext (str): file extensionadd_cmd_name (bool, optional): add command name to file name
Returns:str: full file path
"""proj_info = revit.query.get_project_info()
(continues on next page)
15.1. Implementation 147
pyRevit Documentation, Release 4.5
(continued from previous page)
if add_cmd_name:script_file_id = '{}_{}_{}'.format(EXEC_PARAMS.command_name,
file_id,proj_info.filenameor proj_info.name)
else:script_file_id = '{}_{}'.format(file_id,
proj_info.filenameor proj_info.name)
return appdata.get_data_file(script_file_id, file_ext)
def get_bundle_file(file_name):"""Return full path to file under current script bundle.
Args:file_name (str): bundle file name
Returns:str: full bundle file path
"""return op.join(EXEC_PARAMS.command_path, file_name)
def get_bundle_files(sub_path=None):"""Return full path to all file under current script bundle.
Returns:list[str]: list of bundle file paths
"""if sub_path:
command_path = op.join(EXEC_PARAMS.command_path, sub_path)else:
command_path = EXEC_PARAMS.command_pathreturn [op.join(command_path, x) for x in os.listdir(command_path)]
def journal_write(data_key, msg):"""Write key and value to active Revit journal for current command.
Args:data_key (str): data keymsg (str): data value string
"""# Get the StringStringMap class which can write data into.data_map = EXEC_PARAMS.command_data.JournalDatadata_map.Clear()
# Begin to add the support datadata_map.Add(data_key, msg)
def journal_read(data_key):"""Read value for provided key from active Revit journal.
Args:(continues on next page)
148 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
(continued from previous page)
data_key (str): data key
Returns:str: data value string
"""# Get the StringStringMap class which can write data into.data_map = EXEC_PARAMS.command_data.JournalData
# Begin to get the support datareturn data_map[data_key]
def get_button():"""Find and return current script ui button.
Returns::obj:`pyrevit.coreutils.ribbon._PyRevitRibbonButton`: ui button object
"""from pyrevit.coreutils.ribbon import get_current_uipyrvt_tabs = get_current_ui().get_pyrevit_tabs()for tab in pyrvt_tabs:
button = tab.find_child(EXEC_PARAMS.command_name)if button:
return buttonreturn None
def get_all_buttons():"""Find and return all ui buttons matching current script command name.
Sometimes tools are duplicated across extensions for user access controlso this would help smart buttons to find all the loaded buttons and makeicon adjustments.
Returns::obj:`list(pyrevit.coreutils.ribbon._PyRevitRibbonButton)`:
list of ui button objects"""from pyrevit.coreutils.ribbon import get_current_uipyrvt_tabs = get_current_ui().get_pyrevit_tabs()buttons = []for tab in pyrvt_tabs:
button = tab.find_child(EXEC_PARAMS.command_name)if button:
buttons.append(button)return buttons
def toggle_icon(new_state, on_icon_path=None, off_icon_path=None):"""Set the state of button icon (on or off).
This method expects on.png and off.png in command bundle for on and officon states, unless full path of icon states are provided.
Args:new_state (bool): state of the ui button icon.on_icon_path (str, optional): full path of icon for on state.
(continues on next page)
15.1. Implementation 149
pyRevit Documentation, Release 4.5
(continued from previous page)
default='on.png'off_icon_path (str, optional): full path of icon for off state.
default='off.png'"""# find the ui buttonuibuttons = get_all_buttons()if not uibuttons:
mlogger.debug('Can not find ui button.')return
# get icon for on stateif not on_icon_path:
on_icon_path = get_bundle_file('on.png')if not on_icon_path:
mlogger.debug('Script does not have icon for on state.')return
# get icon for off stateif not off_icon_path:
off_icon_path = get_bundle_file('off.png')if not off_icon_path:
mlogger.debug('Script does not have icon for on state.')return
icon_path = on_icon_path if new_state else off_icon_pathmlogger.debug('Setting icon state to: %s (%s)',
new_state, icon_path)
for uibutton in uibuttons:uibutton.set_icon(icon_path)
def exit(): #pylint: disable=W0622"""Stop the script execution and exit."""sys.exit()
def show_file_in_explorer(file_path):"""Show file in Windows Explorer."""import subprocesssubprocess.Popen(r'explorer /select,"{}"'
.format(os.path.normpath(file_path)))
def open_url(url):"""Open url in a new tab in default webbrowser."""import webbrowserreturn webbrowser.open_new_tab(url)
def clipboard_copy(string_to_copy):"""Copy string to Windows Clipboard."""framework.Clipboard.SetText(string_to_copy)
def load_index(index_file='index.html'):"""Load html file into output window.
(continues on next page)
150 Chapter 15. pyrevit.script
pyRevit Documentation, Release 4.5
(continued from previous page)
This method expects index.html file in the current command bundle,unless full path to an html file is provided.
Args:index_file (str, optional): full path of html file.
"""outputwindow = get_output()if not op.isfile(index_file):
index_file = get_bundle_file(index_file)outputwindow.open_page(index_file)
def get_envvar(envvar):"""Return value of give pyRevit environment variable.
The environment variable system is used to retain small values in memorybetween script runs (e.g. active/inactive state for toggle tools). Do notstore large objects in memory using this method. List of currently setenvironment variables could be sees in pyRevit settings window.
Args:envvar (str): name of environment variable
Returns:any: type of object stored in environment variable
Example:>>> script.get_envvar('ToolActiveState')True
"""return envvars.get_pyrevit_env_var(envvar)
def set_envvar(envvar, value):"""Set value of give pyRevit environment variable.
The environment variable system is used to retain small values in memorybetween script runs (e.g. active/inactive state for toggle tools). Do notstore large objects in memory using this method. List of currently setenvironment variables could be sees in pyRevit settings window.
Args:envvar (str): name of environment variablevalue (any): value of environment variable
Example:>>> script.set_envvar('ToolActiveState', False)>>> script.get_envvar('ToolActiveState')False
"""return envvars.set_pyrevit_env_var(envvar, value)
15.1. Implementation 151
pyRevit Documentation, Release 4.5
152 Chapter 15. pyrevit.script
CHAPTER 16
pyrevit.userconfig
Handle reading and parsing, writin and saving of all user configurations.
This module handles the reading and writing of the pyRevit configuration files. It’s been used extensively by pyRevitsub-modules. user_config is set up automatically in the global scope by this module and can be imported intoscripts and other modules to access the default configurations.
All other modules use this module to query user config.
Example
>>> from pyrevit.userconfig import user_config>>> user_config.add_section('newsection')>>> user_config.newsection.property = value>>> user_config.newsection.get('property', default_value)>>> user_config.save_changes()
The user_config object is also the destination for reading and writing configuration by pyRevit scripts throughget_config() of pyrevit.script module. Here is the function source:
def get_config(section=None):"""Create and return config section parser object for current script.
Args:section (str, optional): config section name
Returns::obj:`pyrevit.coreutils.configparser.PyRevitConfigSectionParser`:
Config section parser object"""from pyrevit.userconfig import user_configif not section:
script_cfg_postfix = 'config'section = EXEC_PARAMS.command_name + script_cfg_postfix
(continues on next page)
153
pyRevit Documentation, Release 4.5
(continued from previous page)
try:return user_config.get_section(section)
except Exception:return user_config.add_section(section)
Example
>>> from pyrevit import script>>> cfg = script.get_config()>>> cfg.property = value>>> cfg.get('property', default_value)>>> script.save_config()
class pyrevit.userconfig.PyRevitConfig(cfg_file_path=None, config_type=’Unknown’)Provide read/write access to pyRevit configuration.
Parameters
• cfg_file_path (str) – full path to config file to be used.
• config_type (str) – type of config file
Example
>>> cfg = PyRevitConfig(cfg_file_path)>>> cfg.add_section('sectionname')>>> cfg.sectionname.property = value>>> cfg.sectionname.get('property', default_value)>>> cfg.save_changes()
config_fileCurrent config file path.
get_config_version()Return version of config file used for change detection.
get_ext_root_dirs()Return a list of all extension directories.
Returns list of strings. user extension directories.
Return type list
get_thirdparty_ext_root_dirs(include_default=True)Return a list of external extension directories set by the user.
Returns list of strings. External user extension directories.
Return type list
save_changes()Save user config into associated config file.
set_thirdparty_ext_root_dirs(path_list)Updates list of external extension directories in config file
154 Chapter 16. pyrevit.userconfig
pyRevit Documentation, Release 4.5
Parameters path_list (list[str]) – list of external extension paths
pyrevit.userconfig.find_config_file(target_path)Find config file in target path.
pyrevit.userconfig.verify_configs(config_file_path=None)Create a user settings file.
if config_file_path is not provided, configs will be in memory only
Parameters config_file_path (str, optional) – config file full name and path
Returns pyRevit config file handler
Return type pyrevit.userconfig.PyRevitConfig
16.1 Implementation
"""Handle reading and parsing, writin and saving of all user configurations.
This module handles the reading and writing of the pyRevit configuration files.It's been used extensively by pyRevit sub-modules. :obj:`user_config` isset up automatically in the global scope by this module and can be importedinto scripts and other modules to access the default configurations.
All other modules use this module to query user config.
Example:
>>> from pyrevit.userconfig import user_config>>> user_config.add_section('newsection')>>> user_config.newsection.property = value>>> user_config.newsection.get('property', default_value)>>> user_config.save_changes()
The :obj:`user_config` object is also the destination for reading and writingconfiguration by pyRevit scripts through :func:`get_config` of:mod:`pyrevit.script` module. Here is the function source:
.. literalinclude:: ../../pyrevitlib/pyrevit/script.py:pyobject: get_config
Example:
>>> from pyrevit import script>>> cfg = script.get_config()>>> cfg.property = value>>> cfg.get('property', default_value)>>> script.save_config()
"""#pylint: disable=C0103,C0413,W0703import osimport os.path as op
from pyrevit import EXEC_PARAMS, HOME_DIRfrom pyrevit import EXTENSIONS_DEFAULT_DIR, THIRDPARTY_EXTENSIONS_DEFAULT_DIRfrom pyrevit import PYREVIT_ALLUSER_APP_DIR, PYREVIT_APP_DIR
(continues on next page)
16.1. Implementation 155
pyRevit Documentation, Release 4.5
(continued from previous page)
from pyrevit.labs import TargetApps
from pyrevit.coreutils import touchfrom pyrevit.coreutils import appdatafrom pyrevit.coreutils import configparserfrom pyrevit.coreutils import loggerfrom pyrevit.versionmgr import upgrade
mlogger = logger.get_logger(__name__)
# =============================================================================# fix obsolete config file naming# config file (and all appdata files) used to include username in the filename# this fixes the existing config file with obsolete naming, to new format# from pyrevit import PYREVIT_APP_DIR, PYREVIT_FILE_PREFIX_UNIVERSAL_USER
# OBSOLETE_CONFIG_FILENAME = '{}_{}'.format(PYREVIT_FILE_PREFIX_UNIVERSAL_USER,# 'config.ini')# OBSOLETE_CONFIG_FILEPATH = op.join(PYREVIT_APP_DIR, OBSOLETE_CONFIG_FILENAME)
# if op.exists(OBSOLETE_CONFIG_FILEPATH):# try:# os.rename(OBSOLETE_CONFIG_FILEPATH, CONFIG_FILE)# except Exception as rename_err:# mlogger.error('Failed to update the config file name to new format. '# 'A new configuration file has been created for you '# 'under \n{}'# '\nYour previous pyRevit configuration file still '# 'existing under the same folder. Please close Revit, '# 'open both configuration files and copy and paste '# 'settings from the old config file to new config file. '# 'Then you can remove the old config file as pyRevit '# 'will not be using that anymore. | {}'# .format(CONFIG_FILE, rename_err))# end fix obsolete config file naming# =============================================================================
class PyRevitConfig(configparser.PyRevitConfigParser):"""Provide read/write access to pyRevit configuration.
Args:cfg_file_path (str): full path to config file to be used.config_type (str): type of config file
Example:>>> cfg = PyRevitConfig(cfg_file_path)>>> cfg.add_section('sectionname')>>> cfg.sectionname.property = value>>> cfg.sectionname.get('property', default_value)>>> cfg.save_changes()
"""
def __init__(self, cfg_file_path=None, config_type='Unknown'):(continues on next page)
156 Chapter 16. pyrevit.userconfig
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Load settings from provided config file and setup parser."""# try opening and reading config file in order.super(PyRevitConfig, self).__init__(cfg_file_path=cfg_file_path)
# set log mode on the logger module based on# user settings (overriding the defaults)self._update_env()self._admin = config_type == 'Admin'self.config_type = config_type
def _update_env(self):# update the debug level based on user configmlogger.reset_level()
try:# first check to see if command is not in forced debug modeif not EXEC_PARAMS.forced_debug_mode:
if self.core.debug:mlogger.set_debug_mode()mlogger.debug('Debug mode is enabled in user settings.')
elif self.core.verbose:mlogger.set_verbose_mode()
logger.set_file_logging(self.core.filelogging)except Exception as env_update_err:
mlogger.debug('Error updating env variable per user config. | %s',env_update_err)
@propertydef config_file(self):
"""Current config file path."""return self._cfg_file_path
def get_config_version(self):"""Return version of config file used for change detection."""return self.get_config_file_hash()
def get_ext_root_dirs(self):"""Return a list of all extension directories.
Returns::obj:`list`: list of strings. user extension directories.
"""dir_list = []dir_list.append(EXTENSIONS_DEFAULT_DIR)dir_list.extend(self.get_thirdparty_ext_root_dirs())return dir_list
def get_thirdparty_ext_root_dirs(self, include_default=True):"""Return a list of external extension directories set by the user.
Returns::obj:`list`: list of strings. External user extension directories.
"""dir_list = []if include_default:
(continues on next page)
16.1. Implementation 157
pyRevit Documentation, Release 4.5
(continued from previous page)
# add default ext pathdir_list.append(THIRDPARTY_EXTENSIONS_DEFAULT_DIR)
try:dir_list.extend([
op.expandvars(op.normpath(x.encode('string-escape')))for x in self.core.userextensions])
except Exception as read_err:mlogger.error('Error reading list of user extension folders. | %s',
read_err)
return dir_list
def set_thirdparty_ext_root_dirs(self, path_list):"""Updates list of external extension directories in config file
Args:path_list (list[str]): list of external extension paths
"""try:
self.core.userextensions = \[op.normpath(x) for x in path_list]
except Exception as write_err:mlogger.error('Error setting list of user extension folders. | %s',
write_err)
def save_changes(self):"""Save user config into associated config file."""if not self._admin:
try:super(PyRevitConfig, self).save()
except Exception as save_err:mlogger.error('Can not save user config to: %s | %s',
self.config_file, save_err)
# adjust environment per user configurationsself._update_env()
else:mlogger.debug('Config is in admin mode. Skipping save.')
def find_config_file(target_path):"""Find config file in target path."""return TargetApps.Revit.PyRevit.FindConfigFileInDirectory(target_path)
def verify_configs(config_file_path=None):"""Create a user settings file.
if config_file_path is not provided, configs will be in memory only
Args:config_file_path (str, optional): config file full name and path
Returns::obj:`pyrevit.userconfig.PyRevitConfig`: pyRevit config file handler
"""(continues on next page)
158 Chapter 16. pyrevit.userconfig
pyRevit Documentation, Release 4.5
(continued from previous page)
if config_file_path:mlogger.debug('Creating default config file at: %s', config_file_path)touch(config_file_path)
try:parser = PyRevitConfig(cfg_file_path=config_file_path)
except Exception as read_err:# can not create default user config file under appdata foldermlogger.warning('Can not create config file under: %s | %s',
config_file_path, read_err)parser = PyRevitConfig()
# set hard-coded valuesconsts = TargetApps.Revit.PyRevitConsts# core sectionif not parser.has_section(consts.ConfigsCoreSection):
parser.add_section(consts.ConfigsCoreSection)# checkupatesif not parser.core.has_option(consts.ConfigsCheckUpdatesKey):
parser.core.set_option(consts.ConfigsCheckUpdatesKey, False)# autoupdateif not parser.core.has_option(consts.ConfigsAutoUpdateKey):
parser.core.set_option(consts.ConfigsAutoUpdateKey, False)# verboseif not parser.core.has_option(consts.ConfigsVerboseKey):
parser.core.set_option(consts.ConfigsVerboseKey, True)# debugif not parser.core.has_option(consts.ConfigsDebugKey):
parser.core.set_option(consts.ConfigsDebugKey, False)# fileloggingif not parser.core.has_option(consts.ConfigsFileLoggingKey):
parser.core.set_option(consts.ConfigsFileLoggingKey, False)# startuplogtimeoutif not parser.core.has_option(consts.ConfigsStartupLogTimeoutKey):
parser.core.set_option(consts.ConfigsStartupLogTimeoutKey, 10)# userextensionsif not parser.core.has_option(consts.ConfigsUserExtensionsKey):
parser.core.set_option(consts.ConfigsUserExtensionsKey, [])# compilecsharpif not parser.core.has_option(consts.ConfigsCompileCSharpKey):
parser.core.set_option(consts.ConfigsCompileCSharpKey, True)# compilevbif not parser.core.has_option(consts.ConfigsCompileVBKey):
parser.core.set_option(consts.ConfigsCompileVBKey, True)# loadbetaif not parser.core.has_option(consts.ConfigsLoadBetaKey):
parser.core.set_option(consts.ConfigsLoadBetaKey, False)# rocketmodeif not parser.core.has_option(consts.ConfigsRocketModeKey):
parser.core.set_option(consts.ConfigsRocketModeKey, True)# bincacheif not parser.core.has_option(consts.ConfigsBinaryCacheKey):
parser.core.set_option(consts.ConfigsBinaryCacheKey, True)# usercanupdateif not parser.core.has_option(consts.ConfigsUserCanUpdateKey):
parser.core.set_option(consts.ConfigsUserCanUpdateKey, True)# usercanextend
(continues on next page)
16.1. Implementation 159
pyRevit Documentation, Release 4.5
(continued from previous page)
if not parser.core.has_option(consts.ConfigsUserCanExtendKey):parser.core.set_option(consts.ConfigsUserCanExtendKey, True)
# usercanconfigif not parser.core.has_option(consts.ConfigsUserCanConfigKey):
parser.core.set_option(consts.ConfigsUserCanConfigKey, True)
# usagelogging sectionif not parser.has_section(consts.ConfigsUsageLoggingSection):
parser.add_section(consts.ConfigsUsageLoggingSection)# usagelogging activeif not parser.usagelogging.has_option(consts.ConfigsUsageLoggingStatusKey):
parser.usagelogging.set_option(consts.ConfigsUsageLoggingStatusKey, False)
# usagelogging fileif not parser.usagelogging.has_option(consts.ConfigsUsageLogFilePathKey):
parser.usagelogging.set_option(consts.ConfigsUsageLogFilePathKey, "")# usagelogging serverif not parser.usagelogging.has_option(consts.ConfigsUsageLogServerUrlKey):
parser.usagelogging.set_option(consts.ConfigsUsageLogServerUrlKey, "")
# save config into config fileif config_file_path:
parser.save_changes()mlogger.debug('Default config saved to: %s', config_file_path)
return parser
LOCAL_CONFIG_FILE = ADMIN_CONFIG_FILE = USER_CONFIG_FILE = CONFIG_FILE = ''user_config = None
# location for default pyRevit config filesif not EXEC_PARAMS.doc_mode:
LOCAL_CONFIG_FILE = find_config_file(HOME_DIR)ADMIN_CONFIG_FILE = find_config_file(PYREVIT_ALLUSER_APP_DIR)USER_CONFIG_FILE = find_config_file(PYREVIT_APP_DIR)
# decide which config file to use# check if a config file is inside the repo. for developers config overrideif LOCAL_CONFIG_FILE:
CONFIG_TYPE = 'Local'CONFIG_FILE = LOCAL_CONFIG_FILE
# check to see if there is any config file provided by adminelif ADMIN_CONFIG_FILE:
# if yes, copy that and use as defaultif os.access(ADMIN_CONFIG_FILE, os.W_OK):
CONFIG_TYPE = 'Seed'TargetApps.Revit.PyRevit.SeedConfig(False, ADMIN_CONFIG_FILE)CONFIG_FILE = find_config_file(PYREVIT_APP_DIR)
# unless it's locked. then read that config file and set admin-modeelse:
CONFIG_TYPE = 'Admin'CONFIG_FILE = ADMIN_CONFIG_FILE
# if a config file is available for user use thatelif USER_CONFIG_FILE:
CONFIG_TYPE = 'User'(continues on next page)
160 Chapter 16. pyrevit.userconfig
pyRevit Documentation, Release 4.5
(continued from previous page)
CONFIG_FILE = USER_CONFIG_FILE# if nothing can be found, make oneelse:
CONFIG_TYPE = 'New'# setup config file name and pathCONFIG_FILE = appdata.get_universal_data_file(file_id='config',
file_ext='ini')
mlogger.debug('Using %s config file: %s', CONFIG_TYPE, CONFIG_FILE)
# read config, or setup default config file if not available# this pushes reading settings at first import of this module.try:
verify_configs(CONFIG_FILE)user_config = PyRevitConfig(cfg_file_path=CONFIG_FILE,
config_type=CONFIG_TYPE)upgrade.upgrade_user_config(user_config)
except Exception as cfg_err:mlogger.debug('Can not read confing file at: %s | %s',
CONFIG_FILE, cfg_err)mlogger.debug('Using configs in memory...')user_config = verify_configs()
16.1. Implementation 161
pyRevit Documentation, Release 4.5
162 Chapter 16. pyrevit.userconfig
CHAPTER 17
pyrevit.coreutils
Misc Helper functions for pyRevit.
17.1 pyrevit.coreutils
Misc Helper functions for pyRevit.
Example
>>> from pyrevit import coreutils>>> coreutils.cleanup_string('some string')
class pyrevit.coreutils.FileWatcher(filepath)Simple file version watcher.
This is a simple utility class to look for changes in a file based on its timestamp.
Example
>>> watcher = FileWatcher('/path/to/file.ext')>>> watcher.has_changedTrue
has_changedCompare current file timestamp to the cached timestamp.
update_tstamp()Update the cached timestamp for later comparison.
163
pyRevit Documentation, Release 4.5
class pyrevit.coreutils.SafeDictDictionary that does not fail on any key.
This is a dictionary subclass to help with string formatting with unknown key values.
Example
>>> string = '{target} {attr} is {color}.'>>> safedict = SafeDict({'target': 'Apple',... 'attr': 'Color'})>>> string.format(safedict) # will not fail with missing 'color' key'Apple Color is {color}.'
class pyrevit.coreutils.ScriptFileParser(file_address)Parse python script to extract variables and docstrings.
Primarily designed to assist pyRevit in determining script configurations but can work for any python script.
Example
>>> finder = ScriptFileParser('/path/to/coreutils/__init__.py')>>> finder.docstring()... "Misc Helper functions for pyRevit.">>> finder.extract_param('SomeValue', [])[]
extract_param(param_name, default_value=None)Find variable and extract its value.
Parameters
• param_name (str) – variable name
• default_value (any) – default value to be returned if variable does not exist
Returns value of the variable or None
Return type any
get_docstring()Get global docstring.
class pyrevit.coreutils.TimerTimer class using python native time module.
Example
>>> timer = Timer()>>> timer.get_time()12
get_time()Get Elapsed Time.
restart()Restart Timer.
164 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
pyrevit.coreutils.calculate_dir_hash(dir_path, dir_filter, file_filter)Create a unique hash to represent state of directory.
Parameters
• dir_path (str) – target directory
• dir_filter (str) – exclude directories matching this regex
• file_filter (str) – exclude files matching this regex
Returns hash value as string
Return type str
Example
>>> calculate_dir_hash(source_path, '\.extension', '\.json')"1a885a0cae99f53d6088b9f7cee3bf4d"
pyrevit.coreutils.can_access_url(url_to_open, timeout=1000)Check if url is accessible within timeout.
Parameters
• url_to_open (str) – url to check access for
• timeout (int) – timeout in milliseconds
Returns true if accessible
Return type bool
pyrevit.coreutils.check_internet_connection(timeout=1000)Check if internet connection is available.
Pings a few well-known websites to check if internet connection is present.
Parameters timeout (int) – timeout in milliseconds
Returns url if internet connection is present, None if no internet.
pyrevit.coreutils.check_revittxt_encoding(filename)Check if given file is in UTF-16 (UCS-2 LE) encoding.
Parameters filename (str) – file path
pyrevit.coreutils.check_utf8bom_encoding(filename)Check if given file is in UTF-8 encoding.
Parameters filename (str) – file path
pyrevit.coreutils.cleanup_filename(file_name)Cleanup file name from special characters.
Parameters file_name (str) – file name
Returns cleaned up file name
Return type str
17.1. pyrevit.coreutils 165
pyRevit Documentation, Release 4.5
Example
>>> cleanup_filename('Myfile-(3).txt')"Myfile3.txt"
pyrevit.coreutils.cleanup_string(input_str)Replace special characters in string with another string.
This function was created to help cleanup pyRevit command unique names from any special characters so C#class names can be created based on those unique names.
coreutils.SPECIAL_CHARS is the conversion table for this function.
Parameters input_str (str) – input string to be cleaned
Example
>>> src_str = 'TEST@Some*<value>'>>> cleanup_string(src_str)"TESTATSomeSTARvalue"
pyrevit.coreutils.correct_revittxt_encoding(filename)Convert encoding of text file generated by Revit to UTF-8.
Parameters filename (str) – file path
pyrevit.coreutils.create_ext_command_attrs()Create dotnet attributes for Revit extenrnal commads.
This method is used in creating custom dotnet types for pyRevit commands and compiling them into aDLL assembly. Current implementation sets RegenerationOption.Manual and TransactionMode.Manual
Returns list of CustomAttributeBuilder for RegenerationOption andTransactionMode attributes.
Return type list
pyrevit.coreutils.create_type(modulebuilder, type_class, class_name, custom_attr_list, *args)Create a dotnet type for a pyRevit command.
See baseclasses.cs code for the template pyRevit command dotnet type and its constructor default argu-ments that must be provided here.
Parameters
• modulebuilder (ModuleBuilder) – dotnet module builder
• type_class (type) – source dotnet type for the command
• class_name (str) – name for the new type
• custom_attr_list (list) – list of dotnet attributes for the type
• *args – list of arguments to be used with type constructor
Returns returns created dotnet type
Return type type
166 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
Example
>>> asm_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(... win_asm_name, AssemblyBuilderAccess.RunAndSave, filepath... )>>> module_builder = asm_builder.DefineDynamicModule(... ext_asm_file_name, ext_asm_full_file_name... )>>> create_type(... module_builder,... PyRevitCommand,... "PyRevitSomeCommandUniqueName",... coreutils.create_ext_command_attrs(),... [scriptpath, atlscriptpath, searchpath, helpurl, name,... bundle, extension, uniquename, False, False])<type PyRevitSomeCommandUniqueName>
pyrevit.coreutils.current_date()Return formatted current date.
Current implementation uses %Y-%m-%d to format date.
Returns formatted current date.
Return type str
Example
>>> current_date()'2018-01-03'
pyrevit.coreutils.current_time()Return formatted current time.
Current implementation uses %H:%M:%S to format time.
Returns formatted current time.
Return type str
Example
>>> current_time()'07:50:53'
pyrevit.coreutils.decrement_str(input_str, step)Decrement identifier.
Parameters
• input_str (str) – identifier e.g. A310a
• step (int) – number of steps to change the identifier
Returns modified identifier
Return type str
17.1. pyrevit.coreutils 167
pyRevit Documentation, Release 4.5
Example
>>> decrement_str('A310a')'A309z'
pyrevit.coreutils.dletter_to_unc(dletter_path)Convert drive letter path into UNC path of that drive.
Parameters dletter_path (str) – drive letter path
Returns UNC path
Return type str
Example
>>> # assuming J: is mapped to //filestore/server/jdrive>>> dletter_to_unc('J:/somefile.txt')'//filestore/server/jdrive/somefile.txt'
pyrevit.coreutils.extract_guid(source_str)Extract GUID number from a string.
pyrevit.coreutils.extract_range(formatted_str, max_range=500)Extract range from formatted string.
String must be formatted as below A103 No range A103-A106 A103 to A106 A103:A106 A103 to A106A103,A105a A103 and A105a A103;A105a A103 and A105a
Parameters formatted_str (str) – string specifying range
Returns list of names in the specified range
Return type list
Example
>>> exract_range('A103:A106')['A103', 'A104', 'A105', 'A106']>>> exract_range('S203-S206')['S203', 'S204', 'S205', 'S206']>>> exract_range('M00A,M00B')['M00A', 'M00B']
pyrevit.coreutils.filter_null_items(src_list)Remove None items in the given list.
Parameters src_list (list) – list of any items
Returns cleaned list
Return type list
pyrevit.coreutils.find_loaded_asm(asm_info, by_partial_name=False, by_location=False)Find loaded assembly based on name, partial name, or location.
Parameters
• asm_info (str) – name or location of the assembly
168 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
• by_partial_name (bool) – returns all assemblies that has the asm_info
• by_location (bool) – returns all assemblies matching location
Returns List of all loaded assemblies matching the provided info If only one assembly has beenfound, it returns the assembly. None will be returned if assembly is not loaded.
Return type list
pyrevit.coreutils.find_type_by_name(assembly, type_name)Find type by name in assembly.
Parameters
• assembly (Assembly) – assembly to find the type in
• type_name (str) – type name
Returns returns the type if found.
Raises PyRevitException if type not found.
pyrevit.coreutils.format_hex_rgb(rgb_value)Formats rgb value as #RGB value string.
pyrevit.coreutils.fully_remove_dir(dir_path)Remove directory recursively.
Parameters dir_path (str) – directory path
pyrevit.coreutils.fuzzy_search_ratio(target_string, sfilter)Match target string against the filter and return a match ratio.
pyrevit.coreutils.get_all_subclasses(parent_classes)Return all subclasses of a python class.
Parameters parent_classes (list) – list of python classes
Returns list of python subclasses
Return type list
pyrevit.coreutils.get_canonical_parts(canonical_string)Splots argument using dot, returning all composing parts.
Parameters canonical_string (str) – Source string e.g. “Config.SubConfig”
Returns list of composing parts
Return type list[str]
Example
>>> get_canonical_parts("Config.SubConfig")['Config', 'SubConfig']
pyrevit.coreutils.get_enum_none(enum_type)Returns the None value in given Enum.
pyrevit.coreutils.get_enum_value(enum_type, value_string)Return enum value matching given value string (case insensitive)
pyrevit.coreutils.get_enum_values(enum_type)Returns enum values.
17.1. pyrevit.coreutils 169
pyRevit Documentation, Release 4.5
pyrevit.coreutils.get_file_name(file_path)Return file basename of the given file.
Parameters file_path (str) – file path
pyrevit.coreutils.get_mapped_drives_dict()Return a dictionary of currently mapped network drives.
pyrevit.coreutils.get_revit_instance_count()Return number of open host app instances.
Returns number of open host app instances.
Return type int
pyrevit.coreutils.get_str_hash(source_str)Calculate hash value of given string.
Current implementation uses hashlib.md5() hash function.
Parameters source_str (str) – source str
Returns hash value as string
Return type str
pyrevit.coreutils.get_sub_folders(search_folder)Get a list of all subfolders directly inside provided folder.
Parameters search_folder (str) – folder path
Returns list of subfolder names
Return type list
pyrevit.coreutils.has_nonprintable(input_str)Check input string for non-printable characters.
Parameters input_str (str) – input string
Returns True if contains non-printable characters
Return type bool
pyrevit.coreutils.increment_str(input_str, step)Incremenet identifier.
Parameters
• input_str (str) – identifier e.g. A310a
• step (int) – number of steps to change the identifier
Returns modified identifier
Return type str
Example
>>> increment_str('A319z')'A320a'
pyrevit.coreutils.inspect_calling_scope_global_var(variable_name)Trace back the stack to find the variable in the caller global stack.
170 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
Parameters variable_name (str) – variable name to look up in caller global scope
pyrevit.coreutils.inspect_calling_scope_local_var(variable_name)Trace back the stack to find the variable in the caller local stack.
PyRevitLoader defines __revit__ in builtins and __window__ in locals. Thus, modules have access to __revit__but not to __window__. This function is used to find __window__ in the caller stack.
Parameters variable_name (str) – variable name to look up in caller local scope
pyrevit.coreutils.is_blank(input_string)Check if input string is blank (multiple white spaces is blank).
Parameters input_string (str) – input string
Returns True if string is blank
Return type bool
Example
>>> is_blank(' ')True
pyrevit.coreutils.is_box_visible_on_screens(left, top, width, height)Check if given box is visible on any screen.
pyrevit.coreutils.is_url_valid(url_string)Check if given URL is in valid format.
Parameters url_string (str) – URL string
Returns True if URL is in valid format
Return type bool
Example
>>> is_url_valid('https://www.google.com')True
pyrevit.coreutils.join_strings(str_list, separator=’;’)Join strings using provided separator.
Parameters
• str_list (list) – list of string values
• separator (str) – single separator character, defaults to DEFAULT_SEPARATOR
Returns joined string
Return type str
pyrevit.coreutils.load_asm(asm_name)Load assembly by name into current domain.
Parameters asm_name (str) – assembly name
Returns returns the loaded assembly, None if not loaded.
17.1. pyrevit.coreutils 171
pyRevit Documentation, Release 4.5
pyrevit.coreutils.load_asm_file(asm_file)Load assembly by file into current domain.
Parameters asm_file (str) – assembly file path
Returns returns the loaded assembly, None if not loaded.
pyrevit.coreutils.make_canonical_name(*args)Join arguments with dot creating a unique id.
Parameters *args – Variable length argument list of type str
Returns dot separated unique name
Return type str
Example
>>> make_canonical_name('somename', 'someid', 'txt')"somename.someid.txt"
pyrevit.coreutils.new_uuid()Create a new UUID (using dotnet Guid.NewGuid)
pyrevit.coreutils.open_folder_in_explorer(folder_path)Open given folder in Windows Explorer.
Parameters folder_path (str) – directory path
pyrevit.coreutils.prepare_html_str(input_string)Reformat html string and prepare for pyRevit output window.
pyRevit output window renders html content. But this means that < and > characters in outputs from python(e.g. <class at xxx>) will be treated as html tags. To avoid this, all <> characters that are defining html contentneed to be replaced with special phrases. pyRevit output later translates these phrases back in to < and >. Thatis how pyRevit ditinquishes between <> printed from python and <> that define html.
Parameters input_string (str) – input html string
Example
>>> prepare_html_str('<p>Some text</p>')"&clt;p&cgt;Some text&clt;/p&cgt;"
pyrevit.coreutils.random_alpha()Return a random alpha value (between 0 and 1.00).
pyrevit.coreutils.random_color()Return a random color channel value (between 0 and 255).
pyrevit.coreutils.random_hex_color()Return a random color in hex format.
Example
>>> random_hex_color()'#FF0000'
172 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
pyrevit.coreutils.random_rgb_color()Return a random color in rgb format.
Example
>>> random_rgb_color()'rgb(255, 0, 0)'
pyrevit.coreutils.random_rgba_color()Return a random color in rgba format.
Example
>>> random_rgba_color()'rgba(255, 0, 0, 0.5)'
pyrevit.coreutils.read_source_file(source_file_path)Read text file and return contents.
Parameters source_file_path (str) – target file path
Returns file contents
Return type str
Raises PyRevitException on read error
pyrevit.coreutils.reformat_string(orig_str, orig_format, new_format)Reformat a string into a new format.
Extracts information from a string based on a given pattern, and recreates a new string based on the given newpattern.
Parameters
• orig_str (str) – Original string to be reformatted
• orig_format (str) – Pattern of the original str (data to be extracted)
• new_format (str) – New pattern (how to recompose the data)
Returns Reformatted string
Return type str
Example
>>> reformat_string('150 - FLOOR/CEILING - WD - 1 HR - FLOOR ASSEMBLY','{section} - {loc} - {mat} - {rating} - {name}','{section}:{mat}:{rating} - {name} ({loc})'))
'150:WD:1 HR - FLOOR ASSEMBLY (FLOOR/CEILING)'
pyrevit.coreutils.reverse_dict(input_dict)Reverse the key, value pairs.
Parameters input_dict (dict) – source ordered dict
Returns reversed dictionary
17.1. pyrevit.coreutils 173
pyRevit Documentation, Release 4.5
Return type defaultdict
Example
>>> reverse_dict({1: 2, 3: 4})defaultdict(<type 'list'>, {2: [1], 4: [3]})
pyrevit.coreutils.reverse_html(input_html)Reformat codified pyRevit output html string back to normal html.
pyRevit output window renders html content. But this means that < and > characters in outputs from python(e.g. <class at xxx>) will be treated as html tags. To avoid this, all <> characters that are defining html contentneed to be replaced with special phrases. pyRevit output later translates these phrases back in to < and >. Thatis how pyRevit ditinquishes between <> printed from python and <> that define html.
Parameters input_html (str) – input codified html string
Example
>>> prepare_html_str('&clt;p&cgt;Some text&clt;/p&cgt;')"<p>Some text</p>"
pyrevit.coreutils.run_process(proc, cwd=”)Run shell process silently.
Parameters
• proc (str) – process executive name
• cwd (str) – current working directory
Exmaple:
>>> run_process('notepad.exe', 'c:/')
pyrevit.coreutils.show_entry_in_explorer(entry_path)Show given entry in Windows Explorer.
Parameters entry_path (str) – directory or file path
pyrevit.coreutils.timestamp()Return timestamp for current time.
Returns timestamp in string format
Return type str
Example
>>> timestamp()'01003075032506808'
pyrevit.coreutils.touch(fname, times=None)Update the timestamp on the given file.
174 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
Parameters
• fname (str) – target file path
• times (int) – number of times to touch the file
pyrevit.coreutils.unc_to_dletter(unc_path)Convert UNC path into drive letter path.
Parameters unc_path (str) – UNC path
Returns drive letter path
Return type str
Example
>>> # assuming J: is mapped to //filestore/server/jdrive>>> unc_to_dletter('//filestore/server/jdrive/somefile.txt')'J:/somefile.txt'
pyrevit.coreutils.verify_directory(folder)Check if the folder exists and if not create the folder.
Parameters folder (str) – path of folder to verify
Returns path of verified folder, equals to provided folder
Return type str
Raises OSError on folder creation error.
17.1.1 Implementation
"""Misc Helper functions for pyRevit.
Example:>>> from pyrevit import coreutils>>> coreutils.cleanup_string('some string')
"""
import osimport os.path as opimport reimport astimport hashlibimport timeimport datetimeimport shutilimport randomimport statimport codecsimport mathfrom collections import defaultdict
#pylint: disable=E0401from pyrevit import HOST_APP, PyRevitExceptionfrom pyrevit.compat import safe_strtype
(continues on next page)
17.1. pyrevit.coreutils 175
pyRevit Documentation, Release 4.5
(continued from previous page)
from pyrevit import frameworkfrom pyrevit import api
# RE: https://github.com/eirannejad/pyRevit/issues/413# import uuidfrom System import Guid
#pylint: disable=W0703,C0302DEFAULT_SEPARATOR = ';'
# extracted from# https://www.fileformat.info/info/unicode/block/general_punctuation/images.htmUNICODE_NONPRINTABLE_CHARS = [
u'\u2000', u'\u2001', u'\u2002', u'\u2003', u'\u2004', u'\u2005', u'\u2006',u'\u2007', u'\u2008', u'\u2009', u'\u200A', u'\u200B', u'\u200C', u'\u200D',u'\u200E', u'\u200F',u'\u2028', u'\u2029', u'\u202A', u'\u202B', u'\u202C', u'\u202D', u'\u202E',u'\u202F',u'\u205F', u'\u2060',u'\u2066', u'\u2067', u'\u2068', u'\u2069', u'\u206A', u'\u206B', u'\u206C'u'\u206D', u'\u206E', u'\u206F']
class Timer:"""Timer class using python native time module.
Example:>>> timer = Timer()>>> timer.get_time()12
"""
def __init__(self):"""Initialize and Start Timer."""self.start = time.time()
def restart(self):"""Restart Timer."""self.start = time.time()
def get_time(self):"""Get Elapsed Time."""return time.time() - self.start
class ScriptFileParser:"""Parse python script to extract variables and docstrings.
Primarily designed to assist pyRevit in determining script configurationsbut can work for any python script.
Example:>>> finder = ScriptFileParser('/path/to/coreutils/__init__.py')>>> finder.docstring()
(continues on next page)
176 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
... "Misc Helper functions for pyRevit.">>> finder.extract_param('SomeValue', [])[]
"""
def __init__(self, file_address):"""Initialize and read provided python script.
Args:file_address (str): python script file path
"""self.file_addr = file_addresswith open(file_address, 'r') as source_file:
self.ast_tree = ast.parse(source_file.read())
def get_docstring(self):"""Get global docstring."""doc_str = ast.get_docstring(self.ast_tree)if doc_str:
return doc_str.decode('utf-8')return None
def extract_param(self, param_name, default_value=None):"""Find variable and extract its value.
Args:param_name (str): variable namedefault_value (any):
default value to be returned if variable does not exist
Returns:any: value of the variable or :obj:`None`
"""try:
for child in ast.iter_child_nodes(self.ast_tree):if hasattr(child, 'targets'):
for target in child.targets:if hasattr(target, 'id') and target.id == param_name:
param_value = ast.literal_eval(child.value)if isinstance(param_value, str):
param_value = param_value.decode('utf-8')return param_value
except Exception as err:raise PyRevitException('Error parsing parameter: {} '
'in script file for : {} | {}'.format(param_name, self.file_addr, err))
return default_value
class FileWatcher(object):"""Simple file version watcher.
This is a simple utility class to look for changes in a file based onits timestamp.
Example:(continues on next page)
17.1. pyrevit.coreutils 177
pyRevit Documentation, Release 4.5
(continued from previous page)
>>> watcher = FileWatcher('/path/to/file.ext')>>> watcher.has_changedTrue
"""
def __init__(self, filepath):"""Initialize and read timestamp of provided file.
Args:filepath (str): file path
"""self._cached_stamp = 0self._filepath = filepathself.update_tstamp()
def update_tstamp(self):"""Update the cached timestamp for later comparison."""self._cached_stamp = os.stat(self._filepath).st_mtime
@propertydef has_changed(self):
"""Compare current file timestamp to the cached timestamp."""return os.stat(self._filepath).st_mtime != self._cached_stamp
class SafeDict(dict):"""Dictionary that does not fail on any key.
This is a dictionary subclass to help with string formatting with unknownkey values.
Example:>>> string = '{target} {attr} is {color}.'>>> safedict = SafeDict({'target': 'Apple',... 'attr': 'Color'})>>> string.format(safedict) # will not fail with missing 'color' key'Apple Color is {color}.'
"""
def __missing__(self, key):return '{' + key + '}'
def get_all_subclasses(parent_classes):"""Return all subclasses of a python class.
Args:parent_classes (list): list of python classes
Returns:list: list of python subclasses
"""sub_classes = []# if super-class, get a list of sub-classes.# Otherwise use component_class to create objects.for parent_class in parent_classes:
try:(continues on next page)
178 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
derived_classes = parent_class.__subclasses__()if not derived_classes:
sub_classes.append(parent_class)else:
sub_classes.extend(derived_classes)except AttributeError:
sub_classes.append(parent_class)return sub_classes
def get_sub_folders(search_folder):"""Get a list of all subfolders directly inside provided folder.
Args:search_folder (str): folder path
Returns:list: list of subfolder names
"""sub_folders = []for sub_folder in os.listdir(search_folder):
if op.isdir(op.join(search_folder, sub_folder)):sub_folders.append(sub_folder)
return sub_folders
def verify_directory(folder):"""Check if the folder exists and if not create the folder.
Args:folder (str): path of folder to verify
Returns:str: path of verified folder, equals to provided folder
Raises:OSError on folder creation error.
"""if not op.exists(folder):
try:os.makedirs(folder)
except OSError as err:raise err
return folder
def join_strings(str_list, separator=DEFAULT_SEPARATOR):"""Join strings using provided separator.
Args:str_list (list): list of string valuesseparator (str): single separator character,
defaults to DEFAULT_SEPARATOR
Returns:str: joined string
"""(continues on next page)
17.1. pyrevit.coreutils 179
pyRevit Documentation, Release 4.5
(continued from previous page)
if str_list:return separator.join(str_list)
return ''
# character replacement list for cleaning up file namesSPECIAL_CHARS = {' ': '',
'~': '','!': 'EXCLAM','@': 'AT','#': 'SHARP','$': 'DOLLAR','%': 'PERCENT','^': '','&': 'AND','*': 'STAR','+': 'PLUS',';': '', ':': '', ',': '', '\"': '','{': '', '}': '', '[': '', ']': '', r'\(': '', r'\)': '','-': 'MINUS','=': 'EQUALS','<': '', '>': '','?': 'QMARK','.': 'DOT','_': 'UNDERS','|': 'VERT',r'\/': '', '\\': ''}
def cleanup_string(input_str):"""Replace special characters in string with another string.
This function was created to help cleanup pyRevit command unique names fromany special characters so C# class names can be created based on thoseunique names.
``coreutils.SPECIAL_CHARS`` is the conversion table for this function.
Args:input_str (str): input string to be cleaned
Example:>>> src_str = 'TEST@Some*<value>'>>> cleanup_string(src_str)"TESTATSomeSTARvalue"
"""# remove spaces and special characters from stringsfor char, repl in SPECIAL_CHARS.items():
input_str = input_str.replace(char, repl)
return input_str
def get_revit_instance_count():"""Return number of open host app instances.
Returns:(continues on next page)
180 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
int: number of open host app instances."""return len(list(framework.Process.GetProcessesByName(HOST_APP.proc_name)))
def run_process(proc, cwd=''):"""Run shell process silently.
Args:proc (str): process executive namecwd (str): current working directory
Exmaple:>>> run_process('notepad.exe', 'c:/')
"""import subprocessreturn subprocess.Popen(proc,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,cwd=cwd, shell=True)
def inspect_calling_scope_local_var(variable_name):"""Trace back the stack to find the variable in the caller local stack.
PyRevitLoader defines __revit__ in builtins and __window__ in locals.Thus, modules have access to __revit__ but not to __window__.This function is used to find __window__ in the caller stack.
Args:variable_name (str): variable name to look up in caller local scope
"""import inspect
frame = inspect.stack()[1][0]while variable_name not in frame.f_locals:
frame = frame.f_backif frame is None:
return Nonereturn frame.f_locals[variable_name]
def inspect_calling_scope_global_var(variable_name):"""Trace back the stack to find the variable in the caller global stack.
Args:variable_name (str): variable name to look up in caller global scope
"""import inspect
frame = inspect.stack()[1][0]while variable_name not in frame.f_globals:
frame = frame.f_backif frame is None:
return Nonereturn frame.f_locals[variable_name]
(continues on next page)
17.1. pyrevit.coreutils 181
pyRevit Documentation, Release 4.5
(continued from previous page)
def find_loaded_asm(asm_info, by_partial_name=False, by_location=False):"""Find loaded assembly based on name, partial name, or location.
Args:asm_info (str): name or location of the assemblyby_partial_name (bool): returns all assemblies that has the asm_infoby_location (bool): returns all assemblies matching location
Returns:list: List of all loaded assemblies matching the provided infoIf only one assembly has been found, it returns the assembly.:obj:`None` will be returned if assembly is not loaded.
"""loaded_asm_list = []for loaded_assembly in framework.AppDomain.CurrentDomain.GetAssemblies():
if by_partial_name:if asm_info.lower() in \
safe_strtype(loaded_assembly.GetName().Name).lower():loaded_asm_list.append(loaded_assembly)
elif by_location:try:
if op.normpath(loaded_assembly.Location) == \op.normpath(asm_info):
loaded_asm_list.append(loaded_assembly)except Exception:
continueelif asm_info.lower() == \
safe_strtype(loaded_assembly.GetName().Name).lower():loaded_asm_list.append(loaded_assembly)
return loaded_asm_list
def load_asm(asm_name):"""Load assembly by name into current domain.
Args:asm_name (str): assembly name
Returns:returns the loaded assembly, None if not loaded.
"""return framework.AppDomain.CurrentDomain.Load(asm_name)
def load_asm_file(asm_file):"""Load assembly by file into current domain.
Args:asm_file (str): assembly file path
Returns:returns the loaded assembly, None if not loaded.
"""try:
return framework.Assembly.LoadFrom(asm_file)except Exception:
(continues on next page)
182 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
return None
def find_type_by_name(assembly, type_name):"""Find type by name in assembly.
Args:assembly (:obj:`Assembly`): assembly to find the type intype_name (str): type name
Returns:returns the type if found.
Raises::obj:`PyRevitException` if type not found.
"""base_class = assembly.GetType(type_name)if base_class is not None:
return base_classelse:
raise PyRevitException('Can not find base class type: {}'.format(type_name))
def make_canonical_name(*args):"""Join arguments with dot creating a unique id.
Args:
*args: Variable length argument list of type :obj:`str`
Returns:str: dot separated unique name
Example:>>> make_canonical_name('somename', 'someid', 'txt')"somename.someid.txt"
"""return '.'.join(args)
def get_canonical_parts(canonical_string):"""Splots argument using dot, returning all composing parts.
Args:canonical_string(:obj:`str`): Source string e.g. "Config.SubConfig"
Returns:list[:obj:`str`]: list of composing parts
Example:>>> get_canonical_parts("Config.SubConfig")['Config', 'SubConfig']
"""return canonical_string.split('.')
def get_file_name(file_path):(continues on next page)
17.1. pyrevit.coreutils 183
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Return file basename of the given file.
Args:file_path (str): file path
"""return op.splitext(op.basename(file_path))[0]
def get_str_hash(source_str):"""Calculate hash value of given string.
Current implementation uses :func:`hashlib.md5` hash function.
Args:source_str (str): source str
Returns:str: hash value as string
"""return hashlib.md5(source_str.encode('utf-8', 'ignore')).hexdigest()
def calculate_dir_hash(dir_path, dir_filter, file_filter):r"""Create a unique hash to represent state of directory.
Args:dir_path (str): target directorydir_filter (str): exclude directories matching this regexfile_filter (str): exclude files matching this regex
Returns:str: hash value as string
Example:>>> calculate_dir_hash(source_path, '\.extension', '\.json')"1a885a0cae99f53d6088b9f7cee3bf4d"
"""mtime_sum = 0for root, dirs, files in os.walk(dir_path): #pylint: disable=W0612
if re.search(dir_filter, op.basename(root), flags=re.IGNORECASE):mtime_sum += op.getmtime(root)for filename in files:
if re.search(file_filter, filename, flags=re.IGNORECASE):modtime = op.getmtime(op.join(root, filename))mtime_sum += modtime
return get_str_hash(safe_strtype(mtime_sum))
def prepare_html_str(input_string):"""Reformat html string and prepare for pyRevit output window.
pyRevit output window renders html content. But this means that < and >characters in outputs from python (e.g. <class at xxx>) will be treatedas html tags. To avoid this, all <> characters that are defininghtml content need to be replaced with special phrases. pyRevit outputlater translates these phrases back in to < and >. That is how pyRevitditinquishes between <> printed from python and <> that define html.
(continues on next page)
184 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
Args:input_string (str): input html string
Example:>>> prepare_html_str('<p>Some text</p>')"&clt;p&cgt;Some text&clt;/p&cgt;"
"""return input_string.replace('<', '&clt;').replace('>', '&cgt;')
def reverse_html(input_html):"""Reformat codified pyRevit output html string back to normal html.
pyRevit output window renders html content. But this means that < and >characters in outputs from python (e.g. <class at xxx>) will be treatedas html tags. To avoid this, all <> characters that are defininghtml content need to be replaced with special phrases. pyRevit outputlater translates these phrases back in to < and >. That is how pyRevitditinquishes between <> printed from python and <> that define html.
Args:input_html (str): input codified html string
Example:>>> prepare_html_str('&clt;p&cgt;Some text&clt;/p&cgt;')"<p>Some text</p>"
"""return input_html.replace('&clt;', '<').replace('&cgt;', '>')
# def check_internet_connection():# client = framework.WebClient()# try:# client.OpenRead("http://www.google.com")# return True# except:# return False#
# def check_internet_connection():# import urllib2## def internet_on():# try:# urllib2.urlopen('http://216.58.192.142', timeout=1)# return True# except urllib2.URLError as err:# return False
def can_access_url(url_to_open, timeout=1000):"""Check if url is accessible within timeout.
Args:url_to_open (str): url to check access for
(continues on next page)
17.1. pyrevit.coreutils 185
pyRevit Documentation, Release 4.5
(continued from previous page)
timeout (int): timeout in milliseconds
Returns:bool: true if accessible
"""try:
client = framework.WebRequest.Create(url_to_open)client.Method = "HEAD"client.Timeout = timeoutclient.Proxy = framework.WebProxy.GetDefaultProxy()response = client.GetResponse()response.GetResponseStream()return True
except Exception:return False
def check_internet_connection(timeout=1000):"""Check if internet connection is available.
Pings a few well-known websites to check if internet connection is present.
Args:timeout (int): timeout in milliseconds
Returns:url if internet connection is present, None if no internet.
"""solid_urls = ["http://google.com/",
"http://github.com/","http://bitbucket.com/","http://airtable.com/","http://todoist.com/","http://stackoverflow.com/","http://twitter.com/","http://youtube.com/"]
random.shuffle(solid_urls)for url in solid_urls:
if can_access_url(url, timeout):return url
return None
def touch(fname, times=None):"""Update the timestamp on the given file.
Args:fname (str): target file pathtimes (int): number of times to touch the file
"""with open(fname, 'a'):
os.utime(fname, times)
def read_source_file(source_file_path):"""Read text file and return contents.
(continues on next page)
186 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
Args:source_file_path (str): target file path
Returns:str: file contents
Raises::obj:`PyRevitException` on read error
"""try:
with open(source_file_path, 'r') as code_file:return code_file.read()
except Exception as read_err:raise PyRevitException('Error reading source file: {} | {}'
.format(source_file_path, read_err))
def create_ext_command_attrs():"""Create dotnet attributes for Revit extenrnal commads.
This method is used in creating custom dotnet types for pyRevit commandsand compiling them into a DLL assembly. Current implementation sets``RegenerationOption.Manual`` and ``TransactionMode.Manual``
Returns:list: list of :obj:`CustomAttributeBuilder` for:obj:`RegenerationOption` and :obj:`TransactionMode` attributes.
"""regen_const_info = \
framework.clr.GetClrType(api.Attributes.RegenerationAttribute) \.GetConstructor(
framework.Array[framework.Type]((api.Attributes.RegenerationOption,)))
regen_attr_builder = \framework.CustomAttributeBuilder(
regen_const_info,framework.Array[object](
(api.Attributes.RegenerationOption.Manual,)))
# add TransactionAttribute to framework.Typetrans_constructor_info = \
framework.clr.GetClrType(api.Attributes.TransactionAttribute) \.GetConstructor(
framework.Array[framework.Type]((api.Attributes.TransactionMode,))
)
trans_attrib_builder = \framework.CustomAttributeBuilder(
trans_constructor_info,framework.Array[object](
(api.Attributes.TransactionMode.Manual,)(continues on next page)
17.1. pyrevit.coreutils 187
pyRevit Documentation, Release 4.5
(continued from previous page)
))
return [regen_attr_builder, trans_attrib_builder]
def create_type(modulebuilder,type_class, class_name, custom_attr_list, *args):
"""Create a dotnet type for a pyRevit command.
See ``baseclasses.cs`` code for the template pyRevit command dotnet typeand its constructor default arguments that must be provided here.
Args:modulebuilder (:obj:`ModuleBuilder`): dotnet module buildertype_class (type): source dotnet type for the commandclass_name (str): name for the new typecustom_attr_list (:obj:`list`): list of dotnet attributes for the type
*args: list of arguments to be used with type constructor
Returns:type: returns created dotnet type
Example:>>> asm_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(... win_asm_name, AssemblyBuilderAccess.RunAndSave, filepath... )>>> module_builder = asm_builder.DefineDynamicModule(... ext_asm_file_name, ext_asm_full_file_name... )>>> create_type(... module_builder,... PyRevitCommand,... "PyRevitSomeCommandUniqueName",... coreutils.create_ext_command_attrs(),... [scriptpath, atlscriptpath, searchpath, helpurl, name,... bundle, extension, uniquename, False, False])<type PyRevitSomeCommandUniqueName>
"""# create type buildertype_builder = \
modulebuilder.DefineType(class_name,framework.TypeAttributes.Class | framework.TypeAttributes.Public,type_class)
for custom_attr in custom_attr_list:type_builder.SetCustomAttribute(custom_attr)
# prepare a list of input param types to find the matching constructortype_list = []param_list = []for param in args:
if isinstance(param, str) \or isinstance(param, int):
type_list.append(type(param))(continues on next page)
188 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
param_list.append(param)
# call base constructorconstructor = \
type_class.GetConstructor(framework.Array[framework.Type](type_list))# create class constructor builderconst_builder = \
type_builder.DefineConstructor(framework.MethodAttributes.Public,framework.CallingConventions.Standard,framework.Array[framework.Type](()))
# add constructor parameters to stackgen = const_builder.GetILGenerator()gen.Emit(framework.OpCodes.Ldarg_0) # Load "this" onto eval stack
# add constructor input params to the stackfor param_type, param in zip(type_list, param_list):
if param_type == str:gen.Emit(framework.OpCodes.Ldstr, param)
elif param_type == int:gen.Emit(framework.OpCodes.Ldc_I4, param)
# call base constructor (consumes "this" and the created stack)gen.Emit(framework.OpCodes.Call, constructor)# Fill some space - this is how it is generated for equivalent C# codegen.Emit(framework.OpCodes.Nop)gen.Emit(framework.OpCodes.Nop)gen.Emit(framework.OpCodes.Nop)gen.Emit(framework.OpCodes.Ret)return type_builder.CreateType()
def open_folder_in_explorer(folder_path):"""Open given folder in Windows Explorer.
Args:folder_path (str): directory path
"""import subprocesssubprocess.Popen(r'explorer /open,"{}"'
.format(os.path.normpath(folder_path)))
def show_entry_in_explorer(entry_path):"""Show given entry in Windows Explorer.
Args:entry_path (str): directory or file path
"""import subprocesssubprocess.Popen(r'explorer /select,"{}"'
.format(os.path.normpath(entry_path)))
def fully_remove_dir(dir_path):"""Remove directory recursively.
Args:(continues on next page)
17.1. pyrevit.coreutils 189
pyRevit Documentation, Release 4.5
(continued from previous page)
dir_path (str): directory path"""def del_rw(action, name, exc): #pylint: disable=W0613
os.chmod(name, stat.S_IWRITE)os.remove(name)
shutil.rmtree(dir_path, onerror=del_rw)
def cleanup_filename(file_name):"""Cleanup file name from special characters.
Args:file_name (str): file name
Returns:str: cleaned up file name
Example:>>> cleanup_filename('Myfile-(3).txt')"Myfile3.txt"
"""return re.sub(r'[^\w_.)( -]', '', file_name)
def _inc_or_dec_string(str_id, shift):"""Increment or decrement identifier.
Args:str_id (str): identifier e.g. A310ashift (int): number of steps to change the identifier
Returns:str: modified identifier
Example:>>> _inc_or_dec_string('A319z')'A320a'
"""next_str = ""index = len(str_id) - 1carry = shift
while index >= 0:if str_id[index].isalpha():
if str_id[index].islower():reset_a = 'a'reset_z = 'z'
else:reset_a = 'A'reset_z = 'Z'
curr_digit = (ord(str_id[index]) + carry)if curr_digit < ord(reset_a):
curr_digit = ord(reset_z) - ((ord(reset_a) - curr_digit) - 1)carry = shift
elif curr_digit > ord(reset_z):(continues on next page)
190 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
curr_digit = ord(reset_a) + ((curr_digit - ord(reset_z)) - 1)carry = shift
else:carry = 0
curr_digit = chr(curr_digit)next_str += curr_digit
elif str_id[index].isdigit():
curr_digit = int(str_id[index]) + carryif curr_digit > 9:
curr_digit = 0 + ((curr_digit - 9)-1)carry = shift
elif curr_digit < 0:curr_digit = 9 - ((0 - curr_digit)-1)carry = shift
else:carry = 0
next_str += safe_strtype(curr_digit)
else:next_str += str_id[index]
index -= 1
return next_str[::-1]
def increment_str(input_str, step):"""Incremenet identifier.
Args:input_str (str): identifier e.g. A310astep (int): number of steps to change the identifier
Returns:str: modified identifier
Example:>>> increment_str('A319z')'A320a'
"""return _inc_or_dec_string(input_str, abs(step))
def decrement_str(input_str, step):"""Decrement identifier.
Args:input_str (str): identifier e.g. A310astep (int): number of steps to change the identifier
Returns:str: modified identifier
Example:(continues on next page)
17.1. pyrevit.coreutils 191
pyRevit Documentation, Release 4.5
(continued from previous page)
>>> decrement_str('A310a')'A309z'
"""return _inc_or_dec_string(input_str, -abs(step))
def filter_null_items(src_list):"""Remove None items in the given list.
Args:src_list (:obj:`list`): list of any items
Returns::obj:`list`: cleaned list
"""return list(filter(bool, src_list))
def reverse_dict(input_dict):"""Reverse the key, value pairs.
Args:input_dict (:obj:`dict`): source ordered dict
Returns::obj:`defaultdict`: reversed dictionary
Example:>>> reverse_dict({1: 2, 3: 4})defaultdict(<type 'list'>, {2: [1], 4: [3]})
"""output_dict = defaultdict(list)for key, value in input_dict.items():
output_dict[value].append(key)return output_dict
def timestamp():"""Return timestamp for current time.
Returns:str: timestamp in string format
Example:>>> timestamp()'01003075032506808'
"""return datetime.datetime.now().strftime("%m%j%H%M%S%f")
def current_time():"""Return formatted current time.
Current implementation uses %H:%M:%S to format time.
Returns:str: formatted current time.
(continues on next page)
192 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
Example:>>> current_time()'07:50:53'
"""return datetime.datetime.now().strftime("%H:%M:%S")
def current_date():"""Return formatted current date.
Current implementation uses %Y-%m-%d to format date.
Returns:str: formatted current date.
Example:>>> current_date()'2018-01-03'
"""return datetime.datetime.now().strftime("%Y-%m-%d")
def is_blank(input_string):"""Check if input string is blank (multiple white spaces is blank).
Args:input_string (str): input string
Returns:bool: True if string is blank
Example:>>> is_blank(' ')True
"""if input_string and input_string.strip():
return Falsereturn True
def is_url_valid(url_string):"""Check if given URL is in valid format.
Args:url_string (str): URL string
Returns:bool: True if URL is in valid format
Example:>>> is_url_valid('https://www.google.com')True
"""regex = re.compile(
r'^(?:http|ftp)s?://' # http:// or https://r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+'
(continues on next page)
17.1. pyrevit.coreutils 193
pyRevit Documentation, Release 4.5
(continued from previous page)
r'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain...r'localhost|' # localhost...r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ipr'(?::\d+)?' # optional portr'(?:/?|[/?]\S+)$', re.IGNORECASE)
return regex.match(url_string)
def reformat_string(orig_str, orig_format, new_format):"""Reformat a string into a new format.
Extracts information from a string based on a given pattern,and recreates a new string based on the given new pattern.
Args:orig_str (str): Original string to be reformattedorig_format (str): Pattern of the original str (data to be extracted)new_format (str): New pattern (how to recompose the data)
Returns:str: Reformatted string
Example:>>> reformat_string('150 - FLOOR/CEILING - WD - 1 HR - FLOOR ASSEMBLY',
'{section} - {loc} - {mat} - {rating} - {name}','{section}:{mat}:{rating} - {name} ({loc})'))
'150:WD:1 HR - FLOOR ASSEMBLY (FLOOR/CEILING)'"""# find the tagstag_extractor = re.compile('{(.+?)}')tags = tag_extractor.findall(orig_format)
# replace the tags with regex patterns# to create a regex pattern that finds valuestag_replacer = re.compile('{.+?}')value_extractor_pattern = tag_replacer.sub('(.+)', orig_format)# find all valuesvalue_extractor = re.compile(value_extractor_pattern)match = value_extractor.match(orig_str)values = match.groups()
# create a dictionary of tags and valuesreformat_dict = {}for key, value in zip(tags, values):
reformat_dict[key] = value
# use dictionary to reformat the string into newreturn new_format.format(**reformat_dict)
def get_mapped_drives_dict():"""Return a dictionary of currently mapped network drives."""searcher = framework.ManagementObjectSearcher(
"root\\CIMV2","SELECT * FROM Win32_MappedLogicalDisk")
(continues on next page)
194 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
return {x['DeviceID']: x['ProviderName'] for x in searcher.Get()}
def dletter_to_unc(dletter_path):"""Convert drive letter path into UNC path of that drive.
Args:dletter_path (str): drive letter path
Returns:str: UNC path
Example:>>> # assuming J: is mapped to //filestore/server/jdrive>>> dletter_to_unc('J:/somefile.txt')'//filestore/server/jdrive/somefile.txt'
"""drives = get_mapped_drives_dict()dletter = dletter_path[:2]for mapped_drive, server_path in drives.items():
if dletter.lower() == mapped_drive.lower():return dletter_path.replace(dletter, server_path)
def unc_to_dletter(unc_path):"""Convert UNC path into drive letter path.
Args:unc_path (str): UNC path
Returns:str: drive letter path
Example:>>> # assuming J: is mapped to //filestore/server/jdrive>>> unc_to_dletter('//filestore/server/jdrive/somefile.txt')'J:/somefile.txt'
"""drives = get_mapped_drives_dict()for mapped_drive, server_path in drives.items():
if server_path in unc_path:return unc_path.replace(server_path, mapped_drive)
def random_color():"""Return a random color channel value (between 0 and 255)."""return random.randint(0, 255)
def random_alpha():"""Return a random alpha value (between 0 and 1.00)."""return round(random.random(), 2)
def random_hex_color():"""Return a random color in hex format.
(continues on next page)
17.1. pyrevit.coreutils 195
pyRevit Documentation, Release 4.5
(continued from previous page)
Example:>>> random_hex_color()'#FF0000'
"""return '#%02X%02X%02X' % (random_color(),
random_color(),random_color())
def random_rgb_color():"""Return a random color in rgb format.
Example:>>> random_rgb_color()'rgb(255, 0, 0)'
"""return 'rgb(%d, %d, %d)' % (random_color(),
random_color(),random_color())
def random_rgba_color():"""Return a random color in rgba format.
Example:>>> random_rgba_color()'rgba(255, 0, 0, 0.5)'
"""return 'rgba(%d, %d, %d, %.2f)' % (random_color(),
random_color(),random_color(),random_alpha())
def extract_range(formatted_str, max_range=500):"""Extract range from formatted string.
String must be formatted as belowA103 No rangeA103-A106 A103 to A106A103:A106 A103 to A106A103,A105a A103 and A105aA103;A105a A103 and A105a
Args:formatted_str (str): string specifying range
Returns:list: list of names in the specified range
Example:>>> exract_range('A103:A106')['A103', 'A104', 'A105', 'A106']>>> exract_range('S203-S206')['S203', 'S204', 'S205', 'S206']>>> exract_range('M00A,M00B')
(continues on next page)
196 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
['M00A', 'M00B']"""for rchar, rchartype in {'::': 'range', '--': 'range',
',': 'list', ';': 'list'}.items():if rchar in formatted_str:
if rchartype == 'range' \and formatted_str.count(rchar) == 1:
items = []start, end = formatted_str.split(rchar)assert len(start) == len(end), \
'Range start and end must have same length'items.append(start)item = increment_str(start, 1)safe_counter = 0while item != end:
items.append(item)item = increment_str(item, 1)safe_counter += 1assert safe_counter < max_range, 'Max range reached.'
items.append(end)return items
elif rchartype == 'list':return [x.strip() for x in formatted_str.split(rchar)]
return [formatted_str]
def correct_revittxt_encoding(filename):"""Convert encoding of text file generated by Revit to UTF-8.
Args:filename (str): file path
"""with codecs.open(filename, 'r', 'utf_16_le') as oldf:
fcontent = oldf.readlines()with codecs.open(filename, 'w', 'utf-8') as newf:
newf.writelines(fcontent)
def check_revittxt_encoding(filename):"""Check if given file is in UTF-16 (UCS-2 LE) encoding.
Args:filename (str): file path
"""with open(filename, 'rb') as rtfile:
return rtfile.read()[:2] == codecs.BOM_UTF16
def check_utf8bom_encoding(filename):"""Check if given file is in UTF-8 encoding.
Args:filename (str): file path
"""with open(filename, 'rb') as rtfile:
return rtfile.read()[:3] == codecs.BOM_UTF8
(continues on next page)
17.1. pyrevit.coreutils 197
pyRevit Documentation, Release 4.5
(continued from previous page)
def has_nonprintable(input_str):"""Check input string for non-printable characters.
Args:input_str (str): input string
Returns:bool: True if contains non-printable characters
"""return any([x in input_str for x in UNICODE_NONPRINTABLE_CHARS])
def get_enum_values(enum_type):"""Returns enum values."""return framework.Enum.GetValues(enum_type)
def get_enum_value(enum_type, value_string):"""Return enum value matching given value string (case insensitive)"""for ftype in get_enum_values(enum_type):
if str(ftype).lower() == value_string.lower():return ftype
def get_enum_none(enum_type):"""Returns the None value in given Enum."""for val in get_enum_values(enum_type):
if str(val) == 'None':return val
def extract_guid(source_str):"""Extract GUID number from a string."""guid_match = re.match(".*([0-9A-Fa-f]{8}"
"[-][0-9A-Fa-f]{4}""[-][0-9A-Fa-f]{4}""[-][0-9A-Fa-f]{4}""[-][0-9A-Fa-f]{12}).*", source_str)
if guid_match:return guid_match.groups()[0]
def format_hex_rgb(rgb_value):"""Formats rgb value as #RGB value string."""if isinstance(rgb_value, str):
if not rgb_value.startswith('#'):return '#%s' % rgb_value
else:return rgb_value
elif isinstance(rgb_value, int):return '#%x' % rgb_value
def new_uuid():"""Create a new UUID (using dotnet Guid.NewGuid)"""# RE: https://github.com/eirannejad/pyRevit/issues/413
(continues on next page)
198 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
# return uuid.uuid1()return str(Guid.NewGuid())
def is_box_visible_on_screens(left, top, width, height):"""Check if given box is visible on any screen."""bounds = \
framework.Drawing.Rectangle(framework.Convert.ToInt32(0 if math.isnan(left) else left),framework.Convert.ToInt32(0 if math.isnan(top) else top),framework.Convert.ToInt32(0 if math.isnan(width) else width),framework.Convert.ToInt32(0 if math.isnan(height) else height))
for scr in framework.Forms.Screen.AllScreens:if bounds.IntersectsWith(scr.Bounds):
return Truereturn False
def fuzzy_search_ratio(target_string, sfilter):"""Match target string against the filter and return a match ratio."""tstring = target_string# 100 for identical matchesif sfilter == tstring:
return 100
# 98 to 99 reserved (2 scores)
# 97 for identical non-case-sensitive matcheslower_tstring = tstring.lower()lower_sfilter_str = sfilter.lower()if lower_sfilter_str == lower_tstring:
return 97
# 95 to 96 reserved (2 scores)
# 93 to 94 for inclusion matchesif sfilter in tstring:
return 94if lower_sfilter_str in lower_tstring:
return 93
# 91 to 92 reserved (2 scores)
## 80 to 90 for parts matcheststring_parts = tstring.split()sfilter_parts = sfilter.split()if all(x in tstring_parts for x in sfilter_parts):
return 90
# 88 to 89 reserved (2 scores)
lower_tstring_parts = [x.lower() for x in tstring_parts]lower_sfilter_parts = [x.lower() for x in sfilter_parts]if all(x in lower_tstring_parts for x in lower_sfilter_parts):
return 87
(continues on next page)
17.1. pyrevit.coreutils 199
pyRevit Documentation, Release 4.5
(continued from previous page)
# 85 to 86 reserved (2 scores)
if all(x in tstring for x in sfilter_parts):return 84
# 82 to 83 reserved (2 scores)
if all(x in lower_tstring for x in lower_sfilter_parts):return 81
# 80 reserved
return 0
17.2 pyrevit.coreutils.envvars
pyRevit managed environment variables framework.
pyRevit provides the environment variables framework to the pyRevit core and all pyRevit tools so they can storearbitary data withing the running host session and share small data quickly between script runs.
Some settings needs to be set for the current session and might need to affect the behaviour of all individual scriptsinside the extensions. (e.g. If user activates the DEBUG mode, all scripts should follow and log the debug entries.)The information is saved using AppDomain.GetData and SetData in a dictionary parameter. The dictionary isused to minimize the addition of named parameters to the AppDomain. The dictionary then includes all the internalparameters and their associated value. This way each script does not need to read the usersettings data which reducesio and saves time.
pyRevit uses environment variables extensively at its core and making changes to the core environment variables(starting with PYREVIT_) through scripts is strongly prohibited.
Example
>>> from pyrevit.coreutils import envvars>>> envvars.set_pyrevit_env_var('MY_SCRIPT_STATUS', True)>>> envvars.set_pyrevit_env_var('MY_SCRIPT_CONFIG', {'someconfig': True})
Then another script or same script when executed later within the same session can query the shared environmentvariable:
>>> envvars.get_pyrevit_env_vars('MY_SCRIPT_STATUS')True>>> envvars.get_pyrevit_env_vars('MY_SCRIPT_CONFIG'){'someconfig': True}
pyrevit.coreutils.envvars.get_pyrevit_env_var(param_name)Get value of a parameter shared between all scripts.
Parameters param_name (str) – name of environment variable
Returns any object stored as the environment variable value
Return type object
200 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
pyrevit.coreutils.envvars.get_pyrevit_env_vars()Get the root dictionary, holding all environment variables.
pyrevit.coreutils.envvars.set_pyrevit_env_var(param_name, param_value)Set value of a parameter shared between all scripts.
Parameters
• param_name (str) – name of environment variable
• param_value (object) – any python object
17.2.1 Implementation
"""pyRevit managed environment variables framework.
pyRevit provides the environment variables framework to the pyRevit coreand all pyRevit tools so they can store arbitary data withing the running hostsession and share small data quickly between script runs.
Some settings needs to be set for the current session and might need to affectthe behaviour of all individual scripts inside the extensions.(e.g. If user activates the ``DEBUG`` mode, all scripts should follow and logthe debug entries.) The information is saved using ``AppDomain.GetData`` and``SetData`` in a dictionary parameter. The dictionary is used to minimize theaddition of named parameters to the AppDomain. The dictionary then includesall the internal parameters and their associated value. This way each scriptdoes not need to read the usersettings data which reduces io and saves time.
pyRevit uses environment variables extensively at its core and making changesto the core environment variables (starting with ``PYREVIT_``) throughscripts is strongly prohibited.
Example:>>> from pyrevit.coreutils import envvars>>> envvars.set_pyrevit_env_var('MY_SCRIPT_STATUS', True)>>> envvars.set_pyrevit_env_var('MY_SCRIPT_CONFIG', {'someconfig': True})
Then another script or same script when executed later within the samesession can query the shared environment variable:
>>> envvars.get_pyrevit_env_vars('MY_SCRIPT_STATUS')True>>> envvars.get_pyrevit_env_vars('MY_SCRIPT_CONFIG'){'someconfig': True}
"""
from pyrevit import PYREVIT_ADDON_NAMEfrom pyrevit.framework import AppDomain
# root env var dictionary key.# must be the same in this file and pyrevit/loader/basetypes/_config.cs# DomainStorageKeys.pyRevitEnvVarsDictKeyPYREVIT_ENV_VAR_DICT_NAME = 'pyRevitEnvVarsDict'PYREVIT_ENVVAR_PREFIX = PYREVIT_ADDON_NAME.upper()
(continues on next page)
17.2. pyrevit.coreutils.envvars 201
pyRevit Documentation, Release 4.5
(continued from previous page)
def get_pyrevit_env_vars():"""Get the root dictionary, holding all environment variables."""return AppDomain.CurrentDomain.GetData(PYREVIT_ENV_VAR_DICT_NAME)
def get_pyrevit_env_var(param_name):"""Get value of a parameter shared between all scripts.
Args:param_name (str): name of environment variable
Returns:object: any object stored as the environment variable value
"""# This function returns None if it can not find the parameter.# Thus value of None should not be used for params
data_dict = AppDomain.CurrentDomain.GetData(PYREVIT_ENV_VAR_DICT_NAME)
if data_dict:try:
return data_dict[param_name]except KeyError:
return Noneelse:
return None
def set_pyrevit_env_var(param_name, param_value):"""Set value of a parameter shared between all scripts.
Args:param_name (str): name of environment variableparam_value (object): any python object
"""# Get function returns None if it can not find the parameter.# Thus value of None should not be used for paramsdata_dict = AppDomain.CurrentDomain.GetData(PYREVIT_ENV_VAR_DICT_NAME)
if data_dict:data_dict[param_name] = param_value
else:data_dict = {param_name: param_value}
AppDomain.CurrentDomain.SetData(PYREVIT_ENV_VAR_DICT_NAME, data_dict)
17.3 pyrevit.coreutils.appdata
Utility functions for creating data files within pyRevit environment.
Most times, scripts need to save some data to shared between different scripts that work on a similar topic or betweenscript executions. This module provides the necessary and consistent mechanism for creating and maintaining suchfiles.
202 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit.coreutils import appdata>>> appdata.list_data_files()
pyrevit.coreutils.appdata.cleanup_appdata_folder()Cleanup appdata folder of all temporary appdata files.
pyrevit.coreutils.appdata.garbage_data_file(file_path)Mark and remove the given appdata file.
Current implementation removes the file immediately.
Parameters file_path (str) – path to the target file
pyrevit.coreutils.appdata.get_data_file(file_id, file_ext, name_only=False)Get path to file that will not be cleaned up at Revit load.
e.g pyrevit_2016_eirannejad_file_id.file_ext
Parameters
• file_id (str) – Unique identifier for the file
• file_ext (str) – File extension
• name_only (bool) – If true, function returns file name only
Returns File name or full file path (depending on name_only)
Return type str
pyrevit.coreutils.appdata.get_instance_data_file(file_id, file_ext=’tmp’,name_only=False)
Get path to file that should be used by current instance only.
These data files will be cleaned up at Revit restart. e.g pyrevit_2016_eirannejad_2353_file_id.file_ext
Parameters
• file_id (str) – Unique identifier for the file
• file_ext (str) – File extension
• name_only (bool) – If true, function returns file name only
Returns File name or full file path (depending on name_only)
Return type str
pyrevit.coreutils.appdata.get_universal_data_file(file_id, file_ext, name_only=False)Get path to file that is shared between all host versions.
These data files are not cleaned up at Revit restart. e.g pyrevit_eirannejad_file_id.file_ext
Parameters
• file_id (str) – Unique identifier for the file
• file_ext (str) – File extension
• name_only (bool) – If true, function returns file name only
Returns File name or full file path (depending on name_only)
Return type str
17.3. pyrevit.coreutils.appdata 203
pyRevit Documentation, Release 4.5
pyrevit.coreutils.appdata.is_data_file_available(file_id, file_ext)Check if given file is available within appdata directory.
Parameters
• file_id (str) – data file id
• file_ext (str) – file extension
Returns file path if file is available
Return type str
pyrevit.coreutils.appdata.is_file_available(file_name, file_ext, universal=False)Check if given file is available within appdata directory.
Parameters
• file_name (str) – file name
• file_ext (str) – file extension
• universal (bool) – Check against universal data files
Returns file path if file is available
Return type str
pyrevit.coreutils.appdata.is_pyrevit_data_file(file_name)Check if given file is a pyRevit data file.
Parameters file_name (str) – file name
Returns True if file is a pyRevit data file
Return type bool
pyrevit.coreutils.appdata.list_data_files(file_ext, universal=False)List all data files with given extension.
Parameters
• file_ext (str) – file extension
• universal (bool) – Check against universal data files
Returns list of files
Return type list
pyrevit.coreutils.appdata.list_session_data_files(file_ext)List all data files associated with current session.
Parameters file_ext (str) – data files with this extension will be listed only.
Returns list of data files
Return type list
17.3.1 Implementation
"""Utility functions for creating data files within pyRevit environment.
Most times, scripts need to save some data to shared between different scriptsthat work on a similar topic or between script executions. This module provides
(continues on next page)
204 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
the necessary and consistent mechanism for creating and maintaining such files.
Example:>>> from pyrevit.coreutils import appdata>>> appdata.list_data_files()
"""
import osimport os.path as opimport re
import pyrevitfrom pyrevit import EXEC_PARAMS, HOST_APPfrom pyrevit.coreutils import make_canonical_namefrom pyrevit.coreutils import loggerfrom pyrevit.labs import TargetApps
#pylint: disable=W0703,C0302mlogger = logger.get_logger(__name__) #pylint: disable=C0103
TEMP_FILE_EXT = 'tmp'
def _remove_app_file(file_path):try:
os.remove(file_path)except Exception as osremove_err:
mlogger.error('Error file cleanup on: %s | %s', file_path, osremove_err)
def _list_app_files(prefix, file_ext, universal=False):requested_files = []
if universal:appdata_folder = pyrevit.PYREVIT_APP_DIR
else:appdata_folder = pyrevit.PYREVIT_VERSION_APP_DIR
for appdata_file in os.listdir(appdata_folder):if appdata_file.startswith(prefix) and appdata_file.endswith(file_ext):
requested_files.append(op.join(appdata_folder, appdata_file))
return requested_files
def _get_app_file(file_id, file_ext,filename_only=False, stamped=False, universal=False):
appdata_folder = pyrevit.PYREVIT_VERSION_APP_DIRfile_prefix = pyrevit.PYREVIT_FILE_PREFIX
if stamped:file_prefix = pyrevit.PYREVIT_FILE_PREFIX_STAMPED
elif universal:appdata_folder = pyrevit.PYREVIT_APP_DIRfile_prefix = pyrevit.PYREVIT_FILE_PREFIX_UNIVERSAL
(continues on next page)
17.3. pyrevit.coreutils.appdata 205
pyRevit Documentation, Release 4.5
(continued from previous page)
full_filename = '{}{}.{}'.format(file_prefix, file_id, file_ext)
if filename_only:return full_filename
else:return op.join(appdata_folder, full_filename)
def _match_file(file_name):match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_STAMPED_USER_REGEX,
string=file_name)if match:
return match.groupdict()
# e.g. pyRevit_2018_14422_match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_STAMPED_REGEX,
string=file_name)if match:
return match.groupdict()
# e.g. pyRevit_2018_eirannejad_match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_USER_REGEX,
string=file_name)if match:
return match.groupdict()
# e.g. pyRevit_2018_match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_REGEX,
string=file_name)if match:
return match.groupdict()
# e.g. pyRevit_eirannejad_match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_UNIVERSAL_USER_REGEX,
string=file_name)if match:
return match.groupdict()
# e.g. pyRevit_match = re.match(pattern=pyrevit.PYREVIT_FILE_PREFIX_UNIVERSAL_REGEX,
string=file_name)if match:
return match.groupdict()
return {}
def get_universal_data_file(file_id, file_ext, name_only=False):"""Get path to file that is shared between all host versions.
These data files are not cleaned up at Revit restart.e.g pyrevit_eirannejad_file_id.file_ext
Args:file_id (str): Unique identifier for the filefile_ext (str): File extension
(continues on next page)
206 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
name_only (bool): If true, function returns file name only
Returns:str: File name or full file path (depending on name_only)
"""return _get_app_file(file_id, file_ext,
filename_only=name_only, universal=True)
def get_data_file(file_id, file_ext, name_only=False):"""Get path to file that will not be cleaned up at Revit load.
e.g pyrevit_2016_eirannejad_file_id.file_ext
Args:file_id (str): Unique identifier for the filefile_ext (str): File extensionname_only (bool): If true, function returns file name only
Returns:str: File name or full file path (depending on name_only)
"""return _get_app_file(file_id, file_ext, filename_only=name_only)
def get_instance_data_file(file_id, file_ext=TEMP_FILE_EXT, name_only=False):"""Get path to file that should be used by current instance only.
These data files will be cleaned up at Revit restart.e.g pyrevit_2016_eirannejad_2353_file_id.file_ext
Args:file_id (str): Unique identifier for the filefile_ext (str): File extensionname_only (bool): If true, function returns file name only
Returns:str: File name or full file path (depending on name_only)
"""return _get_app_file(file_id, file_ext,
filename_only=name_only, stamped=True)
def is_pyrevit_data_file(file_name):"""Check if given file is a pyRevit data file.
Args:file_name (str): file name
Returns:bool: True if file is a pyRevit data file
"""return pyrevit.PYREVIT_FILE_PREFIX in file_name
def is_file_available(file_name, file_ext, universal=False):"""Check if given file is available within appdata directory.
(continues on next page)
17.3. pyrevit.coreutils.appdata 207
pyRevit Documentation, Release 4.5
(continued from previous page)
Args:file_name (str): file namefile_ext (str): file extensionuniversal (bool): Check against universal data files
Returns:str: file path if file is available
"""if universal:
full_filename = op.join(pyrevit.PYREVIT_APP_DIR,make_canonical_name(file_name, file_ext))
else:full_filename = op.join(pyrevit.PYREVIT_VERSION_APP_DIR,
make_canonical_name(file_name, file_ext))if op.exists(full_filename):
return full_filenameelse:
return False
def is_data_file_available(file_id, file_ext):"""Check if given file is available within appdata directory.
Args:file_id (str): data file idfile_ext (str): file extension
Returns:str: file path if file is available
"""full_filename = _get_app_file(file_id, file_ext)if op.exists(full_filename):
return full_filenameelse:
return False
def list_data_files(file_ext, universal=False):"""List all data files with given extension.
Args:file_ext (str): file extensionuniversal (bool): Check against universal data files
Returns::obj:`list`: list of files
"""return _list_app_files(pyrevit.PYREVIT_FILE_PREFIX, file_ext, universal=universal)
def list_session_data_files(file_ext):"""List all data files associated with current session.
Args:file_ext (str): data files with this extension will be listed only.
(continues on next page)
208 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
Returns::obj:`list`: list of data files
"""return _list_app_files(pyrevit.PYREVIT_FILE_PREFIX_STAMPED, file_ext)
def garbage_data_file(file_path):"""Mark and remove the given appdata file.
Current implementation removes the file immediately.
Args:file_path (str): path to the target file
"""_remove_app_file(file_path)
def cleanup_appdata_folder():"""Cleanup appdata folder of all temporary appdata files."""if EXEC_PARAMS.first_load:
hostapp_pids = \[x.ProcessIdfor x in TargetApps.Revit.RevitController.ListRunningRevits()]
for appdata_file in os.listdir(pyrevit.PYREVIT_VERSION_APP_DIR):file_naming_dict = _match_file(appdata_file)if 'pid' in file_naming_dict:
try:pid = int(file_naming_dict['pid'])if pid not in hostapp_pids:
_remove_app_file(op.join(pyrevit.PYREVIT_VERSION_APP_DIR,
appdata_file))
except Exception:pass
17.4 pyrevit.coreutils.pyutils
Helper functions for python.
Example
>>> from pyrevit.coreutils import pyutils>>> pyutils.safe_cast('string', int, 0)
class pyrevit.coreutils.pyutils.DefaultOrderedDict(default_factory=None, *a, **kw)Ordered dictionary with default type.
This is similar to defaultdict and maintains the order of items added to it so in that regards it functions similarto OrderedDict.
17.4. pyrevit.coreutils.pyutils 209
pyRevit Documentation, Release 4.5
Example
>>> from pyrevit.coreutils import pyutils>>> od = pyutils.DefaultOrderedDict(list)>>> od['A'] = [1, 2, 3]>>> od['B'] = [4, 5, 6]>>> od['C'].extend([7, 8, 9])>>> for k, v in od.items():... print(k, v)('A', [1, 2, 3])('B', [4, 5, 6])('C', [7, 8, 9])
copy()Copy the dictionary.
pyrevit.coreutils.pyutils.compare_lists(x, y)Compare two lists.
See: https://stackoverflow.com/a/10872313/2350244
Parameters
• x (list) – first list
• y (list) – second list
pyrevit.coreutils.pyutils.isnumber(token)Verify if given string token is int or float.
Parameters token (str) – string value
Returns True of token is int or float
Return type bool
Example
>>> isnumber('12.3')True
pyrevit.coreutils.pyutils.pairwise(iterable, step=2)Iterate through items in pairs.
Parameters
• iterable (iterable) – any iterable object
• step (int) – number of steps to move when making pairs
Returns list of pairs
Return type iterable
Example
210 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
>>> pairwise([1, 2, 3, 4, 5])[(1, 2), (3, 4)] # 5 can not be paired>>> pairwise([1, 2, 3, 4, 5, 6])[(1, 2), (3, 4), (5, 6)]>>> pairwise([1, 2, 3, 4, 5, 6], step=1)[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
pyrevit.coreutils.pyutils.safe_cast(val, to_type, default=None)Convert value to type gracefully.
This method basically calls to_type(value) and returns the default if exception occurs.
Parameters
• val (any) – value to be converted
• to_type (type) – target type
• default (any) – value to rerun on conversion exception
Example
>>> safe_cast('name', int, default=0)0
17.4.1 Implementation
"""Helper functions for python.
Example:>>> from pyrevit.coreutils import pyutils>>> pyutils.safe_cast('string', int, 0)
"""#pylint: disable=C0103import refrom collections import OrderedDict, Callable #pylint: disable=E0611
class DefaultOrderedDict(OrderedDict):"""Ordered dictionary with default type.
This is similar to defaultdict and maintains the order of items addedto it so in that regards it functions similar to OrderedDict.
Example:>>> from pyrevit.coreutils import pyutils>>> od = pyutils.DefaultOrderedDict(list)>>> od['A'] = [1, 2, 3]>>> od['B'] = [4, 5, 6]>>> od['C'].extend([7, 8, 9])>>> for k, v in od.items():... print(k, v)('A', [1, 2, 3])('B', [4, 5, 6])('C', [7, 8, 9])
(continues on next page)
17.4. pyrevit.coreutils.pyutils 211
pyRevit Documentation, Release 4.5
(continued from previous page)
"""
# Source: http://stackoverflow.com/a/6190500/562769def __init__(self, default_factory=None, *a, **kw): #pylint: disable=W1113
if (default_factory is not None \and not isinstance(default_factory, Callable)):
raise TypeError('first argument must be callable')OrderedDict.__init__(self, *a, **kw)self.default_factory = default_factory
def __getitem__(self, key):try:
return OrderedDict.__getitem__(self, key)except KeyError:
return self.__missing__(key)
def __missing__(self, key):if self.default_factory is None:
raise KeyError(key)self[key] = value = self.default_factory()return value
def __reduce__(self):if self.default_factory is None:
args = tuple()else:
args = self.default_factory,return type(self), args, None, None, self.items()
def copy(self):"""Copy the dictionary."""return self.__copy__()
def __copy__(self):return type(self)(self.default_factory, self)
def __deepcopy__(self, memo):import copyreturn type(self)(self.default_factory,
copy.deepcopy(self.items()))
def __repr__(self, _repr_running=None):return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
OrderedDict.__repr__(self))
def pairwise(iterable, step=2):"""Iterate through items in pairs.
Args:iterable (iterable): any iterable objectstep (int): number of steps to move when making pairs
Returns:iterable: list of pairs
(continues on next page)
212 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
(continued from previous page)
Example:>>> pairwise([1, 2, 3, 4, 5])[(1, 2), (3, 4)] # 5 can not be paired>>> pairwise([1, 2, 3, 4, 5, 6])[(1, 2), (3, 4), (5, 6)]>>> pairwise([1, 2, 3, 4, 5, 6], step=1)[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
"""if step == 1:
from itertools import tee, izip #pylint: disable=E0611a, b = tee(iterable)next(b, None)return izip(a, b)
elif step == 2:a = iter(iterable)return zip(a, a)
def safe_cast(val, to_type, default=None):"""Convert value to type gracefully.
This method basically calls to_type(value) and returns the defaultif exception occurs.
Args:val (any): value to be convertedto_type (type): target typedefault (any): value to rerun on conversion exception
Example:>>> safe_cast('name', int, default=0)0
"""try:
return to_type(val)except (ValueError, TypeError):
return default
def isnumber(token):"""Verify if given string token is int or float.
Args:token (str): string value
Returns:bool: True of token is int or float
Example:>>> isnumber('12.3')True
"""if token:
return re.match("^[0-9.]+?$", token) is not Noneelse:
return False
(continues on next page)
17.4. pyrevit.coreutils.pyutils 213
pyRevit Documentation, Release 4.5
(continued from previous page)
def compare_lists(x, y):"""Compare two lists.
See: https://stackoverflow.com/a/10872313/2350244
Args:x (list): first listy (list): second list
"""return len(frozenset(x).difference(y)) == 0
17.5 pyrevit.coreutils.mathnet
MathNet importer module.
See https://www.mathdotnet.com for documentation.
Example
>>> from pyrevit.coreutils.mathnet import MathNet
17.5.1 Implementation
"""MathNet importer module.
See https://www.mathdotnet.com for documentation.
Example:>>> from pyrevit.coreutils.mathnet import MathNet
"""#pylint: disable=W0703,C0302,C0103from pyrevit import EXEC_PARAMSfrom pyrevit import frameworkfrom pyrevit.framework import clrfrom pyrevit.coreutils.logger import get_logger
mlogger = get_logger(__name__)
MATHNET_LIB = 'MathNet.Numerics'
if not EXEC_PARAMS.doc_mode:mathnet_dll = framework.get_dll_file(MATHNET_LIB)mlogger.debug('Loading dll: %s', mathnet_dll)try:
clr.AddReferenceToFileAndPath(mathnet_dll)import MathNet #pylint: disable=E0401,W0611
except Exception as load_err:mlogger.error('Can not load %s module. | %s', MATHNET_LIB, load_err)
214 Chapter 17. pyrevit.coreutils
pyRevit Documentation, Release 4.5
17.6 pyrevit.coreutils.moduleutils
Utility fuctions to support smart modules.
pyrevit.coreutils.moduleutils.copy_func(func, func_name, doc_string=None,arg_list=None)
Copy a function object to create a new function.
This is used inside smart modules that auto-generate functions based on context.
Parameters
• func (object) – python source function object
• func_name (str) – new function name
• doc_string (str) – new function docstring
• arg_list (list) – list of default values for function arguments
Returns new python function objects
Return type object
17.6.1 Implementation
"""Utility fuctions to support smart modules."""
import types
def copy_func(func, func_name, doc_string=None, arg_list=None):"""Copy a function object to create a new function.
This is used inside smart modules that auto-generate functions based oncontext.
Args:func (object): python source function objectfunc_name (str): new function namedoc_string (str): new function docstringarg_list (list): list of default values for function arguments
Returns:object: new python function objects
"""new_func = types.FunctionType(func.func_code, func.func_globals,
func_name, tuple(arg_list), func.func_closure)new_func.__doc__ = doc_stringreturn new_func
17.6. pyrevit.coreutils.moduleutils 215
pyRevit Documentation, Release 4.5
216 Chapter 17. pyrevit.coreutils
CHAPTER 18
pyrevit.output
Provides access and control over the pyRevit output window.
18.1 pyrevit.output
Provide access to output window and its functionality.
This module provides access to the output window for the currently running pyRevit command. The proper way toaccess this wrapper object is through the get_output() of pyrevit.script module. This method, in returnuses the pyrevit.output module to get access to the output wrapper.
Example
>>> from pyrevit import script>>> output = script.get_output()
Here is the source of pyrevit.script.get_output(). As you can see this functions calls the pyrevit.output.get_output() to receive the output wrapper.
def get_output():"""Return object wrapping output window for current script.
Returns::obj:`pyrevit.output.PyRevitOutputWindow`: Output wrapper object
"""return output.get_output()
class pyrevit.output.PyRevitOutputWindowWrapper to interact with the output window.
add_style(style_code, attribs=None)Inject style tag into current html head of the output window.
217
pyRevit Documentation, Release 4.5
Parameters
• style_code (str) – css styling code
• attribs (dict) – dictionary of attribute names and value
Example
>>> output = pyrevit.output.get_output()>>> output.add_style('body { color: blue; }')
close()Close the window.
close_others(all_open_outputs=False)Close all other windows that belong to the current command.
Parameters all_open_outputs (bool) – Close all any other windows if True
debug_modeSet debug mode on output window and stream.
This will cause the output window to print information about the buffer stream and other aspects of theoutput window mechanism.
freeze()Freeze output contetn update.
get_head_html()str: Return inner code of html head element.
get_height()int: Return current window height.
get_title()str: Return current window title.
get_width()int: Return current window width.
hide()Hide the window.
hide_logpanel()Hide output window logging panel.
hide_progress()Hide output window progress bar.
indeterminate_progress(state)Show or hide indeterminate progress bar.
inject_script(script_code, attribs=None, body=False)Inject script tag into current head (or body) of the output window.
Parameters
• script_code (str) – javascript code
• attribs (dict) – dictionary of attribute names and value
• body (bool, optional) – injects script into body instead of head
218 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
Example
>>> output = pyrevit.output.get_output()>>> output.inject_script('', # no script since it's a link
{'src': js_script_file_path})
inject_to_body(element_tag, element_contents, attribs=None)Inject html element to current html body of the output window.
Parameters
• element_tag (str) – html tag of the element e.g. ‘div’
• element_contents (str) – html code of the element contents
• attribs (dict) – dictionary of attribute names and value
Example
>>> output = pyrevit.output.get_output()>>> output.inject_to_body('script',
'', # no script since it's a link{'src': js_script_file_path})
inject_to_head(element_tag, element_contents, attribs=None)Inject html element to current html head of the output window.
Parameters
• element_tag (str) – html tag of the element e.g. ‘div’
• element_contents (str) – html code of the element contents
• attribs (dict) – dictionary of attribute names and value
Example
>>> output = pyrevit.output.get_output()>>> output.inject_to_head('script',
'', # no script since it's a link{'src': js_script_file_path})
insert_divider()Add horizontal rule to the output window.
static linkify(element_ids, title=None)Create clickable link for the provided element ids.
This method, creates the link but does not print it directly.
Parameters
• element_ids (list of ElementId) –
• element_ids – single or multiple ids
• title (str) – tile of the link. defaults to list of element ids
18.1. pyrevit.output 219
pyRevit Documentation, Release 4.5
Example
>>> output = pyrevit.output.get_output()>>> for idx, elid in enumerate(element_ids):>>> print('{}: {}'.format(idx+1, output.linkify(elid)))
lock_size()Lock window size.
log_error(message)Report ERROR message into output logging panel.
log_info(message)Report INFO message into output logging panel.
log_ok(message)Report OK message into output logging panel.
log_warning(message)Report WARNING message into output logging panel.
make_bar_chart()PyRevitOutputChart: Return bar chart object.
make_bubble_chart()PyRevitOutputChart: Return bubble chart object.
make_chart()PyRevitOutputChart: Return chart object.
make_doughnut_chart()PyRevitOutputChart: Return dougnut chart object.
make_line_chart()PyRevitOutputChart: Return line chart object.
make_pie_chart()PyRevitOutputChart: Return pie chart object.
make_polar_chart()PyRevitOutputChart: Return polar chart object.
make_radar_chart()PyRevitOutputChart: Return radar chart object.
make_stacked_chart()PyRevitOutputChart: Return stacked chart object.
next_page()Add hidden next page tag to the output window.
This is helpful to silently separate the output to multiple pages for better printing.
open_page(dest_file)Open html page in output window.
Parameters dest_file (str) – full path of the target html file
open_url(dest_url)Open url page in output window.
Parameters dest_url (str) – web url of the target page
220 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
output_idstr – Return id of the output window.
In current implementation, Id of output window is equal to the unique id of the pyRevit command itbelongs to. This means that all output windows belonging to the same pyRevit command, will haveidentical output_id values.
output_uniqueidstr – Return unique id of the output window.
In current implementation, unique id of output window is a GUID string generated when the output windowis opened. This id is unique to the instance of output window.
static print_code(code_str)Print code to the output window with special formatting.
Example
>>> output = pyrevit.output.get_output()>>> output.print_code('value = 12')
static print_html(html_str)Add the html code to the output window.
Example
>>> output = pyrevit.output.get_output()>>> output.print_html('<strong>Title</strong>')
static print_md(md_str)Process markdown code and print to output window.
Example
>>> output = pyrevit.output.get_output()>>> output.print_md('### Title')
print_table(table_data, columns=None, formats=None, title=”, last_line_style=”)Print provided data in a table in output window.
Parameters
• table_data (list of iterables) – 2D array of data
• title (str) – table title
• columns (list str) – list of column names
• formats (list str) – column data formats
• last_line_style (str) – css style of last row
18.1. pyrevit.output 221
pyRevit Documentation, Release 4.5
Example
>>> data = [... ['row1', 'data', 'data', 80 ],... ['row2', 'data', 'data', 45 ],... ]>>> output.print_table(... table_data=data,... title="Example Table",... columns=["Row Name", "Column 1", "Column 2", "Percentage"],... formats=['', '', '', '{}%'],... last_line_style='color:red;'... )
rendererReturn html renderer inside output window.
Returns System.Windows.Forms.WebBrowser (In current implementation)
reset_icon()Sets icon on the output window.
reset_progress()Reset output window progress bar to zero.
resize(width, height)Resize window to the new width and height.
save_contents(dest_file)Save html code of the window.
Parameters dest_file (str) – full path of the destination html file
self_destruct(seconds)Set self-destruct (close window) timer.
Parameters seconds (int) – number of seconds after which window is closed.
set_font(font_family, font_size)Set window font family to the new font family and size.
Parameters
• font_family (str) – font family name e.g. ‘Courier New’
• font_size (int) – font size e.g. 16
set_height(height)Set window height to the new height.
set_icon(iconpath)Sets icon on the output window.
set_title(new_title)Set window title to the new title.
set_width(width)Set window width to the new width.
show()Show the window.
222 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
show_logpanel()Show output window logging panel.
unfreeze()Unfreeze output contetn update.
unhide_progress()Unhide output window progress bar.
unlock_size()Unock window size.
update_progress(cur_value, max_value)Activate and update the output window progress bar.
Parameters
• cur_value (float) – current progress value e.g. 50
• max_value (float) – total value e.g. 100
Example
>>> output = pyrevit.output.get_output()>>> for i in range(100):>>> output.update_progress(i, 100)
windowPyRevitBaseClasses.ScriptOutput – Return output window object.
pyrevit.output.docclosing_eventhandler(sender, args)Close all output window on document closing.
pyrevit.output.get_default_stylesheet()Return default css stylesheet used by output window.
pyrevit.output.get_output()pyrevit.output.PyRevitOutputWindow : Return output window.
pyrevit.output.get_stylesheet()Return active css stylesheet used by output window.
pyrevit.output.reset_stylesheet()Reset active stylesheet to default.
pyrevit.output.set_stylesheet(stylesheet)Set active css stylesheet used by output window.
Parameters stylesheet (str) – full path to stylesheet file
pyrevit.output.setup_output_closer()Setup document closing event listener.
18.1.1 Implementation
"""Provide access to output window and its functionality.
This module provides access to the output window for the currently runningpyRevit command. The proper way to access this wrapper object is through
(continues on next page)
18.1. pyrevit.output 223
pyRevit Documentation, Release 4.5
(continued from previous page)
the :func:`get_output` of :mod:`pyrevit.script` module. This method, in returnuses the `pyrevit.output` module to get access to the output wrapper.
Example:>>> from pyrevit import script>>> output = script.get_output()
Here is the source of :func:`pyrevit.script.get_output`. As you can see thisfunctions calls the :func:`pyrevit.output.get_output` to receive theoutput wrapper.
.. literalinclude:: ../../../pyrevitlib/pyrevit/script.py:pyobject: get_output
"""
from __future__ import print_functionimport os.path as opimport itertools
from pyrevit import HOST_APP, EXEC_PARAMSfrom pyrevit import frameworkfrom pyrevit import coreutilsfrom pyrevit.coreutils import loggerfrom pyrevit.coreutils import markdown, chartsfrom pyrevit.coreutils import envvarsfrom pyrevit.coreutils.loadertypes import EnvDictionaryKeysfrom pyrevit.coreutils.loadertypes import ScriptOutputManagerfrom pyrevit.output import linkmakerfrom pyrevit.userconfig import user_configfrom pyrevit import DB
#pylint: disable=W0703,C0302,C0103mlogger = logger.get_logger(__name__)
DEFAULT_STYLESHEET_NAME = 'outputstyles.css'
def docclosing_eventhandler(sender, args): #pylint: disable=W0613"""Close all output window on document closing."""ScriptOutputManager.CloseActiveOutputWindows()
def setup_output_closer():"""Setup document closing event listener."""HOST_APP.app.DocumentClosing += \
framework.EventHandler[DB.Events.DocumentClosingEventArgs](docclosing_eventhandler)
def set_stylesheet(stylesheet):"""Set active css stylesheet used by output window.
Args:stylesheet (str): full path to stylesheet file
(continues on next page)
224 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
"""envvars.set_pyrevit_env_var(EnvDictionaryKeys.outputStyleSheet,
stylesheet)
def get_stylesheet():"""Return active css stylesheet used by output window."""return envvars.get_pyrevit_env_var(EnvDictionaryKeys.outputStyleSheet)
def get_default_stylesheet():"""Return default css stylesheet used by output window."""return op.join(op.dirname(__file__), DEFAULT_STYLESHEET_NAME)
def reset_stylesheet():"""Reset active stylesheet to default."""envvars.set_pyrevit_env_var(EnvDictionaryKeys.outputStyleSheet,
get_default_stylesheet())
# setup output window stylesheetif not EXEC_PARAMS.doc_mode:
active_stylesheet = \user_config.core.get_option('outputstylesheet',
default_value=get_default_stylesheet())set_stylesheet(active_stylesheet)
class PyRevitOutputWindow(object):"""Wrapper to interact with the output window."""
@propertydef window(self):
"""``PyRevitBaseClasses.ScriptOutput``: Return output window object."""return EXEC_PARAMS.window_handle
@propertydef renderer(self):
"""Return html renderer inside output window.
Returns:``System.Windows.Forms.WebBrowser`` (In current implementation)
"""if self.window:
return self.window.renderer
@propertydef output_id(self):
"""str: Return id of the output window.
In current implementation, Id of output window is equal to theunique id of the pyRevit command it belongs to. This means that alloutput windows belonging to the same pyRevit command, will haveidentical output_id values."""if self.window:
(continues on next page)
18.1. pyrevit.output 225
pyRevit Documentation, Release 4.5
(continued from previous page)
return self.window.OutputId
@propertydef output_uniqueid(self):
"""str: Return unique id of the output window.
In current implementation, unique id of output window is a GUID stringgenerated when the output window is opened. This id is unique to theinstance of output window."""if self.window:
return self.window.OutputUniqueId
@propertydef debug_mode(self):
"""Set debug mode on output window and stream.
This will cause the output window to print information about thebuffer stream and other aspects of the output window mechanism."""return EXEC_PARAMS.pyrevit_command.OutputStream.PrintDebugInfo
@debug_mode.setterdef debug_mode(self, value):
EXEC_PARAMS.pyrevit_command.OutputStream.PrintDebugInfo = value
def _get_head_element(self):return self.renderer.Document.GetElementsByTagName('head')[0]
def _get_body_element(self):return self.renderer.Document.GetElementsByTagName('body')[0]
def self_destruct(self, seconds):"""Set self-destruct (close window) timer.
Args:seconds (int): number of seconds after which window is closed.
"""if self.window:
self.window.SelfDestructTimer(seconds)
def inject_to_head(self, element_tag, element_contents, attribs=None):"""Inject html element to current html head of the output window.
Args:element_tag (str): html tag of the element e.g. 'div'element_contents (str): html code of the element contentsattribs (:obj:`dict`): dictionary of attribute names and value
Example:>>> output = pyrevit.output.get_output()>>> output.inject_to_head('script',
'', # no script since it's a link{'src': js_script_file_path})
"""html_element = self.renderer.Document.CreateElement(element_tag)if element_contents:
(continues on next page)
226 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
html_element.InnerHtml = element_contents
if attribs:for attribute, value in attribs.items():
html_element.SetAttribute(attribute, value)
# inject the script into headhead_el = self._get_head_element()head_el.AppendChild(html_element)
def inject_to_body(self, element_tag, element_contents, attribs=None):"""Inject html element to current html body of the output window.
Args:element_tag (str): html tag of the element e.g. 'div'element_contents (str): html code of the element contentsattribs (:obj:`dict`): dictionary of attribute names and value
Example:>>> output = pyrevit.output.get_output()>>> output.inject_to_body('script',
'', # no script since it's a link{'src': js_script_file_path})
"""html_element = self.renderer.Document.CreateElement(element_tag)if element_contents:
html_element.InnerHtml = element_contents
if attribs:for attribute, value in attribs.items():
html_element.SetAttribute(attribute, value)
# inject the script into bodybody_el = self._get_body_element()body_el.AppendChild(html_element)
def inject_script(self, script_code, attribs=None, body=False):"""Inject script tag into current head (or body) of the output window.
Args:script_code (str): javascript codeattribs (:obj:`dict`): dictionary of attribute names and valuebody (bool, optional): injects script into body instead of head
Example:>>> output = pyrevit.output.get_output()>>> output.inject_script('', # no script since it's a link
{'src': js_script_file_path})"""if body:
self.inject_to_body('script', script_code, attribs=attribs)else:
self.inject_to_head('script', script_code, attribs=attribs)
def add_style(self, style_code, attribs=None):"""Inject style tag into current html head of the output window.
(continues on next page)
18.1. pyrevit.output 227
pyRevit Documentation, Release 4.5
(continued from previous page)
Args:style_code (str): css styling codeattribs (:obj:`dict`): dictionary of attribute names and value
Example:>>> output = pyrevit.output.get_output()>>> output.add_style('body { color: blue; }')
"""self.inject_to_head('style', style_code, attribs=attribs)
def get_head_html(self):"""str: Return inner code of html head element."""return self._get_head_element().InnerHtml
def set_title(self, new_title):"""Set window title to the new title."""if self.window:
self.window.Title = new_title
def set_width(self, width):"""Set window width to the new width."""if self.window:
self.window.Width = width
def set_height(self, height):"""Set window height to the new height."""if self.window:
self.window.Height = height
def set_font(self, font_family, font_size):"""Set window font family to the new font family and size.
Args:font_family (str): font family name e.g. 'Courier New'font_size (int): font size e.g. 16
"""self.renderer.Font = \
framework.Drawing.Font(font_family,font_size,framework.Drawing.FontStyle.Regular,framework.Drawing.GraphicsUnit.Point)
def resize(self, width, height):"""Resize window to the new width and height."""self.set_width(width)self.set_height(height)
def get_title(self):"""str: Return current window title."""if self.window:
return self.window.Text
def get_width(self):"""int: Return current window width."""if self.window:
return self.window.Width
(continues on next page)
228 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
def get_height(self):"""int: Return current window height."""if self.window:
return self.window.Height
def close(self):"""Close the window."""if self.window:
self.window.Close()
def close_others(self, all_open_outputs=False):"""Close all other windows that belong to the current command.
Args:all_open_outputs (bool): Close all any other windows if True
"""if all_open_outputs:
ScriptOutputManager.CloseActiveOutputWindows(self.window)else:
ScriptOutputManager.CloseActiveOutputWindows(self.window,self.output_id)
def hide(self):"""Hide the window."""if self.window:
self.window.Hide()
def show(self):"""Show the window."""if self.window:
self.window.Show()
def lock_size(self):"""Lock window size."""if self.window:
self.window.LockSize()
def unlock_size(self):"""Unock window size."""if self.window:
self.window.UnlockSize()
def freeze(self):"""Freeze output contetn update."""if self.window:
self.window.Freeze()
def unfreeze(self):"""Unfreeze output contetn update."""if self.window:
self.window.Unfreeze()
def save_contents(self, dest_file):"""Save html code of the window.
Args:dest_file (str): full path of the destination html file
(continues on next page)
18.1. pyrevit.output 229
pyRevit Documentation, Release 4.5
(continued from previous page)
"""if self.renderer:
html = \self.renderer.Document.Body.OuterHtml.encode('ascii', 'ignore')
doc_txt = self.renderer.DocumentTextfull_html = doc_txt.lower().replace('<body></body>', html)with open(dest_file, 'w') as output_file:
output_file.write(full_html)
def open_url(self, dest_url):"""Open url page in output window.
Args:dest_url (str): web url of the target page
"""if self.renderer:
self.renderer.Navigate(dest_url, False)
def open_page(self, dest_file):"""Open html page in output window.
Args:dest_file (str): full path of the target html file
"""self.show()self.open_url('file:///' + dest_file)
def update_progress(self, cur_value, max_value):"""Activate and update the output window progress bar.
Args:cur_value (float): current progress value e.g. 50max_value (float): total value e.g. 100
Example:>>> output = pyrevit.output.get_output()>>> for i in range(100):>>> output.update_progress(i, 100)
"""if self.window:
self.window.UpdateActivityBar(cur_value, max_value)
def reset_progress(self):"""Reset output window progress bar to zero."""if self.window:
self.window.UpdateActivityBar(0, 1)
def hide_progress(self):"""Hide output window progress bar."""if self.window:
self.window.SetActivityBarVisibility(False)
def unhide_progress(self):"""Unhide output window progress bar."""if self.window:
self.window.SetActivityBarVisibility(True)
(continues on next page)
230 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
def indeterminate_progress(self, state):"""Show or hide indeterminate progress bar. """if self.window:
self.window.UpdateActivityBar(state)
def show_logpanel(self):"""Show output window logging panel."""if self.window:
self.window.SetActivityBarVisibility(True)
def hide_logpanel(self):"""Hide output window logging panel."""if self.window:
self.show_logpanel()self.window.SetActivityBarVisibility(False)
def log_ok(self, message):"""Report OK message into output logging panel."""if self.window:
self.show_logpanel()self.window.activityBar.ConsoleLogOK(message)
def log_info(self, message):"""Report INFO message into output logging panel."""if self.window:
self.show_logpanel()self.window.activityBar.ConsoleLogInfo(message)
def log_warning(self, message):"""Report WARNING message into output logging panel."""if self.window:
self.show_logpanel()self.window.activityBar.ConsoleLogWarning(message)
def log_error(self, message):"""Report ERROR message into output logging panel."""if self.window:
self.show_logpanel()self.window.activityBar.ConsoleLogError(message)
def set_icon(self, iconpath):"""Sets icon on the output window."""if self.window:
self.window.SetIcon(iconpath)
def reset_icon(self):"""Sets icon on the output window."""if self.window:
self.window.ResetIcon()
@staticmethoddef print_html(html_str):
"""Add the html code to the output window.
Example:>>> output = pyrevit.output.get_output()>>> output.print_html('<strong>Title</strong>')
(continues on next page)
18.1. pyrevit.output 231
pyRevit Documentation, Release 4.5
(continued from previous page)
"""print(coreutils.prepare_html_str(html_str),
end="")
@staticmethoddef print_code(code_str):
"""Print code to the output window with special formatting.
Example:>>> output = pyrevit.output.get_output()>>> output.print_code('value = 12')
"""code_div = '<div class="code">{}</div>'print(
coreutils.prepare_html_str(code_div.format(
code_str.replace(' ', ' '*4))
),end="")
@staticmethoddef print_md(md_str):
"""Process markdown code and print to output window.
Example:>>> output = pyrevit.output.get_output()>>> output.print_md('### Title')
"""tables_ext = 'pyrevit.coreutils.markdown.extensions.tables'markdown_html = markdown.markdown(md_str, extensions=[tables_ext])markdown_html = markdown_html.replace('\n', '').replace('\r', '')html_code = coreutils.prepare_html_str(markdown_html)print(html_code, end="")
def print_table(self, table_data, columns=None, formats=None,title='', last_line_style=''):
"""Print provided data in a table in output window.
Args:table_data (:obj:`list` of iterables): 2D array of datatitle (str): table titlecolumns (:obj:`list` str): list of column namesformats (:obj:`list` str): column data formatslast_line_style (str): css style of last row
Example:>>> data = [... ['row1', 'data', 'data', 80 ],... ['row2', 'data', 'data', 45 ],... ]>>> output.print_table(... table_data=data,... title="Example Table",... columns=["Row Name", "Column 1", "Column 2", "Percentage"],... formats=['', '', '', '{}%'],
(continues on next page)
232 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
... last_line_style='color:red;'
... )"""if not columns:
columns = []if not formats:
formats = []
if last_line_style:self.add_style('tr:last-child {{ {style} }}'
.format(style=last_line_style))
zipper = itertools.izip_longest #pylint: disable=E1101adjust_base_col = '|'adjust_extra_col = ':---|'base_col = '|'extra_col = '{data}|'
# find max column countmax_col = max([len(x) for x in table_data])
header = ''if columns:
header = base_colfor idx, col_name in zipper(range(max_col), columns, fillvalue=''):
→˓#pylint: disable=W0612header += extra_col.format(data=col_name)
header += '\n'
justifier = adjust_base_colfor idx in range(max_col):
justifier += adjust_extra_col
justifier += '\n'
rows = ''for entry in table_data:
row = base_colfor idx, attrib, attr_format \
in zipper(range(max_col), entry, formats, fillvalue=''):if attr_format:
value = attr_format.format(attrib)else:
value = attribrow += extra_col.format(data=value)
rows += row + '\n'
table = header + justifier + rowsself.print_md('### {title}'.format(title=title))self.print_md(table)
def insert_divider(self):"""Add horizontal rule to the output window."""self.print_md('-----')
def next_page(self):(continues on next page)
18.1. pyrevit.output 233
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Add hidden next page tag to the output window.
This is helpful to silently separate the output to multiple pagesfor better printing."""self.print_html('<div class="nextpage"></div><div> </div>')
@staticmethoddef linkify(element_ids, title=None):
"""Create clickable link for the provided element ids.
This method, creates the link but does not print it directly.
Args:element_ids (`ElementId`) orelement_ids (:obj:`list` of `ElementId`): single or multiple idstitle (str): tile of the link. defaults to list of element ids
Example:>>> output = pyrevit.output.get_output()>>> for idx, elid in enumerate(element_ids):>>> print('{}: {}'.format(idx+1, output.linkify(elid)))
"""return coreutils.prepare_html_str(
linkmaker.make_link(element_ids, contents=title))
def make_chart(self):""":obj:`PyRevitOutputChart`: Return chart object."""return charts.PyRevitOutputChart(self)
def make_line_chart(self):""":obj:`PyRevitOutputChart`: Return line chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.LINE_CHART)
def make_stacked_chart(self):""":obj:`PyRevitOutputChart`: Return stacked chart object."""chart = charts.PyRevitOutputChart(self, chart_type=charts.LINE_CHART)chart.options.scales = {'yAxes': [{'stacked': True, }]}return chart
def make_bar_chart(self):""":obj:`PyRevitOutputChart`: Return bar chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.BAR_CHART)
def make_radar_chart(self):""":obj:`PyRevitOutputChart`: Return radar chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.RADAR_CHART)
def make_polar_chart(self):""":obj:`PyRevitOutputChart`: Return polar chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.POLAR_CHART)
def make_pie_chart(self):""":obj:`PyRevitOutputChart`: Return pie chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.PIE_CHART)
(continues on next page)
234 Chapter 18. pyrevit.output
pyRevit Documentation, Release 4.5
(continued from previous page)
def make_doughnut_chart(self):""":obj:`PyRevitOutputChart`: Return dougnut chart object."""return charts.PyRevitOutputChart(self,
chart_type=charts.DOUGHNUT_CHART)
def make_bubble_chart(self):""":obj:`PyRevitOutputChart`: Return bubble chart object."""return charts.PyRevitOutputChart(self, chart_type=charts.BUBBLE_CHART)
def get_output():""":obj:`pyrevit.output.PyRevitOutputWindow` : Return output window."""return PyRevitOutputWindow()
18.2 pyrevit.output.linkmaker
Handle creation of output window helper links.
pyrevit.output.linkmaker.make_link(element_ids, contents=None)Create link for given element ids.
This link is a special format link with revit:// scheme that is handled by the output window to select the providedelement ids in current project. Scripts should not call this function directly. Creating clickable element links ishandled by the output wrapper object through the linkify() method.
Example
>>> output = pyrevit.output.get_output()>>> for idx, elid in enumerate(element_ids):>>> print('{}: {}'.format(idx+1, output.linkify(elid)))
18.2.1 Implementation
"""Handle creation of output window helper links."""
from pyrevit.compat import safe_strtypefrom pyrevit import DBfrom pyrevit.coreutils.logger import get_logger
#pylint: disable=W0703,C0302,C0103mlogger = get_logger(__name__)
PROTOCOL_NAME = 'revit://outputhelpers?'
DEFAULT_LINK = '<a title="Click to select or show element" ' \'class="elementlink" {}>{}</a>'
def make_link(element_ids, contents=None):(continues on next page)
18.2. pyrevit.output.linkmaker 235
pyRevit Documentation, Release 4.5
(continued from previous page)
"""Create link for given element ids.
This link is a special format link with revit:// scheme that is handledby the output window to select the provided element ids in current project.Scripts should not call this function directly. Creating clickable elementlinks is handled by the output wrapper object through the :func:`linkify`method.
Example:>>> output = pyrevit.output.get_output()>>> for idx, elid in enumerate(element_ids):>>> print('{}: {}'.format(idx+1, output.linkify(elid)))
"""elementquery = []if isinstance(element_ids, list):
strids = [safe_strtype(x.IntegerValue) for x in element_ids]elif isinstance(element_ids, DB.ElementId):
strids = [safe_strtype(element_ids.IntegerValue)]
for strid in strids:elementquery.append('element[]={}'.format(strid))
reviturl = '&'.join(elementquery)linkname = ', '.join(strids)
if len(reviturl) >= 2000:alertjs = 'alert("Url was too long and discarded!");'linkattrs = 'href="#" onClick="{}"'.format(alertjs)
else:linkattrs = 'href="{}{}{}"'.format(PROTOCOL_NAME,
'&command=select&',reviturl)
return DEFAULT_LINK.format(linkattrs, contents or linkname)
236 Chapter 18. pyrevit.output
Python Module Index
ppyrevit, 61pyrevit.api, 75pyrevit.compat, 77pyrevit.coreutils, 163pyrevit.coreutils.appdata, 202pyrevit.coreutils.envvars, 200pyrevit.coreutils.mathnet, 214pyrevit.coreutils.moduleutils, 215pyrevit.coreutils.pyutils, 209pyrevit.forms, 79pyrevit.forms.utils, 94pyrevit.framework, 133pyrevit.output, 217pyrevit.output.linkmaker, 235pyrevit.script, 137pyrevit.userconfig, 153
237
pyRevit Documentation, Release 4.5
238 Python Module Index
Index
Symbols_ExecutorParams (class in pyrevit), 63_HostAppPostableCommand (class in pyrevit), 61_HostApplication (class in pyrevit), 62_setup() (pyrevit.forms.CommandSwitchWindow
method), 80_setup() (pyrevit.forms.GetValueWindow method), 80_setup() (pyrevit.forms.ProgressBar method), 81_setup() (pyrevit.forms.SelectFromList method), 84_setup() (pyrevit.forms.TemplatePromptBar method), 85_setup() (pyrevit.forms.TemplateUserInputWindow
method), 85_setup() (pyrevit.forms.WarningBar method), 87
Aactiveview (pyrevit._HostApplication attribute), 62add_style() (pyrevit.output.PyRevitOutputWindow
method), 217alert() (in module pyrevit.forms), 87alert_ifnot() (in module pyrevit.forms), 88app (pyrevit._HostApplication attribute), 62available_servers (pyrevit._HostApplication attribute), 62
Bbitmap_from_file() (in module pyrevit.forms.utils), 94build (pyrevit._HostApplication attribute), 62button_select() (pyrevit.forms.SelectFromList method),
84
Ccalculate_dir_hash() (in module pyrevit.coreutils), 164can_access_url() (in module pyrevit.coreutils), 165check_all() (pyrevit.forms.SelectFromList method), 84check_familydoc() (in module pyrevit.forms), 88check_internet_connection() (in module pyre-
vit.coreutils), 165check_revittxt_encoding() (in module pyrevit.coreutils),
165
check_selected() (pyrevit.forms.SelectFromList method),84
check_selection() (in module pyrevit.forms), 89check_utf8bom_encoding() (in module pyrevit.coreutils),
165check_workshared() (in module pyrevit.forms), 89checkable (pyrevit.forms.TemplateListItem attribute), 85cleanup_appdata_folder() (in module pyre-
vit.coreutils.appdata), 203cleanup_filename() (in module pyrevit.coreutils), 165cleanup_string() (in module pyrevit.coreutils), 166clear_search() (pyrevit.forms.SelectFromList method), 84clicked_cancel() (pyrevit.forms.ProgressBar method), 81clipboard_copy() (in module pyrevit.script), 137close() (pyrevit.output.PyRevitOutputWindow method),
218close_others() (pyrevit.output.PyRevitOutputWindow
method), 218command_alt_path (pyrevit._ExecutorParams attribute),
63command_bundle (pyrevit._ExecutorParams attribute),
63command_data (pyrevit._ExecutorParams attribute), 63command_extension (pyrevit._ExecutorParams attribute),
63command_mode (pyrevit._ExecutorParams attribute), 63command_name (pyrevit._ExecutorParams attribute), 63command_path (pyrevit._ExecutorParams attribute), 63command_uniqueid (pyrevit._ExecutorParams attribute),
63CommandSwitchWindow (class in pyrevit.forms), 79compare_lists() (in module pyrevit.coreutils.pyutils), 210config_file (pyrevit.userconfig.PyRevitConfig attribute),
154copy() (pyrevit.coreutils.pyutils.DefaultOrderedDict
method), 210copy_func() (in module pyrevit.coreutils.moduleutils),
215correct_revittxt_encoding() (in module pyrevit.coreutils),
166
239
pyRevit Documentation, Release 4.5
create_ext_command_attrs() (in module pyre-vit.coreutils), 166
create_type() (in module pyrevit.coreutils), 166current_date() (in module pyrevit.coreutils), 167current_time() (in module pyrevit.coreutils), 167
Ddebug_mode (pyrevit.output.PyRevitOutputWindow at-
tribute), 218decrement_str() (in module pyrevit.coreutils), 167DefaultOrderedDict (class in pyrevit.coreutils.pyutils),
209disable_element() (pyrevit.forms.WPFWindow static
method), 86dletter_to_unc() (in module pyrevit.coreutils), 168doc (pyrevit._HostApplication attribute), 62doc_mode (pyrevit._ExecutorParams attribute), 63docclosing_eventhandler() (in module pyrevit.output),
223docs (pyrevit._HostApplication attribute), 62
Eenable_element() (pyrevit.forms.WPFWindow static
method), 86engine_mgr (pyrevit._ExecutorParams attribute), 63engine_ver (pyrevit._ExecutorParams attribute), 63executed_from_ui (pyrevit._ExecutorParams attribute),
63exit() (in module pyrevit.script), 137extract_guid() (in module pyrevit.coreutils), 168extract_param() (pyrevit.coreutils.ScriptFileParser
method), 164extract_range() (in module pyrevit.coreutils), 168
FFileWatcher (class in pyrevit.coreutils), 163filter_null_items() (in module pyrevit.coreutils), 168find_config_file() (in module pyrevit.userconfig), 155find_direct_match() (pyrevit.forms.SearchPrompt
method), 82find_loaded_asm() (in module pyrevit.coreutils), 168find_type_by_name() (in module pyrevit.coreutils), 169find_word_match() (pyrevit.forms.SearchPrompt
method), 82first_load (pyrevit._ExecutorParams attribute), 64forced_debug_mode (pyrevit._ExecutorParams attribute),
64format_hex_rgb() (in module pyrevit.coreutils), 169freeze() (pyrevit.output.PyRevitOutputWindow method),
218fully_remove_dir() (in module pyrevit.coreutils), 169fuzzy_search_ratio() (in module pyrevit.coreutils), 169
Ggarbage_data_file() (in module pyrevit.coreutils.appdata),
203get_all_buttons() (in module pyrevit.script), 137get_all_subclasses() (in module pyrevit.coreutils), 169get_alt_script_path() (in module pyrevit.script), 137get_bundle_file() (in module pyrevit.script), 137get_bundle_files() (in module pyrevit.script), 138get_bundle_name() (in module pyrevit.script), 138get_button() (in module pyrevit.script), 138get_canonical_parts() (in module pyrevit.coreutils), 169get_config() (in module pyrevit.script), 138get_config_version() (pyrevit.userconfig.PyRevitConfig
method), 154get_data_file() (in module pyrevit.coreutils.appdata), 203get_data_file() (in module pyrevit.script), 138get_default_stylesheet() (in module pyrevit.output), 223get_dll_file() (in module pyrevit.framework), 133get_docstring() (pyrevit.coreutils.ScriptFileParser
method), 164get_document_data_file() (in module pyrevit.script), 138get_enum_none() (in module pyrevit.coreutils), 169get_enum_value() (in module pyrevit.coreutils), 169get_enum_values() (in module pyrevit.coreutils), 169get_envvar() (in module pyrevit.script), 139get_ext_root_dirs() (pyrevit.userconfig.PyRevitConfig
method), 154get_extension_name() (in module pyrevit.script), 139get_file_name() (in module pyrevit.coreutils), 169get_head_html() (pyrevit.output.PyRevitOutputWindow
method), 218get_height() (pyrevit.output.PyRevitOutputWindow
method), 218get_info() (in module pyrevit.script), 139get_instance_data_file() (in module pyre-
vit.coreutils.appdata), 203get_instance_data_file() (in module pyrevit.script), 139get_logger() (in module pyrevit.script), 140get_mapped_drives_dict() (in module pyrevit.coreutils),
170get_output() (in module pyrevit.output), 223get_output() (in module pyrevit.script), 140get_postable_commands() (pyrevit._HostApplication
method), 62get_pyrevit_env_var() (in module pyre-
vit.coreutils.envvars), 200get_pyrevit_env_vars() (in module pyre-
vit.coreutils.envvars), 200get_pyrevit_version() (in module pyrevit.script), 140get_results() (in module pyrevit.script), 140get_revit_instance_count() (in module pyrevit.coreutils),
170get_script_path() (in module pyrevit.script), 140get_str_hash() (in module pyrevit.coreutils), 170
240 Index
pyRevit Documentation, Release 4.5
get_stylesheet() (in module pyrevit.output), 223get_sub_folders() (in module pyrevit.coreutils), 170get_thirdparty_ext_root_dirs() (pyre-
vit.userconfig.PyRevitConfig method), 154get_time() (pyrevit.coreutils.Timer method), 164get_title() (pyrevit.output.PyRevitOutputWindow
method), 218get_type() (in module pyrevit.framework), 133get_unique_id() (in module pyrevit.script), 140get_universal_data_file() (in module pyre-
vit.coreutils.appdata), 203get_universal_data_file() (in module pyrevit.script), 141get_width() (pyrevit.output.PyRevitOutputWindow
method), 218GetValueWindow (class in pyrevit.forms), 80
Hhandle_click() (pyrevit.forms.CommandSwitchWindow
method), 80handle_input_key() (pyre-
vit.forms.CommandSwitchWindow method),80
handle_input_key() (pyrevit.forms.WPFWindowmethod), 86
handle_kb_key() (pyrevit.forms.SearchPrompt method),82
has_changed (pyrevit.coreutils.FileWatcher attribute),163
has_nonprintable() (in module pyrevit.coreutils), 170hide() (pyrevit.output.PyRevitOutputWindow method),
218hide_element() (pyrevit.forms.WPFWindow static
method), 86hide_logpanel() (pyrevit.output.PyRevitOutputWindow
method), 218hide_progress() (pyrevit.output.PyRevitOutputWindow
method), 218
Iid (pyrevit._HostAppPostableCommand attribute), 61increment_str() (in module pyrevit.coreutils), 170indeterminate (pyrevit.forms.ProgressBar attribute), 81indeterminate_progress() (pyre-
vit.output.PyRevitOutputWindow method),218
inject_script() (pyrevit.output.PyRevitOutputWindowmethod), 218
inject_to_body() (pyrevit.output.PyRevitOutputWindowmethod), 219
inject_to_head() (pyrevit.output.PyRevitOutputWindowmethod), 219
insert_divider() (pyrevit.output.PyRevitOutputWindowmethod), 219
inspect_calling_scope_global_var() (in module pyre-vit.coreutils), 170
inspect_calling_scope_local_var() (in module pyre-vit.coreutils), 171
is_blank() (in module pyrevit.coreutils), 171is_box_visible_on_screens() (in module pyre-
vit.coreutils), 171is_checkbox() (pyrevit.forms.TemplateListItem class
method), 85is_data_file_available() (in module pyre-
vit.coreutils.appdata), 203is_exactly() (pyrevit._HostApplication method), 62is_file_available() (in module pyrevit.coreutils.appdata),
204is_newer_than() (pyrevit._HostApplication method), 62is_older_than() (pyrevit._HostApplication method), 62is_pyrevit_data_file() (in module pyre-
vit.coreutils.appdata), 204is_url_valid() (in module pyrevit.coreutils), 171isnumber() (in module pyrevit.coreutils.pyutils), 210
Jjoin_strings() (in module pyrevit.coreutils), 171journal_read() (in module pyrevit.script), 141journal_write() (in module pyrevit.script), 141
Kkey (pyrevit._HostAppPostableCommand attribute), 61
Llinkify() (pyrevit.output.PyRevitOutputWindow static
method), 219list_data_files() (in module pyrevit.coreutils.appdata),
204list_session_data_files() (in module pyre-
vit.coreutils.appdata), 204load_asm() (in module pyrevit.coreutils), 171load_asm_file() (in module pyrevit.coreutils), 171load_index() (in module pyrevit.script), 141lock_size() (pyrevit.output.PyRevitOutputWindow
method), 220log_error() (pyrevit.output.PyRevitOutputWindow
method), 220log_info() (pyrevit.output.PyRevitOutputWindow
method), 220log_ok() (pyrevit.output.PyRevitOutputWindow method),
220log_warning() (pyrevit.output.PyRevitOutputWindow
method), 220
Mmake_bar_chart() (pyrevit.output.PyRevitOutputWindow
method), 220
Index 241
pyRevit Documentation, Release 4.5
make_bubble_chart() (pyre-vit.output.PyRevitOutputWindow method),220
make_canonical_name() (in module pyrevit.coreutils),172
make_chart() (pyrevit.output.PyRevitOutputWindowmethod), 220
make_doughnut_chart() (pyre-vit.output.PyRevitOutputWindow method),220
make_line_chart() (pyre-vit.output.PyRevitOutputWindow method),220
make_link() (in module pyrevit.output.linkmaker), 235make_pie_chart() (pyrevit.output.PyRevitOutputWindow
method), 220make_polar_chart() (pyre-
vit.output.PyRevitOutputWindow method),220
make_radar_chart() (pyre-vit.output.PyRevitOutputWindow method),220
make_stacked_chart() (pyre-vit.output.PyRevitOutputWindow method),220
Nname (pyrevit._HostAppPostableCommand attribute), 61name (pyrevit.forms.RevisionOption attribute), 82name (pyrevit.forms.SheetOption attribute), 85name (pyrevit.forms.TemplateListItem attribute), 85name (pyrevit.forms.ViewOption attribute), 86new_uuid() (in module pyrevit.coreutils), 172next_page() (pyrevit.output.PyRevitOutputWindow
method), 220number (pyrevit.forms.SheetOption attribute), 85
Oopen_folder_in_explorer() (in module pyrevit.coreutils),
172open_page() (pyrevit.output.PyRevitOutputWindow
method), 220open_url() (in module pyrevit.script), 141open_url() (pyrevit.output.PyRevitOutputWindow
method), 220output_id (pyrevit.output.PyRevitOutputWindow at-
tribute), 220output_uniqueid (pyrevit.output.PyRevitOutputWindow
attribute), 221
Ppairwise() (in module pyrevit.coreutils.pyutils), 210pick_excel_file() (in module pyrevit.forms), 89pick_file() (in module pyrevit.forms), 89
pick_folder() (in module pyrevit.forms), 90prepare_html_str() (in module pyrevit.coreutils), 172print_code() (pyrevit.output.PyRevitOutputWindow static
method), 221print_html() (pyrevit.output.PyRevitOutputWindow static
method), 221print_md() (pyrevit.output.PyRevitOutputWindow static
method), 221print_table() (pyrevit.output.PyRevitOutputWindow
method), 221proc (pyrevit._HostApplication attribute), 62proc_id (pyrevit._HostApplication attribute), 62proc_name (pyrevit._HostApplication attribute), 62proc_path (pyrevit._HostApplication attribute), 62proc_screen (pyrevit._HostApplication attribute), 62proc_screen_scalefactor (pyrevit._HostApplication at-
tribute), 63proc_screen_workarea (pyrevit._HostApplication at-
tribute), 63process_option() (pyre-
vit.forms.CommandSwitchWindow method),80
ProgressBar (class in pyrevit.forms), 80pyrevit (module), 61pyrevit.api (module), 75pyrevit.compat (module), 77pyrevit.coreutils (module), 163pyrevit.coreutils.appdata (module), 202pyrevit.coreutils.envvars (module), 200pyrevit.coreutils.mathnet (module), 214pyrevit.coreutils.moduleutils (module), 215pyrevit.coreutils.pyutils (module), 209pyrevit.forms (module), 79pyrevit.forms.utils (module), 94pyrevit.framework (module), 133pyrevit.output (module), 217pyrevit.output.linkmaker (module), 235pyrevit.script (module), 137pyrevit.userconfig (module), 153pyrevit_command (pyrevit._ExecutorParams attribute),
64PyRevitConfig (class in pyrevit.userconfig), 154PyRevitException (class in pyrevit), 61PyRevitIOError (class in pyrevit), 61PyRevitOutputWindow (class in pyrevit.output), 217
Rrandom_alpha() (in module pyrevit.coreutils), 172random_color() (in module pyrevit.coreutils), 172random_hex_color() (in module pyrevit.coreutils), 172random_rgb_color() (in module pyrevit.coreutils), 172random_rgba_color() (in module pyrevit.coreutils), 173read_source_file() (in module pyrevit.coreutils), 173reformat_string() (in module pyrevit.coreutils), 173
242 Index
pyRevit Documentation, Release 4.5
renderer (pyrevit.output.PyRevitOutputWindow at-tribute), 222
reset() (pyrevit.forms.ProgressBar method), 81reset_config() (in module pyrevit.script), 141reset_icon() (pyrevit.output.PyRevitOutputWindow
method), 222reset_progress() (pyrevit.output.PyRevitOutputWindow
method), 222reset_stylesheet() (in module pyrevit.output), 223resize() (pyrevit.output.PyRevitOutputWindow method),
222restart() (pyrevit.coreutils.Timer method), 164result_dict (pyrevit._ExecutorParams attribute), 64reverse_dict() (in module pyrevit.coreutils), 173reverse_html() (in module pyrevit.coreutils), 174RevisionOption (class in pyrevit.forms), 82run_process() (in module pyrevit.coreutils), 174rvtobj (pyrevit._HostAppPostableCommand attribute), 61
Ssafe_cast() (in module pyrevit.coreutils.pyutils), 211SafeDict (class in pyrevit.coreutils), 163save_changes() (pyrevit.userconfig.PyRevitConfig
method), 154save_config() (in module pyrevit.script), 141save_contents() (pyrevit.output.PyRevitOutputWindow
method), 222save_excel_file() (in module pyrevit.forms), 90save_file() (in module pyrevit.forms), 90ScriptFileParser (class in pyrevit.coreutils), 164search_input (pyrevit.forms.SearchPrompt attribute), 82search_input_parts (pyrevit.forms.SearchPrompt at-
tribute), 82search_matches (pyrevit.forms.SearchPrompt attribute),
82search_term (pyrevit.forms.SearchPrompt attribute), 83search_term_args (pyrevit.forms.SearchPrompt attribute),
83search_term_main (pyrevit.forms.SearchPrompt at-
tribute), 83search_term_switches (pyrevit.forms.SearchPrompt at-
tribute), 83search_txt_changed() (pyre-
vit.forms.CommandSwitchWindow method),80
search_txt_changed() (pyrevit.forms.SearchPromptmethod), 83
search_txt_changed() (pyrevit.forms.SelectFromListmethod), 84
SearchPrompt (class in pyrevit.forms), 82select_image() (in module pyrevit.forms), 90select_open_docs() (in module pyrevit.forms), 91select_revisions() (in module pyrevit.forms), 91select_sheets() (in module pyrevit.forms), 92
select_swatch() (in module pyrevit.forms), 92select_titleblocks() (in module pyrevit.forms), 93select_views() (in module pyrevit.forms), 93SelectFromList (class in pyrevit.forms), 83self_destruct() (pyrevit.output.PyRevitOutputWindow
method), 222set_envvar() (in module pyrevit.script), 142set_font() (pyrevit.output.PyRevitOutputWindow
method), 222set_height() (pyrevit.output.PyRevitOutputWindow
method), 222set_icon() (pyrevit.output.PyRevitOutputWindow
method), 222set_image_source() (pyrevit.forms.WPFWindow
method), 86set_pyrevit_env_var() (in module pyre-
vit.coreutils.envvars), 201set_search_results() (pyrevit.forms.SearchPrompt
method), 83set_stylesheet() (in module pyrevit.output), 223set_thirdparty_ext_root_dirs() (pyre-
vit.userconfig.PyRevitConfig method), 154set_title() (pyrevit.output.PyRevitOutputWindow
method), 222set_width() (pyrevit.output.PyRevitOutputWindow
method), 222setup_icon() (pyrevit.forms.WPFWindow method), 87setup_output_closer() (in module pyrevit.output), 223SheetOption (class in pyrevit.forms), 85show() (pyrevit.forms.SearchPrompt class method), 83show() (pyrevit.forms.TemplateUserInputWindow class
method), 86show() (pyrevit.forms.WPFWindow method), 87show() (pyrevit.output.PyRevitOutputWindow method),
222show_dialog() (pyrevit.forms.WPFWindow method), 87show_element() (pyrevit.forms.WPFWindow static
method), 87show_entry_in_explorer() (in module pyrevit.coreutils),
174show_file_in_explorer() (in module pyrevit.script), 142show_logpanel() (pyrevit.output.PyRevitOutputWindow
method), 222subversion (pyrevit._HostApplication attribute), 63
TTemplateListItem (class in pyrevit.forms), 85TemplatePromptBar (class in pyrevit.forms), 85TemplateUserInputWindow (class in pyrevit.forms), 85Timer (class in pyrevit.coreutils), 164timestamp() (in module pyrevit.coreutils), 174title (pyrevit.forms.ProgressBar attribute), 81toast() (in module pyrevit.forms), 94toggle_all() (pyrevit.forms.SelectFromList method), 84
Index 243
pyRevit Documentation, Release 4.5
toggle_element() (pyrevit.forms.WPFWindow staticmethod), 87
toggle_icon() (in module pyrevit.script), 142touch() (in module pyrevit.coreutils), 174
Uuiapp (pyrevit._HostApplication attribute), 63uidoc (pyrevit._HostApplication attribute), 63unc_to_dletter() (in module pyrevit.coreutils), 175uncheck_all() (pyrevit.forms.SelectFromList method), 85uncheck_selected() (pyrevit.forms.SelectFromList
method), 85unfreeze() (pyrevit.output.PyRevitOutputWindow
method), 223unhide_progress() (pyre-
vit.output.PyRevitOutputWindow method),223
unlock_size() (pyrevit.output.PyRevitOutputWindowmethod), 223
unwrap() (pyrevit.forms.TemplateListItem method), 85update_progress() (pyrevit.forms.ProgressBar method),
81update_progress() (pyrevit.output.PyRevitOutputWindow
method), 223update_results_display() (pyrevit.forms.SearchPrompt
method), 83update_tstamp() (pyrevit.coreutils.FileWatcher method),
163update_window() (pyrevit.forms.TemplatePromptBar
method), 85username (pyrevit._HostApplication attribute), 63
Vverify_configs() (in module pyrevit.userconfig), 155verify_directory() (in module pyrevit.coreutils), 175version (pyrevit._HostApplication attribute), 63version_name (pyrevit._HostApplication attribute), 63ViewOption (class in pyrevit.forms), 86
Wwait_async() (pyrevit.forms.ProgressBar method), 81WarningBar (class in pyrevit.forms), 87window (pyrevit.output.PyRevitOutputWindow at-
tribute), 223window_handle (pyrevit._ExecutorParams attribute), 64WPFWindow (class in pyrevit.forms), 86
244 Index