Interactor PythonPart
In an interactor PythonPart, you implement the entire workflow. This is done by building a class with certain methods. When your PythonPart is run, the framework will call these methods, depending on the action taken by the user. Your interactor will then handle these actions. So the user acts, your PythonPart reacts - an interaction is established.
Because with an interactor Python you can precisely react to almost any action taken by the user in ALLPLAN UI (you will handle literally each mouse movement), it allows you to build freely any workflow. E.g. you can include steps like element selection or point input and this in any order you want. Creation of the element can be done, unlike in Standard PythonPart, in any moment of the workflow and more than one time. It is also possible to modify existing elements including the deletion.
With an interactor PythonPart, you can truly create an entire functionality, that does nor yet exists in ALLPLAN.
Implementation
First steps
First, tell the PythonParts framework, that the script implementation is done as an interactor
by setting the <Interactor>
tag to True in the PYP file, in the <Script>
section:
Now, implement following two functions in the PY file:
-
This function is called at the very beginning to check, whether the PythonPart can be started in the current ALLPLAN version. Implementation is exactly the same, as in the Standard PythonPart
-
This function is called directly after the version check. It should only construct and return your Interactor object.
create_interactor
create_interactor(
coord_input: CoordinateInput,
pyp_path: str,
global_str_table_service: StringTableService,
build_ele_list: list[BuildingElement],
build_ele_composite: BuildingElementComposite,
control_props_list: list[BuildingElementControlProperties],
modify_uuid_list: list[str],
) -> Interactor
Parameters:
-
coord_input
(CoordinateInput
) –coordinate input
-
pyp_path
(str
) –path of the pyp file
-
global_str_table_service
(StringTableService
) –global string table service for default strings
-
build_ele_list
(list[BuildingElement]
) –list with the building elements containing parameter properties
-
build_ele_composite
(BuildingElementComposite
) –building element composite
-
control_props_list
(list[BuildingElementControlProperties]
) –control properties list
-
modify_uuid_list
(list[str]
) –UUIDs of the existing elements in the modification mode
Returns:
-
Interactor
–Created interactor object
Source code in src\PythonPartsScriptTemplates\InteractorPythonPart.py
Example
def create_interactor(coord_input: AllplanIFWInput.CoordinateInput,
pyp_path: str,
global_str_table_service: StringTableService,
build_ele_list: list[BuildingElement],
build_ele_composite: BuildingElementComposite,
control_props_list: list[BuildingElementControlProperties],
modify_uuid_list: list[str]):
return MyInteractor(coord_input, pyp_path, global_str_table_service, build_ele_list, #(1)!
build_ele_composite, control_props_list, modify_uuid_list)
- You implement the
MyInteractor
class. The name is exemplary: name it as you want it. Scroll down below to learn, how to implement it.
Warning
In some older examples you may still encounter the following implementation:
def create_interactor(coord_input: AllplanIFWInput.CoordinateInput,
pyp_path: str,
global_str_table_service: StringTableService) -> object:
return MyInteractor(...)
This signature is soft-deprecated. We recommend using the new one, with 7 arguments.
Interactor class
The interactor object is basically an event handler: its methods handle events triggered by actions done in ALLPLAN UI. In other words: PythonParts framework calls these methods during the runtime of the PythonPart, depending on what action is taken by the user.
Some of these methods are mandatory to implement, some optional. To ensure that your interactor class fulfill the contract properly, use the BaseInteractor as base class:
The BaseInteractor class contains all the methods, that framework will look for. The mandatory ones are implemented as abstract methods, and the optional ones as stub methods. You should therefore always override these methods in your implementation.
Events
What methods of the interactor are called, depends on the action done by the user in ALLPLAN UI. The general script flow is as follows:
-
The PythonPart is started. The interactor object is constructed, i.e. the
__init__()
is called. Therefore, it is a good practice to implement here these actions, that must be done only once, at the beginning. -
During the runtime of the PythonPart, actions taken by the user triggers method calls. See table below for some common actions and the method, that gets called.
Action Called method Remarks Mouse is moved inside the viewport process_mouse_msg calling IsMouseMove will return True
Left mouse button is clicked inside the viewport process_mouse_msg calling IsMouseMove will return False
Mouse leaves the viewport on_mouse_leave Input in the property palette is done modify_element_property confirming the input with Enter triggers the event; name and new value is passed to the function as argument Input in the dialog line1 is done on_preview_draw any change triggers the event, pressing Enter is not necessary Enter is hit during the input in the dialog line on_value_input_control_enter A button in the property palette is pressed on_control_event ID of the pressed button is passed to the function as argument Property palette tab is changed set_active_palette_page_index Restore basic settings button is hit in the property palette reset_param_values -
When the user hits Esc, the on_cancel_function is called. Depending on the returned value the PythonPart is either terminated (
True
) or continue running (False
).Tip
If your PythonPart consists of multiple input steps, you can handle the event of hitting the Esc as a backward step to the previous input. When the user hit Esc so many times, that the first input step is reached, hitting Esc one more time should terminate the PythonPart. This is a common practice for many native ALLPLAN function and we recommend to align with it.
Here is how the implementation can look like for a two-step workflow, where the user must input first point in the first step, and another point in the second step:
def on_cancel_function(self): if self.first_point_input: #(1)! palette_service.close_palette() return True self.first_point_input = True #(2)! coord_input.InitFirstPointInput(AllplanIFW.InputStringConvert("From point")) return False #(3)!
- When hitting Esc while the first point input is active, the PythonPart should shut down completely...
- Hitting Esc during the second point input should only switch to the first point input.
- The False value is returned here to communicate to the PythonPart framework, not to terminate the running PythonPart.
-
When the user starts another ALLPLAN function during the runtime of the PythonPart, the on_cancel_by_menu_function is called. Then, the PythonPart is terminated. Always.
- After calling this function and completing the procedures in it, PythonPart framework will always shut down the PythonPart. A return value is not needed.
Info
When on_cancel_by_menu_function is not implemented, the framework will call the
on_cancel_function
over and over, until the returned value is True. This multiple calls may cause the palette to blink. If your implementation of on_cancel_function always returns True, this won't be a problem. Otherwise, we recommend implementing the on_cancel_by_menu_function
The following flow chart shows the workflow described above. The user action is shown symbolically as action
and the method called by it as interactor_method()
, as showing all of them on the graph would make it hard
to read.
graph TB
subgraph runtime ["PythonPart runtime"]
m["interactor_method()"]:::meth --> A{{action}}:::action
A --> m
end
START{{PythonPart start}}:::action --> init["__init__()"]:::meth
init --> runtime
runtime -- "esc pressed" --> on_cancel_function["on_cancel_function()"]:::meth
runtime -- "another function started" --> on_cancel_by_menu_function["on_cancel_by_menu_function()"]:::meth
on_cancel_function -- "False" --> runtime
on_cancel_function -- "True" --> Z{{PythonPart termination}}:::action
on_cancel_by_menu_function --> Z
classDef meth stroke:#f00
classDef action stroke:#0f0
Example on GitHub
To see, what event is triggered in which situation, run the example Events ( PYP | PY) in ALLPLAN. Then take any action in GUI, like move the mouse or input something in the property palette. The triggered event will be printed in the ALLPLAN trace window.
Optional functions
To create elements, which should be displayed in the ALLPLAN library as a preview, implement the function create_preview into your script. See the chapter Library preview to learn more.
create_preview
create_preview(
build_ele: BuildingElement, doc: DocumentAdapter
) -> CreateElementResult
Parameters:
-
build_ele
(BuildingElement
) –building element with the parameter properties
-
doc
(DocumentAdapter
) –document of the Allplan drawing files
Returns:
-
CreateElementResult
–created element result
Source code in src\PythonPartsScriptTemplates\InteractorPythonPart.py
-
Dialog line is the input toolbar, by default shown at the bottom left corner of ALLPLAN UI, where the user can input XYZ coordinates.