Support Forum

[Frage] [PythonParts] Create Wall with GeneralOpeningElement in the same PythonPart

Schlagworte:
  • PythonParts
  • Wall
  • GeneralOpeningElement
  • PythonPartUtil
  • Question

Hello,

I am trying to create a standard PythonPart with a Wall in it and a GeneralOpeningElement related to this Wall. The idea is to configure the Wall through the Palette as well as an Opening for the Wall. The final result of the PythonPart execution is going to return a PythonPart created with PythonPartUtils.
I've managed to create one standard PythonPart (PythonPartUtil) with a Wall in it, some 3D views, and an Opening related to an existing Wall created outside the PythonPart, however, when trying to relate the Opening to the Wall that's being created inside the PythonPartUtil, I get an Exception:

Exception nr C0000005 -access violation is occured at 00007FFA39E675B3, while reading from address 8

Is there any way to achieve what I want? Maybe the Wall must be already created in Allplan to be able to be added to the GeneralOpeningElement as the general element (parent)?

Below is a simplification of the code I am using to do so:

def create_element(build_ele, doc):
    build_ele.zUnique.value = random.random() * 3600

    common_props = AllplanBaseElements.CommonProperties()
    common_props.GetGlobalProperties()

    wall = Wall_PP(build_ele.zUnique.value, build_ele.llarg.value, build_ele.ample.value, build_ele.axis_x.value, build_ele.axis_y.value)

    python_part_util = PythonPartUtil(common_props)

    cube_element = wall.get_cuboid()
    python_part_util.add_pythonpart_view_2d3d(cube_element)

    wall_element = wall.wall_element()
    python_part_util.add_architecture_elements([wall_element])

    opening_prop = AllplanArchElements.GeneralOpeningProperties(AllplanArchElements.OpeningType.eNiche)

    opening_prop.VisibleInViewSection3D   = True
    opening_prop.Independent2DInteraction = False
    opening_prop.PlaneReferences          = AllplanArchElements.PlaneReferences(doc, wall_element.GetBaseElementAdapter())
    opening_prop.PlaneReferences.Height = 1010
    opening_prop.PlaneReferences.BottomElevation = 1000

    custom_create_opening_geo_properties(opening_prop.GetGeometryProperties())

    custom_create_sill_properties(opening_prop.GetSillProperties())

    custom_create_opening_symbols_properties(opening_prop.GetOpeningSymbolsProperties())

    opening_start_pnt = AllplanGeometry.Point3D(0,0,0)

    opening_end_pnt = OpeningPointsUtil.OpeningPointsUtil.create_opening_end_point_for_shaped_element(opening_start_pnt.To2D,
                                                                                                500.,
                                                                                                wall.get_wall_axis()).To3D

    opening_ele = AllplanArchElements.GeneralOpeningElement(opening_prop, wall_element.GetBaseElementAdapter(),
                                                        opening_start_pnt.To2D,
                                                        opening_end_pnt.To2D,
                                                        build_ele.INPUT_MODE.value == 1)

    python_part_util.add_architecture_elements([opening_ele])

    return CreateElementResult(python_part_util.create_pythonpart(build_ele,
                                                                    type_display_name = "PythonPart with sub objects"))

Thanks

Hilfreichste Antwort anzeigen Hilfreichste Antwort verbergen

Hello,

the problem here is, that to create an opening in a wall, the wall must already be created in the drawing file. In other words, you need to perform two transactions with the database: first for creating the wall, the second for creating the opening.

This is because the opening element needs the BaseElementAdapter of the wall. But you can only get it, when the wall is created in the drawing file. In your example, the call `wall_element.GetBaseElementAdapter()` is causing memory access violation, because wall_element does not yet exist in the DF, so it's not possible to get the adapter to it.

When you create a standard PythonPart, like in your example, only one transaction is happening. The elements you put into the CreateElementResult dataclass will be created all at once when the PythonPart is placed and terminated. This is how a Standard PythonPart works and you cannot do anything about it.

For your task, you would need to build an interactor and implement two transactions (PythonPartTransaction):

  • create the PythonPart with the walls. Executing the transaction gives you the valid BaseElementAdapters (see the return value of PythonPartTransaction.execute() is a list of adapters)
  • Create openings, using the appropriate adapters from the first step
Quite challenging task, but there is no other way to do it. This is how architectural elements and the openings are handled in ALLPLAN in general. There is no native function in ALLPLAN, that would create both at once. You can only create one after another.

Hope that helps at least somehow.

Best,
Bart

Hello,

the problem here is, that to create an opening in a wall, the wall must already be created in the drawing file. In other words, you need to perform two transactions with the database: first for creating the wall, the second for creating the opening.

This is because the opening element needs the BaseElementAdapter of the wall. But you can only get it, when the wall is created in the drawing file. In your example, the call `wall_element.GetBaseElementAdapter()` is causing memory access violation, because wall_element does not yet exist in the DF, so it's not possible to get the adapter to it.

When you create a standard PythonPart, like in your example, only one transaction is happening. The elements you put into the CreateElementResult dataclass will be created all at once when the PythonPart is placed and terminated. This is how a Standard PythonPart works and you cannot do anything about it.

For your task, you would need to build an interactor and implement two transactions (PythonPartTransaction):

  • create the PythonPart with the walls. Executing the transaction gives you the valid BaseElementAdapters (see the return value of PythonPartTransaction.execute() is a list of adapters)
  • Create openings, using the appropriate adapters from the first step
Quite challenging task, but there is no other way to do it. This is how architectural elements and the openings are handled in ALLPLAN in general. There is no native function in ALLPLAN, that would create both at once. You can only create one after another.

Hope that helps at least somehow.

Best,
Bart

Hi bmarciniec,

Thanks for your reply.

The idea behind having everything in a PythonPart is to enable the creation and modification of the Walls based on interactions with other 3D elements and user inputs, while also creating or modifying the Openings depending on these interactions. As part of the workflow, it is necessary to be able to re-enter the PythonPart, update certain elements, and perform actions that will automatically update the other elements, including the Walls and the Openings.

Implementing your proposal in an Interactor PythonPart will create two independent elements that won't be editable together if they are created as simple objects in the PythonPartTransaction.execute(). However, is it possible to create the Opening using the gathered BaseElementAdapter of the Wall while still utilizing the original Wall element (AllplanArchElements.WallElement()) for other purposes (such as creating a PythonPart that includes both the Wall and the Opening) or will that Wall and the Wall in the DF no longer be connected?

Thanks,
Arnau

Hi Arnau

Zitiert von: arnaunadal
Implementing your proposal in an Interactor PythonPart will create two independent elements that won't be editable together if they are created as simple objects in the PythonPartTransaction.execute().

Not exactly true. With

python_part_util.add_architecture_elements([wall_element])
you are attaching the wall to the PythonPart. After they are created, they will have a parent-child relationship (PythonPart being the parent).

Creating an opening in the wall in the second step would also result in the same relationship between the opening and the wall (wall being the parent). So as a result you will have 3 elements, but all connected with each other.

Modifying the python part (by double click) basically deletes the PythonPart and recreates it again with the script. Also child and grandchild elements are deleted. If your script then recreates all 3 elements, you have one seamless workflow of modifying one PythonPart

Zitiert von: arnaunadal
As part of the workflow, it is necessary to be able to re-enter the PythonPart, update certain elements, and perform actions that will automatically update the other elements, including the Walls and the Openings.

Depending on what these objects are (whether they are modifyable using Python API, which you can find out in this article), this can be achieved also with a different approach. Maybe you can create independent elements, but only save the UUIDs of the other elements in the PythonPart (as a list of strings in a hidden parameter), so that when the PythonPart is re-entered, these elements are modified accordingly? This approach may be valid, but not necessarily have to be. I am just throwing ideas to widen the scope of possible solutions.

Best,
Bart

Hi bmarciniec,

Thanks for your proposals and sorry for the late response.

I've been trying your first solution:

Zitiert von: bmarciniec
With python_part_util.add_architecture_elements([wall_element]) you are attaching the wall to the PythonPart. After they are created, they will have a parent-child relationship (PythonPart being the parent).Creating an opening in the wall in the second step would also result in the same relationship between the opening and the wall (wall being the parent). So as a result you will have 3 elements, but all connected with each other.
Modifying the python part (by double click) basically deletes the PythonPart and recreates it again with the script. Also child and grandchild elements are deleted. If your script then recreates all 3 elements, you have one seamless workflow of modifying one PythonPart

I managed to create a PythonPart with a Wall in it through the PythonPartTransaction.execute() which can be entered, modified and recreated as you mentioned.

However, when trying to attach an Opening to it from any other PythonPart process, Allplan keeps crashing with the same error. Not only when created from the same interactor PythonPart as the one that creates my Wall, but also when trying to execute the Allplan example PolygonalGeneralOpening.py, which creates an Opening to an existent Wall, when doing it for my Wall in the PtyhonPart, the wall is detected properly (I print the selected element and it is a "Wall, Straight") but Allplan crashes when calling

opening_ele = AllplanArchEle.GeneralOpeningElement(opening_prop, self.general_ele, self.opening_polygon, False)

I hope the Wall is properly created since Allplan has no problems managing it as a normal Wall although being in a PythonPart, even being able to create Openings manually.

This is the code to create the Wall:

self.wall_axis = AllplanGeometry.Line2D(
   position.To2D,
   position.To2D + AllplanGeometry.Point2D(self.x, self.y),
)

wall_prop = AllplanArchElements.WallProperties()
wall_axis_prop = AllplanArchElements.AxisProperties()

wall_axis_prop.Extension = -1
wall_axis_prop.Position = (
    AllplanArchElements.WallAxisPosition.eFree
)

wall_prop.SetAxis(wall_axis_prop)
wall_prop.SetTierCount(1)
tier_count = wall_prop.GetTierCount()

for tier_index in range(tier_count):
    wall_tier_prop = wall_prop.GetWallTierProperties(tier_index + 1)
    wall_tier_prop.SetThickness(thickness)

self.wall_prop = wall_prop

return AllplanArchElements.WallElement(self.wall_prop, self.wall_axis)

Ok, so I tried it out and I can create an opening (with a PythonPart) in a wall being part of a PythonPart (I used the examples PythonPart with Sub Elements and General Opening). But I cannot modify the PythonPart like this. Seems, that we need to solve this problem first. Until this, your goal cannot be accomplished, because you want a modifiable PythonPart consisting of walls and openings.

Best,
Bart

Hi,

You are right, using the General Opening example I can create the opening for both the PythonPart with Sub Elements and my Wall PythonPart. However, using the example Polygonal General Opening Allplan crashes in both cases. Since I am creating the opening in the same way it is created in the Polygonal General Opening I have the same problem this example might have.

For my Wall PythonPart, since it is created with the execute() method you recommended to me, I can enter the PythonPart and create a new Wall even after adding an opening with General Opening, the opening being expectedly lost because the Wall delete and recreation. So, to create the opening from inside the same PythonPart interactor as the Wall, maybe I can proceed if I mimic the behavior of the General Opening.

I understand the openings' functionality is under revision since it's something new added in Allplan 2025, so I'll wait for updates and try to get something working meanwhile.

Thanks for your help.
Regards,
Arnau