multiconn_archicad 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. multiconn_archicad-0.2.0/LICENSE +21 -0
  2. multiconn_archicad-0.2.0/PKG-INFO +249 -0
  3. multiconn_archicad-0.2.0/README.md +234 -0
  4. multiconn_archicad-0.2.0/pyproject.toml +54 -0
  5. multiconn_archicad-0.2.0/src/multiconn_archicad/__init__.py +43 -0
  6. multiconn_archicad-0.2.0/src/multiconn_archicad/actions/__init__.py +12 -0
  7. multiconn_archicad-0.2.0/src/multiconn_archicad/actions/connection_manager.py +59 -0
  8. multiconn_archicad-0.2.0/src/multiconn_archicad/actions/project_handler.py +92 -0
  9. multiconn_archicad-0.2.0/src/multiconn_archicad/actions/refresh.py +43 -0
  10. multiconn_archicad-0.2.0/src/multiconn_archicad/basic_types.py +223 -0
  11. multiconn_archicad-0.2.0/src/multiconn_archicad/conn_header.py +124 -0
  12. multiconn_archicad-0.2.0/src/multiconn_archicad/core_commands.py +49 -0
  13. multiconn_archicad-0.2.0/src/multiconn_archicad/dialog_handlers/__init__.py +11 -0
  14. multiconn_archicad-0.2.0/src/multiconn_archicad/dialog_handlers/dialog_handler_base.py +22 -0
  15. multiconn_archicad-0.2.0/src/multiconn_archicad/dialog_handlers/win_dialog_handler.py +93 -0
  16. multiconn_archicad-0.2.0/src/multiconn_archicad/dialog_handlers/win_int_handler_factory.py +64 -0
  17. multiconn_archicad-0.2.0/src/multiconn_archicad/errors.py +16 -0
  18. multiconn_archicad-0.2.0/src/multiconn_archicad/multi_conn.py +165 -0
  19. multiconn_archicad-0.2.0/src/multiconn_archicad/py.typed +0 -0
  20. multiconn_archicad-0.2.0/src/multiconn_archicad/standard_connection.py +36 -0
  21. multiconn_archicad-0.2.0/src/multiconn_archicad/utilities/__init__.py +0 -0
  22. multiconn_archicad-0.2.0/src/multiconn_archicad/utilities/async_utils.py +42 -0
  23. multiconn_archicad-0.2.0/src/multiconn_archicad/utilities/platform_utils.py +30 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Szamosi Máté
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: multiconn_archicad
3
+ Version: 0.2.0
4
+ Summary: MultiConn ArchiCAD is a connection object for ArchiCAD’s JSON API and its Python wrapper, designed to manage multiple open instances of ArchiCAD simultaneously.
5
+ Author-email: SzamosiMate <szamimate@yahoo.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: aiohttp>=3.11.11
9
+ Requires-Dist: archicad>=28.3000
10
+ Requires-Dist: psutil>=6.1.1
11
+ Requires-Dist: pywinauto>=0.6.9
12
+ Provides-Extra: dialog-handlers
13
+ Requires-Dist: pywinauto>=0.6.9; extra == 'dialog-handlers'
14
+ Description-Content-Type: text/markdown
15
+
16
+ <img src="https://github.com/user-attachments/assets/370ad589-117c-44cb-9a4a-80bfcb734445" width="400px" />
17
+
18
+ ##
19
+
20
+ **MultiConn ArchiCAD** is a Python-based connection object for ArchiCAD's JSON API and its Python wrapper. It is designed to manage multiple open instances of Archicad simultaneously, making it easier to execute commands across multiple instances.
21
+
22
+ [![Latest Release](https://img.shields.io/github/v/release/SzamosiMate/multiconn_archicad)](https://github.com/SzamosiMate/multiconn_archicad/releases/latest)
23
+ ![License](https://img.shields.io/github/license/SzamosiMate/multiconn_archicad)
24
+ ![Issues](https://img.shields.io/github/issues/SzamosiMate/multiconn_archicad)
25
+ ![Forks](https://img.shields.io/github/forks/SzamosiMate/multiconn_archicad)
26
+ ![Stars](https://img.shields.io/github/stars/SzamosiMate/multiconn_archicad)
27
+
28
+ ## Features
29
+
30
+ - **Multi-connection Support**: Connect to one, multiple, or all open instances of Archicad.
31
+ - **Seamless Integration**: Utilizes ArchiCAD's official Python package.
32
+ - **Tapir Add-On Support**: Run commands from the Tapir Archicad Add-On.
33
+ - **Efficient I/O Operations**: Handles connection management using concurrent or asynchronous code.
34
+ - **Project Management**: Find and open ArchiCAD projects programmatically.
35
+
36
+ ## Installation
37
+
38
+ You can install the latest version of the package from the following link using `pip`:
39
+
40
+ ```bash
41
+ pip install https://github.com/SzamosiMate/multiconn_archicad/releases/latest/download/multiconn_archicad-0.1.2-py3-none-any.whl
42
+ ```
43
+
44
+ The package depends on the [Tapir Archicad Add-On](https://github.com/ENZYME-APD/tapir-archicad-automation?tab=readme-ov-file). It is recommended to install the latest version of Tapir to access all features. While some functionality may work without the add-on, all tests have been conducted with it installed.
45
+
46
+ ## Usage
47
+
48
+ **Disclaimer:** The connection object is functional but in the early stages of development. It is not thoroughly tested, and its interfaces may change in future updates.
49
+
50
+ ### Actions - managing the connection
51
+ Actions allow you to manage the state of the connection object. You can connect to or disconnect from Archicad instances, quit instances, or refresh ports. All actions can have multiple types of inputs. For each type of input you have to call the corresponding method of the action. To connect to all available ArchiCAD instances, you have to call the .all() method on .connect ( e.g. `conn.connect.all()`). The aim of this method is to provide better autocompletion.
52
+
53
+ #### Example: Connection Management
54
+ ```python
55
+ from multiconn_archicad import MultiConn, Port
56
+
57
+ conn = MultiConn()
58
+
59
+ # connect all ArchiCAD instances that were running at instantiation / the last refresh
60
+ conn.connect.all()
61
+
62
+ # disconnect from the instance at port 19723
63
+ conn.disconnect.from_ports(Port(19723))
64
+
65
+ # refresh all closed ports - ports with no running archicad instance
66
+ conn.refresh.closed_ports()
67
+
68
+ # close, and remove from the dict of open port headers the archicad instance specified by ConnHeader
69
+ conn.quit.from_headers(conn.open_port_headers[Port(19735)])
70
+ ```
71
+
72
+ ### Project Management
73
+
74
+ The MultiConn object provides actions to find and open ArchiCAD projects programmatically.
75
+
76
+ #### Finding ArchiCAD Instances
77
+
78
+ You can use the `find_archicad` action to locate a specific ArchiCAD instance from a `ConnHeader`.
79
+
80
+ ```python
81
+ from multiconn_archicad import MultiConn, ConnHeader
82
+
83
+ conn = MultiConn()
84
+ conn_header = ConnHeader(Port(19723))
85
+
86
+ # Find the port for a specific connection header
87
+ port = conn.find_archicad.from_header(conn_header)
88
+ if port:
89
+ print(f"Found ArchiCAD instance at port: {port}")
90
+ ```
91
+
92
+ #### Opening Projects
93
+
94
+ The `open_project` action allows you to programmatically open ArchiCAD projects.
95
+
96
+ ```python
97
+ from multiconn_archicad import MultiConn, ConnHeader, TeamworkCredentials
98
+
99
+ conn = MultiConn()
100
+
101
+ # Open a project using a connection header
102
+ conn_header = ConnHeader.from_dict(saved_header_data)
103
+ port = conn.open_project.from_header(conn_header)
104
+
105
+ # For teamwork projects, you can provide credentials
106
+ credentials = TeamworkCredentials("username", "password")
107
+ port = conn.open_project.with_teamwork_credentials(conn_header, credentials)
108
+ ```
109
+
110
+ ### Dialog Handling
111
+
112
+ MultiConn can automatically handle most dialog windows that appear when opening ArchiCAD projects. This is particularly useful for batch operations and automation scripts.
113
+
114
+ ```python
115
+ from multiconn_archicad import MultiConn, WinDialogHandler, win_int_handler_factory
116
+
117
+ # Create a MultiConn instance with a dialog handler
118
+ conn = MultiConn(dialog_handler=WinDialogHandler(win_int_handler_factory))
119
+
120
+ # Dialog windows will be automatically handled when opening projects
121
+ conn.open_project.from_header(conn_header)
122
+ ```
123
+
124
+ The current implementation includes:
125
+ - `EmptyDialogHandler`: Does nothing (default)
126
+ - `WinDialogHandler`: Waits for ArchiCAD to start, and monitors appearing dialogs. If dialog appears, searches for appropriate handler in win_int_handler factory. Only works on windows.
127
+ - `win_int_handler_factory`: Provides dialog handleing logic on a dialog by dialog basis for the INT language version. It is an example you should customize for your specific project needs. Even if you end up not modifying it, you should definitely know what it does for what dialog.
128
+
129
+ ### Serialization
130
+
131
+ The MultiConn package allows you to save and load connection configurations, making it easier to work with specific projects across multiple sessions.
132
+
133
+ #### Saving Connection Headers
134
+
135
+ ```python
136
+ from multiconn_archicad import MultiConn, Port
137
+
138
+ conn = MultiConn()
139
+ conn.connect.all()
140
+
141
+ # Get a connection header
142
+ conn_header = conn.open_port_headers[Port(19723)]
143
+
144
+ # Convert to dictionary for serialization
145
+ header_dict = conn_header.to_dict()
146
+
147
+ # Save to file using your preferred method
148
+ import json
149
+ with open('conn_header.json', 'w') as f:
150
+ json.dump(header_dict, f)
151
+ ```
152
+
153
+ #### Loading Connection Headers
154
+
155
+ ```python
156
+ from multiconn_archicad import ConnHeader, TeamworkCredentials
157
+
158
+ # Load from file
159
+ import json
160
+ with open('conn_header.json', 'r') as f:
161
+ header_dict = json.load(f)
162
+
163
+ # Create a header from the dictionary
164
+ conn_header = ConnHeader.from_dict(header_dict)
165
+
166
+ # For teamwork projects, you need to provide credentials
167
+ if isinstance(conn_header.archicad_id, TeamworkProjectID):
168
+ credentials = TeamworkCredentials("username", "password")
169
+ # Use the credentials when opening the project
170
+ port = conn.open_project.with_teamwork_credentials(conn_header, credentials)
171
+ ```
172
+
173
+ Note: Passwords are not stored in serialized connection headers for security reasons. You must provide them when loading teamwork projects.
174
+
175
+ ### Running Commands
176
+
177
+ #### Single Archicad Instance
178
+
179
+ To run commands on one chosen ArchiCAD instance the `MultiConn` object has a connection called `primary`. Calling a command directly from the MultiConn object will send it to the `primary` instance. The `primary` connection can be changed by assigning any valid `Port`, or `ConnHeader` object to `MultiConn.primary`.
180
+
181
+ #### Example: Running Commands on a Single Archicad Instance
182
+ ```python
183
+ from multiconn_archicad import MultiConn, Port
184
+
185
+ # After instantiation the primary connection will be the instance with the lowest port number (probably 19723)
186
+ conn = MultiConn()
187
+
188
+ # Set the primary connection to the instance running on port 19725
189
+ conn.primary = Port(19725)
190
+
191
+ # Prints project info from the instance on port 19725
192
+ print(conn.core.post_tapir_command("GetProjectInfo"))
193
+ ```
194
+
195
+ #### Multiple Archicad Instances
196
+
197
+ The MultiConn object stores references to `ConnHeaders` for all open ports (ports, with a running ArchiCAD instance). The references are stored in a dictionary at `.open_port_headers`. This dictionary maps each port to its corresponding connection. Each `ConnHeader` object has its own command objects for each used command namespace. The MultiConn objects has properties to access 3 subsets of open ports based on the status of the `ConnHeaders`:
198
+
199
+ - **`active`**: Successfully connected instances.
200
+ - **`failed`**: Instances where the connection attempt failed.
201
+ - **`pending`**: Instances with no connection attempt made or disconnected.
202
+
203
+ #### Example: Running Commands on Multiple Archicad Instances
204
+
205
+ ```python
206
+ from multiconn_archicad import MultiConn
207
+
208
+ conn = MultiConn()
209
+ conn.connect.all()
210
+
211
+ # Explicit loop to gather elements from all active connections
212
+ elements = {}
213
+ for port, conn_header in conn.active.items():
214
+ elements[port] = conn_header.standard.commands.GetAllElements()
215
+
216
+ # Using dictionary comprehension
217
+ elements = {
218
+ port: conn_header.standard.commands.GetAllElements()
219
+ for port, conn_header in conn.active.items()
220
+ }
221
+ ```
222
+
223
+ ### Namespaces
224
+
225
+ The aim of the module is to incorporate all solutions that let users automate ArchiCAD from python. The different solutions are separated into namespaces, accessed from properties of the connection object. One of the planned features is letting users supply a list of namespaces they want to use when creating the connections. At the moment there are only two namespaces:
226
+
227
+ - **`standard`**: The official ArchiCAD python wrapper
228
+ - **`core`**: A simple JSON based module that lets the users post official and tapir commands based on Tapir's ["aclib"](https://github.com/ENZYME-APD/tapir-archicad-automation/tree/main/archicad-addon/Examples/aclib)
229
+
230
+ #### Example: Using two namespaces together
231
+ ```python
232
+ def run(conn: MultiConn | ConnHeader) -> dict[str, Any]:
233
+ elements = conn.standard.commands.GetAllElements()
234
+ command_parameters = {
235
+ "elements": [element.to_dict() for element in elements],
236
+ "highlightedColors": [[50, 255, 100, 100] for _ in range(len(elements))],
237
+ "wireframe3D": True,
238
+ "nonHighlightedColor": [0, 0, 255, 128],
239
+ }
240
+ return conn.core.post_tapir_command('HighlightElements', command_parameters)
241
+ ```
242
+
243
+ ## Contributing
244
+
245
+ Contributions are welcome! Feel free to submit issues, feature requests, or pull requests to help improve MultiConn ArchiCAD.
246
+
247
+ ## License
248
+
249
+ This project is licensed under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,234 @@
1
+ <img src="https://github.com/user-attachments/assets/370ad589-117c-44cb-9a4a-80bfcb734445" width="400px" />
2
+
3
+ ##
4
+
5
+ **MultiConn ArchiCAD** is a Python-based connection object for ArchiCAD's JSON API and its Python wrapper. It is designed to manage multiple open instances of Archicad simultaneously, making it easier to execute commands across multiple instances.
6
+
7
+ [![Latest Release](https://img.shields.io/github/v/release/SzamosiMate/multiconn_archicad)](https://github.com/SzamosiMate/multiconn_archicad/releases/latest)
8
+ ![License](https://img.shields.io/github/license/SzamosiMate/multiconn_archicad)
9
+ ![Issues](https://img.shields.io/github/issues/SzamosiMate/multiconn_archicad)
10
+ ![Forks](https://img.shields.io/github/forks/SzamosiMate/multiconn_archicad)
11
+ ![Stars](https://img.shields.io/github/stars/SzamosiMate/multiconn_archicad)
12
+
13
+ ## Features
14
+
15
+ - **Multi-connection Support**: Connect to one, multiple, or all open instances of Archicad.
16
+ - **Seamless Integration**: Utilizes ArchiCAD's official Python package.
17
+ - **Tapir Add-On Support**: Run commands from the Tapir Archicad Add-On.
18
+ - **Efficient I/O Operations**: Handles connection management using concurrent or asynchronous code.
19
+ - **Project Management**: Find and open ArchiCAD projects programmatically.
20
+
21
+ ## Installation
22
+
23
+ You can install the latest version of the package from the following link using `pip`:
24
+
25
+ ```bash
26
+ pip install https://github.com/SzamosiMate/multiconn_archicad/releases/latest/download/multiconn_archicad-0.1.2-py3-none-any.whl
27
+ ```
28
+
29
+ The package depends on the [Tapir Archicad Add-On](https://github.com/ENZYME-APD/tapir-archicad-automation?tab=readme-ov-file). It is recommended to install the latest version of Tapir to access all features. While some functionality may work without the add-on, all tests have been conducted with it installed.
30
+
31
+ ## Usage
32
+
33
+ **Disclaimer:** The connection object is functional but in the early stages of development. It is not thoroughly tested, and its interfaces may change in future updates.
34
+
35
+ ### Actions - managing the connection
36
+ Actions allow you to manage the state of the connection object. You can connect to or disconnect from Archicad instances, quit instances, or refresh ports. All actions can have multiple types of inputs. For each type of input you have to call the corresponding method of the action. To connect to all available ArchiCAD instances, you have to call the .all() method on .connect ( e.g. `conn.connect.all()`). The aim of this method is to provide better autocompletion.
37
+
38
+ #### Example: Connection Management
39
+ ```python
40
+ from multiconn_archicad import MultiConn, Port
41
+
42
+ conn = MultiConn()
43
+
44
+ # connect all ArchiCAD instances that were running at instantiation / the last refresh
45
+ conn.connect.all()
46
+
47
+ # disconnect from the instance at port 19723
48
+ conn.disconnect.from_ports(Port(19723))
49
+
50
+ # refresh all closed ports - ports with no running archicad instance
51
+ conn.refresh.closed_ports()
52
+
53
+ # close, and remove from the dict of open port headers the archicad instance specified by ConnHeader
54
+ conn.quit.from_headers(conn.open_port_headers[Port(19735)])
55
+ ```
56
+
57
+ ### Project Management
58
+
59
+ The MultiConn object provides actions to find and open ArchiCAD projects programmatically.
60
+
61
+ #### Finding ArchiCAD Instances
62
+
63
+ You can use the `find_archicad` action to locate a specific ArchiCAD instance from a `ConnHeader`.
64
+
65
+ ```python
66
+ from multiconn_archicad import MultiConn, ConnHeader
67
+
68
+ conn = MultiConn()
69
+ conn_header = ConnHeader(Port(19723))
70
+
71
+ # Find the port for a specific connection header
72
+ port = conn.find_archicad.from_header(conn_header)
73
+ if port:
74
+ print(f"Found ArchiCAD instance at port: {port}")
75
+ ```
76
+
77
+ #### Opening Projects
78
+
79
+ The `open_project` action allows you to programmatically open ArchiCAD projects.
80
+
81
+ ```python
82
+ from multiconn_archicad import MultiConn, ConnHeader, TeamworkCredentials
83
+
84
+ conn = MultiConn()
85
+
86
+ # Open a project using a connection header
87
+ conn_header = ConnHeader.from_dict(saved_header_data)
88
+ port = conn.open_project.from_header(conn_header)
89
+
90
+ # For teamwork projects, you can provide credentials
91
+ credentials = TeamworkCredentials("username", "password")
92
+ port = conn.open_project.with_teamwork_credentials(conn_header, credentials)
93
+ ```
94
+
95
+ ### Dialog Handling
96
+
97
+ MultiConn can automatically handle most dialog windows that appear when opening ArchiCAD projects. This is particularly useful for batch operations and automation scripts.
98
+
99
+ ```python
100
+ from multiconn_archicad import MultiConn, WinDialogHandler, win_int_handler_factory
101
+
102
+ # Create a MultiConn instance with a dialog handler
103
+ conn = MultiConn(dialog_handler=WinDialogHandler(win_int_handler_factory))
104
+
105
+ # Dialog windows will be automatically handled when opening projects
106
+ conn.open_project.from_header(conn_header)
107
+ ```
108
+
109
+ The current implementation includes:
110
+ - `EmptyDialogHandler`: Does nothing (default)
111
+ - `WinDialogHandler`: Waits for ArchiCAD to start, and monitors appearing dialogs. If dialog appears, searches for appropriate handler in win_int_handler factory. Only works on windows.
112
+ - `win_int_handler_factory`: Provides dialog handleing logic on a dialog by dialog basis for the INT language version. It is an example you should customize for your specific project needs. Even if you end up not modifying it, you should definitely know what it does for what dialog.
113
+
114
+ ### Serialization
115
+
116
+ The MultiConn package allows you to save and load connection configurations, making it easier to work with specific projects across multiple sessions.
117
+
118
+ #### Saving Connection Headers
119
+
120
+ ```python
121
+ from multiconn_archicad import MultiConn, Port
122
+
123
+ conn = MultiConn()
124
+ conn.connect.all()
125
+
126
+ # Get a connection header
127
+ conn_header = conn.open_port_headers[Port(19723)]
128
+
129
+ # Convert to dictionary for serialization
130
+ header_dict = conn_header.to_dict()
131
+
132
+ # Save to file using your preferred method
133
+ import json
134
+ with open('conn_header.json', 'w') as f:
135
+ json.dump(header_dict, f)
136
+ ```
137
+
138
+ #### Loading Connection Headers
139
+
140
+ ```python
141
+ from multiconn_archicad import ConnHeader, TeamworkCredentials
142
+
143
+ # Load from file
144
+ import json
145
+ with open('conn_header.json', 'r') as f:
146
+ header_dict = json.load(f)
147
+
148
+ # Create a header from the dictionary
149
+ conn_header = ConnHeader.from_dict(header_dict)
150
+
151
+ # For teamwork projects, you need to provide credentials
152
+ if isinstance(conn_header.archicad_id, TeamworkProjectID):
153
+ credentials = TeamworkCredentials("username", "password")
154
+ # Use the credentials when opening the project
155
+ port = conn.open_project.with_teamwork_credentials(conn_header, credentials)
156
+ ```
157
+
158
+ Note: Passwords are not stored in serialized connection headers for security reasons. You must provide them when loading teamwork projects.
159
+
160
+ ### Running Commands
161
+
162
+ #### Single Archicad Instance
163
+
164
+ To run commands on one chosen ArchiCAD instance the `MultiConn` object has a connection called `primary`. Calling a command directly from the MultiConn object will send it to the `primary` instance. The `primary` connection can be changed by assigning any valid `Port`, or `ConnHeader` object to `MultiConn.primary`.
165
+
166
+ #### Example: Running Commands on a Single Archicad Instance
167
+ ```python
168
+ from multiconn_archicad import MultiConn, Port
169
+
170
+ # After instantiation the primary connection will be the instance with the lowest port number (probably 19723)
171
+ conn = MultiConn()
172
+
173
+ # Set the primary connection to the instance running on port 19725
174
+ conn.primary = Port(19725)
175
+
176
+ # Prints project info from the instance on port 19725
177
+ print(conn.core.post_tapir_command("GetProjectInfo"))
178
+ ```
179
+
180
+ #### Multiple Archicad Instances
181
+
182
+ The MultiConn object stores references to `ConnHeaders` for all open ports (ports, with a running ArchiCAD instance). The references are stored in a dictionary at `.open_port_headers`. This dictionary maps each port to its corresponding connection. Each `ConnHeader` object has its own command objects for each used command namespace. The MultiConn objects has properties to access 3 subsets of open ports based on the status of the `ConnHeaders`:
183
+
184
+ - **`active`**: Successfully connected instances.
185
+ - **`failed`**: Instances where the connection attempt failed.
186
+ - **`pending`**: Instances with no connection attempt made or disconnected.
187
+
188
+ #### Example: Running Commands on Multiple Archicad Instances
189
+
190
+ ```python
191
+ from multiconn_archicad import MultiConn
192
+
193
+ conn = MultiConn()
194
+ conn.connect.all()
195
+
196
+ # Explicit loop to gather elements from all active connections
197
+ elements = {}
198
+ for port, conn_header in conn.active.items():
199
+ elements[port] = conn_header.standard.commands.GetAllElements()
200
+
201
+ # Using dictionary comprehension
202
+ elements = {
203
+ port: conn_header.standard.commands.GetAllElements()
204
+ for port, conn_header in conn.active.items()
205
+ }
206
+ ```
207
+
208
+ ### Namespaces
209
+
210
+ The aim of the module is to incorporate all solutions that let users automate ArchiCAD from python. The different solutions are separated into namespaces, accessed from properties of the connection object. One of the planned features is letting users supply a list of namespaces they want to use when creating the connections. At the moment there are only two namespaces:
211
+
212
+ - **`standard`**: The official ArchiCAD python wrapper
213
+ - **`core`**: A simple JSON based module that lets the users post official and tapir commands based on Tapir's ["aclib"](https://github.com/ENZYME-APD/tapir-archicad-automation/tree/main/archicad-addon/Examples/aclib)
214
+
215
+ #### Example: Using two namespaces together
216
+ ```python
217
+ def run(conn: MultiConn | ConnHeader) -> dict[str, Any]:
218
+ elements = conn.standard.commands.GetAllElements()
219
+ command_parameters = {
220
+ "elements": [element.to_dict() for element in elements],
221
+ "highlightedColors": [[50, 255, 100, 100] for _ in range(len(elements))],
222
+ "wireframe3D": True,
223
+ "nonHighlightedColor": [0, 0, 255, 128],
224
+ }
225
+ return conn.core.post_tapir_command('HighlightElements', command_parameters)
226
+ ```
227
+
228
+ ## Contributing
229
+
230
+ Contributions are welcome! Feel free to submit issues, feature requests, or pull requests to help improve MultiConn ArchiCAD.
231
+
232
+ ## License
233
+
234
+ This project is licensed under the MIT License. See the LICENSE file for details.
@@ -0,0 +1,54 @@
1
+ [project]
2
+ name = "multiconn_archicad"
3
+ version = "0.2.0"
4
+ description = "MultiConn ArchiCAD is a connection object for ArchiCAD’s JSON API and its Python wrapper, designed to manage multiple open instances of ArchiCAD simultaneously."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "SzamosiMate", email = "szamimate@yahoo.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "aiohttp>=3.11.11",
12
+ "archicad>=28.3000",
13
+ "psutil>=6.1.1",
14
+ "pywinauto>=0.6.9",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dialog-handlers = [
19
+ "pywinauto>=0.6.9",
20
+ ]
21
+
22
+ [build-system]
23
+ requires = ["hatchling"]
24
+ build-backend = "hatchling.build"
25
+ [tool.hatch.build.targets.sdist]
26
+ include = [
27
+ "pkg/*.py",
28
+ "/src",
29
+ ]
30
+
31
+ [dependency-groups]
32
+ dev = [
33
+ "mypy>=1.14.0",
34
+ "pytest>=8.3.4",
35
+ "ruff>=0.8.4",
36
+ ]
37
+
38
+ [[tool.mypy.overrides]]
39
+ module = ["archicad.versioning",
40
+ "archicad.connection",
41
+ "archicad.releases",
42
+ "pywinauto",
43
+ "pywinauto.controls.uiawrapper",
44
+ "psutil"]
45
+ follow_untyped_imports = true
46
+
47
+ [tool.ruff]
48
+ line-length = 120
49
+
50
+ [tool.ruff.format]
51
+ indent-style = "space"
52
+ quote-style = "double"
53
+ line-ending = "lf"
54
+ docstring-code-format = true
@@ -0,0 +1,43 @@
1
+ from .multi_conn import MultiConn
2
+ from .conn_header import ConnHeader
3
+ from .basic_types import (
4
+ ArchiCadID,
5
+ TeamworkProjectID,
6
+ SoloProjectID,
7
+ UntitledProjectID,
8
+ TeamworkCredentials,
9
+ ProductInfo,
10
+ ArchicadLocation,
11
+ Port,
12
+ APIResponseError,
13
+ FromAPIResponse,
14
+ )
15
+ from .standard_connection import StandardConnection
16
+ from .core_commands import CoreCommands
17
+ from .dialog_handlers import (
18
+ DialogHandlerBase,
19
+ WinDialogHandler,
20
+ win_int_handler_factory,
21
+ UnhandledDialogError,
22
+ )
23
+
24
+ __all__: tuple[str, ...] = (
25
+ "MultiConn",
26
+ "ConnHeader",
27
+ "ArchiCadID",
28
+ "APIResponseError",
29
+ "FromAPIResponse",
30
+ "ProductInfo",
31
+ "Port",
32
+ "StandardConnection",
33
+ "CoreCommands",
34
+ "TeamworkCredentials",
35
+ "DialogHandlerBase",
36
+ "WinDialogHandler",
37
+ "win_int_handler_factory",
38
+ "UnhandledDialogError",
39
+ "TeamworkProjectID",
40
+ "SoloProjectID",
41
+ "UntitledProjectID",
42
+ "ArchicadLocation",
43
+ )
@@ -0,0 +1,12 @@
1
+ from .connection_manager import Connect, QuitAndDisconnect, Disconnect
2
+ from .project_handler import FindArchicad, OpenProject
3
+ from .refresh import Refresh
4
+
5
+ __all__: tuple[str, ...] = (
6
+ "Connect",
7
+ "Disconnect",
8
+ "QuitAndDisconnect",
9
+ "Refresh",
10
+ "FindArchicad",
11
+ "OpenProject",
12
+ )
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+ from abc import ABC, abstractmethod
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from multiconn_archicad.conn_header import ConnHeader
7
+ from multiconn_archicad.multi_conn import MultiConn
8
+ from multiconn_archicad.basic_types import Port
9
+
10
+
11
+ class ConnectionManager(ABC):
12
+ def __init__(self, multi_conn: MultiConn):
13
+ self.multi_conn: MultiConn = multi_conn
14
+
15
+ def from_ports(self, *args: Port) -> list[ConnHeader]:
16
+ return self.execute_action(
17
+ [
18
+ self.multi_conn.open_port_headers[port]
19
+ for port in args
20
+ if port in self.multi_conn.open_port_headers.keys()
21
+ ]
22
+ )
23
+
24
+ def from_headers(self, *args: ConnHeader) -> list[ConnHeader]:
25
+ return self.execute_action([*args])
26
+
27
+ def all(self) -> list[ConnHeader]:
28
+ return self.execute_action(list(self.multi_conn.open_port_headers.values()))
29
+
30
+ @abstractmethod
31
+ def execute_action(self, conn_headers: list[ConnHeader]) -> list[ConnHeader]: ...
32
+
33
+
34
+ class Connect(ConnectionManager):
35
+ def execute_action(self, conn_headers: list[ConnHeader]) -> list[ConnHeader]:
36
+ for conn_header in conn_headers:
37
+ print(f"connecting {conn_header.product_info}")
38
+ conn_header.connect()
39
+ return conn_headers
40
+
41
+ def failed(self) -> None:
42
+ self.execute_action(list(self.multi_conn.failed.values()))
43
+
44
+
45
+ class Disconnect(ConnectionManager):
46
+ def execute_action(self, conn_headers: list[ConnHeader]) -> list[ConnHeader]:
47
+ for conn_header in conn_headers:
48
+ conn_header.disconnect()
49
+ return conn_headers
50
+
51
+
52
+ class QuitAndDisconnect(ConnectionManager):
53
+ def execute_action(self, conn_headers: list[ConnHeader]) -> list[ConnHeader]:
54
+ for conn_header in conn_headers:
55
+ if conn_header.port:
56
+ conn_header.core.post_tapir_command("QuitArchicad")
57
+ self.multi_conn.open_port_headers.pop(conn_header.port)
58
+ conn_header.unassign()
59
+ return conn_headers