Project > Exchange

IFC Exporter

Writes the selected objects to an IFC4 file with storeys from their levels, IFC classes from their categories, colours, GlobalIds and property sets. Shift+Click to change which IFC class a category becomes.

Tab and panelProject > Exchange
KindButton
Live whenSomething is selected

Status

Untested Never executed inside Navisworks. Passed the headless checks only.

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

None. It runs only when you click it.

Files

Paths are relative to the Playground.extension folder.

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

Install

  1. Only this tool. In Navisworks, click pyNavis > Playground, search for IFC Exporter, 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. IFC Exporter is on the Project tab, Exchange 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/Exchange.panel/IFC_Exporter.pushbutton/script.py View on GitHub
# -*- coding: utf-8 -*-
"""Writes the selected objects to an IFC4 file with storeys, classes, colours, GlobalIds and property sets."""
import os

from pynavis import doc as nwdoc
from pynavis import script, toast

from namo import config, ifc, ifcexport, items, progress, store, ui, xchg_common

NAME_TEMPLATE = '{file} - {date}'
IFC_FILTER = 'IFC files (*.ifc)|*.ifc'


def class_overrides():
    """The team's category -> IFC class overrides (Shift+Click to edit)."""
    return ifcexport.stored_overrides(store.Store('ifc-class-map', scope='global').load({}))


def run():
    if xchg_common.under_runner():
        return
    document = nwdoc.get_doc()
    selected = items.selected(document)
    if not selected:
        toast.info('Nothing selected', 'Select the objects to export, then run IFC Exporter again.')
        return
    values = dict(ifcexport.DEFAULT_OPTIONS)
    values.update(script.get_config(ifcexport.DEFAULT_OPTIONS))
    stem = os.path.splitext(xchg_common.doc_title(document))[0]
    fields = ifcexport.form_fields(values, config.get('project_name') or stem, stem)
    answer = ui.form('IFC exporter', fields, ok='Export', width=560, intro=ifcexport.FORM_INTRO)
    if answer is None:
        return
    values.update(answer)
    script.save_config(dict((k, values[k]) for k in ifcexport.DEFAULT_OPTIONS))
    path = xchg_common.ask_save(document, NAME_TEMPLATE, '.ifc', IFC_FILTER, 'Export IFC')
    if path is None:
        return
    overrides = class_overrides()
    seed = (xchg_common.doc_path(document) or xchg_common.doc_title(document)).lower()

    def work(report):
        read = ifcexport.collect(selected, document, values, progress=lambda f, label: report(f * 0.6, label))
        if not read['records']:
            return read, None, []
        report(0.62, 'Placing objects on storeys')
        model, dropped = ifcexport.model_for(read, document, values, os.path.basename(path), seed, overrides)
        if not model['elements']:
            return read, model, dropped
        ifc.write(path, model, progress=lambda f, label: report(0.65 + 0.35 * f, label))
        return read, model, dropped

    try:
        outcome = progress.run('Exporting IFC', work)
    except (IOError, OSError) as error:
        toast.error('Could not write the IFC file', '%s. Close it in any program that has it open, then try again.'
                    % error)
        return
    if outcome is progress.CANCELLED:
        return
    read, model, dropped = outcome
    xchg_common.show_skipped('IFC exporter', read['skipped'], dropped)
    written = len(model['elements']) if model else 0
    storeys = len(set(e['storey'] for e in model['elements'])) if written else 0
    level, message, detail = ifcexport.summary(written, len(read['skipped']) + len(dropped), read['sampled'],
                                               storeys)
    if written:
        detail = xchg_common.where_text(detail, path)
    toast.show(level, message, detail)
    if written:
        xchg_common.reveal(path)


run()

config.py

The Shift+Click action.

Project.tab/Exchange.panel/IFC_Exporter.pushbutton/config.py View on GitHub
# -*- coding: utf-8 -*-
"""Sets which IFC class a category becomes in IFC Exporter, for the whole team."""
from pynavis import output, toast

from namo import ifcexport, store, ui, xchg_common


def run():
    if xchg_common.under_runner():
        return
    holder = store.Store('ifc-class-map', scope='global')
    current = ifcexport.stored_overrides(holder.load({}))
    answer = ui.form('IFC classes', ifcexport.override_fields(current), ok='Save', width=600,
                     intro='These lines win over the built-in mapping for everyone who shares your '
                           'Playground team folder.')
    if answer is None:
        return
    mapping, _errors = ifcexport.parse_overrides(answer['map'])
    try:
        holder.save(dict((category, list(mapping[category])) for category in mapping))
    except (IOError, OSError) as error:
        toast.error('Could not save the IFC classes', str(error))
        return
    if answer['show']:
        output.set_title('IFC classes')
        output.print_md('### Built-in IFC classes')
        output.print_table(ifcexport.builtin_rows(), ['Category', 'IFC class', 'Predefined type'])
    if mapping:
        toast.success('Saved %d IFC class %s' % (len(mapping), 'rule' if len(mapping) == 1 else 'rules'),
                      holder.note or None)
    else:
        toast.success('Using the built-in IFC classes', holder.note or None)


run()

Library modules this tool uses

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

  • namo.configPlayground-wide settings: one JSON file shared by every Playground tool.
  • namo.ifcA pure IFC4 (ISO 10303-21 STEP) writer for the IFC Exporter, plus the reader its tests use.
  • namo.ifcexportIFC Exporter: which IFC class each object becomes, which storey it sits on, its property sets, and the walk that turns a Navisworks selection into a namo.ifc model.
  • namo.itemsModel items: walking, collecting, identifying, measuring and showing them.
  • namo.progressGlue between pyNavis' cancellable progress window and Playground's progress callbacks.
  • namo.storePer-document and global JSON stores, in one of three places.
  • namo.uiWPF building blocks for Playground tools, themed from the pyNavis design system.
  • namo.xchg_commonSmall pieces the Geometry and IFC exporters share: where files go, default names, and the list of skipped objects in the output window.

bundle.yaml

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

Project.tab/Exchange.panel/IFC_Exporter.pushbutton/bundle.yaml View on GitHub
title: IFC\nExporter
tooltip: Writes the selected objects to an IFC4 file with storeys from their levels, IFC classes from their categories, colours, GlobalIds and property sets. Shift+Click to change which IFC class a category becomes.
context: selection