Start
Your first button
Build a working Navisworks tool from an empty folder in about five minutes, then extend it with an icon, a tooltip, a keyboard shortcut and a settings dialog.
This page builds one real tool end to end. Every later page goes deeper on one part of what happens here, so it is worth typing along rather than skimming.
The tool counts what is selected in the model and reports it. Small, but it exercises the
whole path: discovery, metadata, the injected globals, the pynavis library and
the toast surface.
Before you start#
You need Navisworks with pyNavis installed, and a folder that pyNavis scans for
extensions. Check which folders those are: open
%APPDATA%\pyNavis\config.json and look at the "extensions" array.
{
"extensions": [
"D:\\NavisTools"
]
}If the file does not exist or has no "extensions" key, pyNavis is reading from
its default root, %APPDATA%\pyNavis\extensions, where the shipped extension sits.
You can either put your extension there or create the config file above. A folder you own is
easier to work in, so this guide assumes D:\NavisTools.
The command line does this for you:
pynavis extensions add D:\NavisTools. Run
pynavis extensions list to confirm what is registered.
Step 1: create the folders#
Four nested folders, and the suffixes are what matter. The names before the suffixes are yours to choose and become the labels you see.
D:\NavisTools\
MyTools.extension\
MyTools.tab\
Selection.panel\
Count.pushbutton\
script.pyCreate them in Explorer or in one line:
New-Item -ItemType Directory -Force `
D:\NavisTools\MyTools.extension\MyTools.tab\Selection.panel\Count.pushbutton.extension, .tab, .panel and
.pushbutton are matched literally, though case does not matter. A folder named
Count.pushButton works. Count.pushbuttons or
Count.button builds nothing; because it sits inside a panel, the runtime does at
least say so, in the log and in a toast. A misspelled .extension,
.tab or .panel is the genuinely silent one.
Step 2: write the script#
Create script.py inside Count.pushbutton:
"""Counts the items in the current selection."""
from pynavis import selection, toast
items = selection.get_items()
if items:
toast.success('%d item(s) selected' % len(items))
else:
toast.info('Nothing selected', 'Pick something in the model and run this again.')Three things are already happening that you did not configure:
- The button label is Count, taken from the folder name.
- The tooltip is Counts the items in the current selection., taken from the module docstring on the first line.
- The script runs on IronPython, the default engine.
Step 3: load it#
Start Navisworks, or if it is already open, click Reload on the pyNavis panel. A new MyTools tab appears with a Selection panel and a Count button on it.
Select a few objects and click the button. A toast appears in the corner of the Navisworks window with the count.
Watch for a warning toast reading N bundle folders were skipped: pyNavis shows one
after every scan that had to drop something, and quotes the first problem in full. The rest
are in %APPDATA%\pyNavis\logs\pyNavis.log. The most common causes are a typo in a
bundle suffix, a missing script.py, or an extension root that is not in
config.json. The last of those is the one that produces no toast and no log line
at all, because the scan never reaches the folder.
Troubleshooting lists every failure mode with its log
line.
Step 4: add metadata#
The folder name is a poor title once you want spaces and punctuation, and the docstring
is a poor tooltip once you want more than a sentence. Add a bundle.yaml beside
the script:
title: Count selection
tooltip: Reports how many objects are currently selected.
shortcut: Ctrl+Shift+KClick Reload. The button is now labelled Count selection, the tooltip reads better, and pressing Ctrl+Shift+K anywhere in the Navisworks main window runs it without touching the ribbon.
Metadata resolves in a fixed order, most specific first:
Step 5: add an icon#
Without an icon the button renders as text only. Drop a 96×96 PNG named
icon.png into the bundle folder and Reload.
Count.pushbutton\
script.py
bundle.yaml
icon.png # 96x96, large buttons
icon.dark.png # 96x96, dark theme
icon.small.png # 32x32, stacks and pulldown menus
icon.small.dark.png # 32x32, dark themeOnly icon.png is required; the other three fall back to it. Supplying all
four is what makes a tool look native in both themes and at both sizes.
Icons covers the drawing rules and the generator that produces the
shipped set.
Step 6: add a settings dialog#
A bundle can ship a second script called config.py. Shift-clicking the
button runs it instead of script.py, which is where a tool's options live.
"""Options for Count selection."""
from pynavis import forms, settings, toast
TOOL = 'count_selection'
DEFAULTS = {'warn_above': 1000}
values = settings.load(TOOL, DEFAULTS)
answer = forms.ask_string(
'Warn when the selection is larger than:',
default=str(values['warn_above']),
title='Count selection',
)
if answer is not None: # None means cancelled: change nothing
try:
values['warn_above'] = int(answer)
except ValueError:
toast.error('That is not a whole number')
else:
settings.save(TOOL, values)
toast.success('Saved', 'Warning above %d items.' % values['warn_above'])Now read it back in the main script:
"""Counts the items in the current selection."""
from pynavis import selection, settings, toast
TOOL = 'count_selection'
DEFAULTS = {'warn_above': 1000}
values = settings.load(TOOL, DEFAULTS)
items = selection.get_items()
if not items:
toast.info('Nothing selected', 'Pick something in the model and run this again.')
elif len(items) > values['warn_above']:
toast.warning('%d item(s) selected' % len(items), 'That is a large selection.')
else:
toast.success('%d item(s) selected' % len(items))settings.load never raises. A missing, empty or corrupt settings file yields
a fresh copy of your defaults, and keys you have since removed from DEFAULTS are
dropped rather than leaking back in.
The three click actions#
Your bundle now responds to all three, and you only wrote code for two of them:
| Action | Runs | Notes |
|---|---|---|
| Click | script.py | The primary action. |
| Shift+Click | config.py |
Same engine, same globals. Without a config.py, an informational
toast says the action is not defined. |
| Alt+Click | Nothing | Opens the bundle folder in Explorer with script.py selected. Built in,
and the fastest way back to your own code. |
Ctrl+Shift+K runs script.py, not
config.py, even though the chord contains Shift. Modifier click actions and
keyboard chords are separate mechanisms.
What you have built#
D:\NavisTools\
MyTools.extension\
MyTools.tab\
Selection.panel\
Count.pushbutton\
script.py # the primary action
config.py # Shift+Click, the settings dialog
bundle.yaml # title, tooltip, shortcut
icon.png # 96x96That is a complete pyNavis tool. Everything else in these docs is a richer way to present it on the ribbon, a script that runs itself when something happens in the model, or a larger slice of the Navisworks API to point it at.