Skip to content

Events

2027-ALPHA-1

In addition to allowing users to actively invoke Python code by starting a PythonPart, ALLPLAN also enables passive invocation in response to specific events. These events include:

  • Starting or closing ALLPLAN
  • Starting or closing a project
  • Loading or saving a drawing file
  • Making changes to the current document, such as creating a new wall

ALLPLAN allows you to hook into these events using event hooks. These hooks enable you to execute Python code either before or after an event occurs, as indicated by the event type name (Pre or Post). The complete list of events you can hook into is defined in the enumeration class PythonEventHooks.

Entry Point

The entry point for an event hook is a Python function defined as follows:

def execute_event(event_id: AllplanSettings.PythonEventHooks,
                  *args: Any):
    """Event hook with the given ID and arguments

    Args:
        event_id: The event ID
        *args:    A list of arguments for the event
    """
    # Your code goes here

Define this function in a Python module named AllplanEventHooks.py. Save the file in one of the following locations:

  • ...usr\{user name}\PythonPartsScripts
  • ...prj\{project name}\PythonPartsScripts
  • ...std\PythonPartsScripts

Info

  • You can save the AllplanEventHooks.py file directly in these locations or in a subdirectory.
  • Multiple AllplanEventHooks.py files may exist in various locations. The first file loaded by Python will be executed first, but the order cannot be controlled.

Example

Here’s a simple implementation of a logger that logs activity to a TXT file located at ...usr\{user name}\logfile.txt:

from datetime import datetime
from pathlib import Path
from typing import Any

import NemAll_Python_AllplanSettings as AllplanSettings
from FileNameService import FileNameService

def execute_event(event_id: AllplanSettings.PythonEventHooks,
                  *args: Any):
    """Execute the event with the given ID and arguments

    Args:
        event_id: The event ID
        *args:    A list of arguments for the event
    """
    log_file_path = Path(FileNameService.get_global_standard_path("usr\\logfile.txt"))

    msg_map = {
        AllplanSettings.PythonEventHooks.ePostAllplanStart: "ALLPLAN Started",
        AllplanSettings.PythonEventHooks.ePreAllplanClose: "ALLPLAN Closed",
        AllplanSettings.PythonEventHooks.ePostDrawingFilesLoad: "Drawing files loaded",
        AllplanSettings.PythonEventHooks.ePreDrawingFilesSave: "Drawing files saved",
        AllplanSettings.PythonEventHooks.ePreProjectClose: "Project closing",
        AllplanSettings.PythonEventHooks.ePostProjectLoad: "Project loaded",
        AllplanSettings.PythonEventHooks.ePostDocumentUpdate: "Document updated",
    }

    with log_file_path.open("a", encoding="utf-8") as log_file:
        msg = f"{datetime.now()}: {msg_map.get(event_id, 'Unknown event')}\n"
        log_file.write(msg)

To try it out, copy the code and save it in an AllplanEventHooks.py file in one of the locations mentioned above. Then start ALLPLAN. The logfile.txt file will be created in your USR location (if it doesn’t already exist), and all activity will be logged there.

In addition to the event_id parameter, you receive other input arguments as *args. These vary depending on the event type. For example:

Event Input Arguments
ALLPLAN started Current document (as DocumentAdapter)
Drawing file saved Current document
Document changed Current document; list of created/modified elements (as BaseElementAdapterList); list of deleted elements

Warning

Be aware of the following limitations for the document update event:

  • It is not possible to distinguish between an element being created or modified; both are included in the same list.
  • When an element is deleted, the event is not triggered at that exact moment but shortly before the subsequent event.

Multithreading

The code you define in a hook is executed synchronously within the ALLPLAN application. This means ALLPLAN waits for your code to finish before proceeding. If your code involves expensive operations, this may cause ALLPLAN to become temporarily unresponsive.

If your expensive operation does not require the ALLPLAN application thread, you can use Python’s multithreading or multiprocessing features. For example, sending an HTTP request or writing a large amount of data to a file can be moved to a separate thread. However, if your operation involves creating or modifying elements in the ALLPLAN model or interacting with the ALLPLAN application, DO NOT move it to a separate thread.

Example

In this example, the expensive_operation() function takes a while to execute.

This implementation runs the operation on a separate thread, allowing the user to continue working after saving the drawing file:

import time
import threading
from typing import Any

import NemAll_Python_AllplanSettings as AllplanSettings

def execute_event(event_id: AllplanSettings.PythonEventHooks,
                  *args: Any):

    if event_id == AllplanSettings.PythonEventHooks.ePreDrawingFilesSave:
        thread = threading.Thread(target=expensive_operation, daemon=False)  #(1)!
        thread.start()

def expensive_operation():
    """Simulate an expensive operation"""
    time.sleep(5)  # Simulate a delay
    print()
    print("#############    Expensive operation completed    #############")
    print()
  1. A non-daemon thread prevents ALLPLAN from exiting while the thread is still running but does not block the UI.

This implementation runs the operation synchronously, causing ALLPLAN to become unresponsive for 5 seconds after saving the drawing file:

import time
from typing import Any

import NemAll_Python_AllplanSettings as AllplanSettings

def execute_event(event_id: AllplanSettings.PythonEventHooks,
                  *args: Any):

    if event_id == AllplanSettings.PythonEventHooks.ePreDrawingFilesSave:
        expensive_operation()

def expensive_operation():
    """Simulate an expensive operation"""
    time.sleep(5)  # Simulate a delay
    print()
    print("#############    Expensive operation completed    #############")
    print()

Try both code snippets to observe the difference in behavior after saving a drawing file.

Placeholder