Project > Field

Photo Pins

Pins site photos to a point in the model, with a caption and the level, grid and object found there, and draws the pins in the view. Click the point, choose the photos, then type a caption. Shift+Click for settings.

Tab and panelProject > Field
KindButton
Live whenA model is open

Status

Launched Ran inside Navisworks at least once with no error logged; nobody checked the result yet.

First field click found the pin landing away from the marker; refit to the host Measure pick on 2026-10-05 and not run since.

Recorded on 2026-10-05. The evidence is a line in a pyNavis log.

Related asks to confirm

No quote is on record yet. These threads look related, but a person still has to open and read them.

Events it listens to

It needs these Navisworks event hooks, so installing it also copies the shared hooks folder:

  • doc-closed
  • doc-opened

Files

Paths are relative to the Playground.extension folder.

  • Project.tab/Field.panel/Photo_Pins.pushbutton/bundle.yaml
  • Project.tab/Field.panel/Photo_Pins.pushbutton/config.py
  • Project.tab/Field.panel/Photo_Pins.pushbutton/icon.dark.png
  • Project.tab/Field.panel/Photo_Pins.pushbutton/icon.png
  • Project.tab/Field.panel/Photo_Pins.pushbutton/icon.small.dark.png
  • Project.tab/Field.panel/Photo_Pins.pushbutton/icon.small.png
  • Project.tab/Field.panel/Photo_Pins.pushbutton/script.py

Install

  1. Only this tool. In Navisworks, click pyNavis > Playground, search for Photo Pins, tick it and click Install.
  2. The whole extension. Download Playground.extension.zip, unzip it, put the Playground.extension folder in %APPDATA%\pyNavis\extensions\, and click pyNavis > Reload. Photo Pins is on the Project tab, Field panel.

You need pyNavis 1.3.0 or later.

Tried it? Tell us whether it worked or not. That report is what moves a tool up the status ladder.

Report

Source

The tool's own files, word for word as published in pynavis/Playground. Paths are relative to the Playground.extension folder.

script.py

The script that runs on a click.

Project.tab/Field.panel/Photo_Pins.pushbutton/script.py View on GitHub
# -*- coding: utf-8 -*-
"""Pins site photos to a point you click in the model, with a caption, the level, grid and object there."""
import os

from pynavis import forms, toast

from namo import camera, config as namo_config, field_photobrowser, fourd_common, items, photopins, picking, ui

TITLE = 'Photo Pins'


def linked_object(hit, doc):
    """(entries, name) of the object under the clicked point, ([], '') when there is none."""
    if hit.item is None:
        return [], ''
    try:
        element = items.object_of(hit.item)
        return items.entries([element], doc), str(element.DisplayName or '') or items.category(element)
    except Exception:
        return [], ''


def ask():
    """(photo files, caption) for the new pin, or None when the user backs out."""
    files = photopins.choose_photos('Photos to pin here')
    if not files:
        return None
    default = os.path.splitext(os.path.basename(files[0]))[0]
    caption = forms.ask_string('Caption for %s:' % fourd_common.plural(len(files), 'photo'), default=default,
                               title=TITLE)
    if caption is None:
        return None
    return files, caption


def run():
    if ui.is_runner():
        toast.info('%s needs a person at the screen' % TITLE, 'Run it from the ribbon.')
        return
    doc = fourd_common.active_doc()
    if not fourd_common.has_models(doc):
        toast.info('No model is open', 'Open or append a model first.')
        return
    hit = picking.point('Click where the photos were taken', want_item=True, doc=doc)
    if hit is None:
        return
    view_camera = camera.capture(doc)
    photopins.preview(hit.point, doc)          # the pin shows where it will land while you choose
    try:
        answer = ask()
    finally:
        photopins.clear_preview()
    if answer is None:
        return
    files, caption = answer
    level, grid = photopins.location_labels(hit.point, doc)
    item_entries, item_name = linked_object(hit, doc)
    record = photopins.new_pin(hit.point, str(doc.Units), [], caption, level, grid, item_entries, item_name,
                               view_camera, namo_config.user_name())
    records = photopins.store(doc)
    try:
        saved = photopins.add_pin(records, record, files, doc)
    except (IOError, OSError, ValueError) as error:
        toast.error('Could not pin the photos', str(error))
        return
    place = ', '.join(p for p in (level, 'grid %s' % grid if grid else '', item_name) if p)
    detail = (place + '. ' if place else '')
    shown = photopins.settings().get('show_pins', True)
    drawn = photopins.refresh(doc)
    browser_open = field_photobrowser.refresh_open(records.list())
    if not shown:
        detail += 'Pins are hidden: Shift+Click Photo Pins to show them.'
    elif not drawn:
        detail += 'The pin is saved but could not be drawn in the view. Photo Browser lists it.'
    elif not browser_open:
        detail += 'Open Photo Browser to see them.'
    if records.note:
        detail = records.note + ' ' + detail
    toast.success('Pinned %s as %s' % (fourd_common.plural(len(saved['photos']), 'photo'), saved['id']),
                  detail.strip() or None)


run()

config.py

The Shift+Click action.

Project.tab/Field.panel/Photo_Pins.pushbutton/config.py View on GitHub
# -*- coding: utf-8 -*-
"""Shows or hides the photo pins in the view and picks their size and what their labels say."""
from pynavis import toast

from namo import fourd_common, photopins, ui


def run():
    if ui.is_runner():
        return
    values = photopins.settings()
    fields = [
        {'kind': 'check', 'key': 'show_pins', 'label': 'Show photo pins in the view',
         'default': bool(values.get('show_pins', True)),
         'hint': 'Pins are redrawn whenever a model with pins opens.'},
        {'kind': 'radio', 'key': 'label', 'label': 'Pin labels show', 'inline': True,
         'options': [('Caption', 'caption'), ('Pin number', 'id')], 'default': values.get('label', 'caption'), 'fallback': 'caption'},
        {'kind': 'radio', 'key': 'size', 'label': 'Pin size', 'inline': True,
         'options': [(label, key) for key, label, _metres in photopins.PIN_SIZES],
         'default': values.get('size', 'medium'), 'fallback': 'medium',
         'hint': 'The marker is 0.2 m across when small, 0.4 m medium, 1 m large.'},
    ]
    answer = ui.form('Photo Pins settings', fields, ok='Save')
    if answer is None:
        return
    photopins.save_settings({'show_pins': bool(answer['show_pins']), 'label': answer['label'],
                             'size': answer['size']})
    doc = fourd_common.active_doc()
    if not answer['show_pins']:
        photopins.clear_drawn()
        toast.success('Photo pins hidden', 'They stay saved, and Photo Browser still lists them.')
        return
    if not fourd_common.has_models(doc):
        toast.success('Photo pins will show', 'Open a model with pins to see them.')
        return
    total = len(photopins.store(doc).list())
    drawn = photopins.refresh(doc)
    if total and not drawn:
        toast.warning('Photo pins could not be drawn', 'The view overlay is not loaded. Restart Navisworks, '
                      'then try again.')
    elif not total:
        toast.success('Photo pins shown', 'This model has no pins yet. Click Photo Pins to add one.')
    else:
        toast.success('Photo pins shown', '%s in this model.' % fourd_common.plural(drawn, 'pin'))


run()

Library modules this tool uses

From the shared namo library, which installs with every tool. Each name opens the module on GitHub.

  • namo.cameraThe Navisworks camera, saved viewpoints and section planes, as plain data.
  • namo.configPlayground-wide settings: one JSON file shared by every Playground tool.
  • namo.field_photobrowserPhoto Browser: a modeless window of photo-pin thumbnails grouped by level, grid or linked object, with a filter, Go to, Open photo, Delete pin and an HTML photo report.
  • namo.fourd_commonHelpers the 4D, Quantities and Field tools share: scopes, default files, text.
  • namo.itemsModel items: walking, collecting, identifying, measuring and showing them.
  • namo.photopinsPhoto Pins and the Photo Browser: site photos pinned to points in the model.
  • namo.pickingThe one way a Playground tool asks for a click in the 3D view.
  • namo.uiWPF building blocks for Playground tools, themed from the pyNavis design system.

bundle.yaml

The bundle contract: the keys pyNavis reads for this tool.

Project.tab/Field.panel/Photo_Pins.pushbutton/bundle.yaml View on GitHub
title: Photo\nPins
tooltip: Pins site photos to a point in the model, with a caption and the level, grid and object found there, and draws the pins in the view. Click the point, choose the photos, then type a caption. Shift+Click for settings.
context: doc