floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.3__py3-none-any.whl
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.
- floodmodeller_api/__init__.py +8 -9
- floodmodeller_api/_base.py +184 -176
- floodmodeller_api/backup.py +273 -273
- floodmodeller_api/dat.py +909 -831
- floodmodeller_api/diff.py +136 -119
- floodmodeller_api/ied.py +307 -306
- floodmodeller_api/ief.py +647 -637
- floodmodeller_api/ief_flags.py +253 -253
- floodmodeller_api/inp.py +266 -266
- floodmodeller_api/libs/libifcoremd.dll +0 -0
- floodmodeller_api/libs/libifcoremt.so.5 +0 -0
- floodmodeller_api/libs/libifport.so.5 +0 -0
- floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
- floodmodeller_api/libs/libintlc.so.5 +0 -0
- floodmodeller_api/libs/libmmd.dll +0 -0
- floodmodeller_api/libs/libsvml.so +0 -0
- floodmodeller_api/libs/libzzn_read.so +0 -0
- floodmodeller_api/libs/zzn_read.dll +0 -0
- floodmodeller_api/logs/__init__.py +2 -2
- floodmodeller_api/logs/lf.py +320 -312
- floodmodeller_api/logs/lf_helpers.py +354 -352
- floodmodeller_api/logs/lf_params.py +643 -529
- floodmodeller_api/mapping.py +84 -0
- floodmodeller_api/test/__init__.py +4 -4
- floodmodeller_api/test/conftest.py +9 -8
- floodmodeller_api/test/test_backup.py +117 -117
- floodmodeller_api/test/test_dat.py +221 -92
- floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
- floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
- floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
- floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
- floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
- floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
- floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
- floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
- floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
- floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
- floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
- floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
- floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
- floodmodeller_api/test/test_data/EX1.DAT +321 -321
- floodmodeller_api/test/test_data/EX1.ext +107 -107
- floodmodeller_api/test/test_data/EX1.feb +320 -320
- floodmodeller_api/test/test_data/EX1.gxy +107 -107
- floodmodeller_api/test/test_data/EX17.DAT +421 -422
- floodmodeller_api/test/test_data/EX17.ext +213 -213
- floodmodeller_api/test/test_data/EX17.feb +422 -422
- floodmodeller_api/test/test_data/EX18.DAT +375 -375
- floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
- floodmodeller_api/test/test_data/EX2.DAT +302 -302
- floodmodeller_api/test/test_data/EX3.DAT +926 -926
- floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
- floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
- floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
- floodmodeller_api/test/test_data/EX6.ext +532 -532
- floodmodeller_api/test/test_data/EX6.feb +2084 -2084
- floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
- floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
- floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
- floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
- floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
- floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
- floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
- floodmodeller_api/test/test_data/blockage.dat +50 -50
- floodmodeller_api/test/test_data/blockage.ext +45 -45
- floodmodeller_api/test/test_data/blockage.feb +9 -9
- floodmodeller_api/test/test_data/blockage.gxy +71 -71
- floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
- floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
- floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
- floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
- floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
- floodmodeller_api/test/test_data/ex3.ief +20 -20
- floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
- floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
- floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
- floodmodeller_api/test/test_data/example1.inp +329 -329
- floodmodeller_api/test/test_data/example2.inp +158 -158
- floodmodeller_api/test/test_data/example3.inp +297 -297
- floodmodeller_api/test/test_data/example4.inp +388 -388
- floodmodeller_api/test/test_data/example5.inp +147 -147
- floodmodeller_api/test/test_data/example6.inp +154 -154
- floodmodeller_api/test/test_data/jump.dat +176 -176
- floodmodeller_api/test/test_data/network.dat +1374 -1374
- floodmodeller_api/test/test_data/network.ext +45 -45
- floodmodeller_api/test/test_data/network.exy +1 -1
- floodmodeller_api/test/test_data/network.feb +45 -45
- floodmodeller_api/test/test_data/network.ied +45 -45
- floodmodeller_api/test/test_data/network.ief +20 -20
- floodmodeller_api/test/test_data/network.inp +147 -147
- floodmodeller_api/test/test_data/network.pxy +57 -57
- floodmodeller_api/test/test_data/network.zzd +122 -122
- floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
- floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
- floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
- floodmodeller_api/test/test_data/rnweir.dat +9 -9
- floodmodeller_api/test/test_data/rnweir.ext +45 -45
- floodmodeller_api/test/test_data/rnweir.feb +9 -9
- floodmodeller_api/test/test_data/rnweir.gxy +45 -45
- floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
- floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
- floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
- floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
- floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
- floodmodeller_api/test/test_data/unit checks.dat +16 -16
- floodmodeller_api/test/test_ied.py +29 -29
- floodmodeller_api/test/test_ief.py +125 -24
- floodmodeller_api/test/test_inp.py +47 -48
- floodmodeller_api/test/test_json.py +114 -0
- floodmodeller_api/test/test_logs_lf.py +48 -51
- floodmodeller_api/test/test_tool.py +165 -152
- floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
- floodmodeller_api/test/test_xml2d.py +151 -156
- floodmodeller_api/test/test_zzn.py +36 -34
- floodmodeller_api/to_from_json.py +218 -0
- floodmodeller_api/tool.py +332 -329
- floodmodeller_api/toolbox/__init__.py +5 -5
- floodmodeller_api/toolbox/example_tool.py +45 -45
- floodmodeller_api/toolbox/model_build/__init__.py +2 -2
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
- floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
- floodmodeller_api/units/__init__.py +10 -10
- floodmodeller_api/units/_base.py +214 -212
- floodmodeller_api/units/boundaries.py +467 -467
- floodmodeller_api/units/comment.py +52 -55
- floodmodeller_api/units/conduits.py +382 -402
- floodmodeller_api/units/helpers.py +123 -131
- floodmodeller_api/units/iic.py +107 -101
- floodmodeller_api/units/losses.py +305 -306
- floodmodeller_api/units/sections.py +444 -446
- floodmodeller_api/units/structures.py +1690 -1683
- floodmodeller_api/units/units.py +93 -104
- floodmodeller_api/units/unsupported.py +44 -44
- floodmodeller_api/units/variables.py +87 -89
- floodmodeller_api/urban1d/__init__.py +11 -11
- floodmodeller_api/urban1d/_base.py +188 -179
- floodmodeller_api/urban1d/conduits.py +93 -85
- floodmodeller_api/urban1d/general_parameters.py +58 -58
- floodmodeller_api/urban1d/junctions.py +81 -79
- floodmodeller_api/urban1d/losses.py +81 -74
- floodmodeller_api/urban1d/outfalls.py +114 -110
- floodmodeller_api/urban1d/raingauges.py +111 -111
- floodmodeller_api/urban1d/subsections.py +92 -98
- floodmodeller_api/urban1d/xsections.py +147 -144
- floodmodeller_api/util.py +77 -21
- floodmodeller_api/validation/parameters.py +660 -660
- floodmodeller_api/validation/urban_parameters.py +388 -404
- floodmodeller_api/validation/validation.py +110 -108
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +688 -673
- floodmodeller_api/xml2d_template.py +37 -37
- floodmodeller_api/zzn.py +387 -363
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/METADATA +82 -82
- floodmodeller_api-0.4.3.dist-info/RECORD +179 -0
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
- floodmodeller_api/libifcoremd.dll +0 -0
- floodmodeller_api/test/test_data/EX3.bmp +0 -0
- floodmodeller_api/test/test_data/test_output.csv +0 -87
- floodmodeller_api/zzn_read.dll +0 -0
- floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/entry_points.txt +0 -0
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
floodmodeller_api/tool.py
CHANGED
|
@@ -1,329 +1,332 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self.master
|
|
104
|
-
|
|
105
|
-
self.master.geometry("400x300")
|
|
106
|
-
self.master.configure(bg="#f5f5f5")
|
|
107
|
-
self.parameters = parameters
|
|
108
|
-
self.run_function = run_function
|
|
109
|
-
self.create_widgets(description)
|
|
110
|
-
|
|
111
|
-
def create_widgets(self, description):
|
|
112
|
-
# Create and place the description label
|
|
113
|
-
desc_label = tk.Label(self.master, text=description, font=("Arial", 14), bg="#f5f5f5")
|
|
114
|
-
desc_label.pack(pady=(20, 10))
|
|
115
|
-
# Run the method to add inputs based upon parameters
|
|
116
|
-
self.add_inputs()
|
|
117
|
-
# Create and place the button
|
|
118
|
-
self.button = tk.Button(
|
|
119
|
-
self.master,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
entry.
|
|
150
|
-
elif data_type ==
|
|
151
|
-
entry = tk.Entry(self.master, validate="key")
|
|
152
|
-
entry.config(validatecommand=(entry.register(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
are unique.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
#
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
import tkinter as tk
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass()
|
|
10
|
+
class Parameter:
|
|
11
|
+
"""Class to represent an FM Tool parameter.
|
|
12
|
+
There should be one parameter for each argument of the tool_function function
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name (str): The name of the parameter.
|
|
16
|
+
dtype (type): The expected data type of the parameter.
|
|
17
|
+
description (str): A description of the parameter.
|
|
18
|
+
help_text (str): The help text to be displayed for the parameter.
|
|
19
|
+
required (bool): A flag indicating whether the parameter is required or optional. Default is True.
|
|
20
|
+
|
|
21
|
+
Methods:
|
|
22
|
+
__eq__: Compare two parameters by their name attribute.
|
|
23
|
+
__hash__: Return the hash value of the parameter name.
|
|
24
|
+
__repr__: Return a string representation of the parameter.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
name: str
|
|
29
|
+
dtype: type
|
|
30
|
+
description: str | None = None
|
|
31
|
+
help_text: str | None = None
|
|
32
|
+
required: bool = True
|
|
33
|
+
|
|
34
|
+
def __eq__(self, other: object) -> bool:
|
|
35
|
+
if not isinstance(other, Parameter):
|
|
36
|
+
return NotImplemented
|
|
37
|
+
return self.name == other.name
|
|
38
|
+
|
|
39
|
+
def __hash__(self):
|
|
40
|
+
return hash(self.name)
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
return f"Parameter({self.name})"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def validate_int(value):
|
|
47
|
+
"""Function to validate integer input.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
value (str): The input value to be validated.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
bool: True if input value is a valid integer or an empty string, False otherwise.
|
|
54
|
+
"""
|
|
55
|
+
if value.isdigit():
|
|
56
|
+
return True
|
|
57
|
+
if value == "":
|
|
58
|
+
return True
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def validate_float(value):
|
|
63
|
+
"""Function to validate float input.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
value (str): The input value to be validated.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
bool: True if input value is a valid float or an empty string, False otherwise.
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
float(value)
|
|
73
|
+
return True
|
|
74
|
+
except ValueError:
|
|
75
|
+
if value == "":
|
|
76
|
+
return True
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Gui:
|
|
81
|
+
"""
|
|
82
|
+
Method to generate a Tkinter graphical user interface (GUI).
|
|
83
|
+
This method generates a GUI based upon a function, its parameters, as well as descriptive properties allowing any tool to be
|
|
84
|
+
run from a GUI rather than as python code.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
master (tkinter.Tk): a tkinter root object. Contains app methods and attributes
|
|
88
|
+
title (str): The Apps title
|
|
89
|
+
description (str): A description of the application
|
|
90
|
+
parameters (list[Parameter]): a list of parameters. This is used to generate the input boxes and pass kwargs to the run function
|
|
91
|
+
run_function (function): a function that should be run by the app
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__( # noqa: PLR0913
|
|
96
|
+
self,
|
|
97
|
+
master: tk.Tk,
|
|
98
|
+
title: str,
|
|
99
|
+
description: str,
|
|
100
|
+
parameters: list[Parameter],
|
|
101
|
+
run_function,
|
|
102
|
+
):
|
|
103
|
+
self.master = master
|
|
104
|
+
self.master.title(title)
|
|
105
|
+
self.master.geometry("400x300")
|
|
106
|
+
self.master.configure(bg="#f5f5f5")
|
|
107
|
+
self.parameters = parameters
|
|
108
|
+
self.run_function = run_function
|
|
109
|
+
self.create_widgets(description)
|
|
110
|
+
|
|
111
|
+
def create_widgets(self, description):
|
|
112
|
+
# Create and place the description label
|
|
113
|
+
desc_label = tk.Label(self.master, text=description, font=("Arial", 14), bg="#f5f5f5")
|
|
114
|
+
desc_label.pack(pady=(20, 10))
|
|
115
|
+
# Run the method to add inputs based upon parameters
|
|
116
|
+
self.add_inputs()
|
|
117
|
+
# Create and place the button
|
|
118
|
+
self.button = tk.Button(
|
|
119
|
+
self.master,
|
|
120
|
+
text="Run",
|
|
121
|
+
font=("Arial", 14),
|
|
122
|
+
command=self.run_gui_callback,
|
|
123
|
+
)
|
|
124
|
+
self.button.pack(pady=10)
|
|
125
|
+
# Add other widgets to the app
|
|
126
|
+
###
|
|
127
|
+
|
|
128
|
+
def add_inputs(self):
|
|
129
|
+
"""
|
|
130
|
+
Method to add inputs widgets to the app based upon parameters.
|
|
131
|
+
|
|
132
|
+
This method adds an input widget to the app for each parameter.
|
|
133
|
+
"""
|
|
134
|
+
# Extract the parameters to a list to iterate through
|
|
135
|
+
parameters = [(param.name, param.dtype) for param in self.parameters]
|
|
136
|
+
|
|
137
|
+
# Create a label and entry box for each parameter
|
|
138
|
+
# Adding the input boxes as a class attribute dictionary
|
|
139
|
+
# this enables us to easily get the values of in each input box and pass them to
|
|
140
|
+
# the run function. It also makes it easier to debug since you can create an instance, generate the GUI
|
|
141
|
+
# and then inspect the attributes.
|
|
142
|
+
self.root_entries = {}
|
|
143
|
+
for name, data_type in parameters:
|
|
144
|
+
label = tk.Label(self.master, text=name, anchor="w")
|
|
145
|
+
label.pack()
|
|
146
|
+
# Conditional stuff to add validation for different data types.
|
|
147
|
+
# This ensures that you can't enter text if the input should be a number, etc.
|
|
148
|
+
if data_type == str:
|
|
149
|
+
entry = tk.Entry(self.master)
|
|
150
|
+
elif data_type == int:
|
|
151
|
+
entry = tk.Entry(self.master, validate="key")
|
|
152
|
+
entry.config(validatecommand=(entry.register(validate_int), "%P"))
|
|
153
|
+
elif data_type == float:
|
|
154
|
+
entry = tk.Entry(self.master, validate="key")
|
|
155
|
+
entry.config(validatecommand=(entry.register(validate_float), "%P"))
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError("Invalid data type")
|
|
158
|
+
entry.pack()
|
|
159
|
+
self.root_entries[name] = entry
|
|
160
|
+
|
|
161
|
+
# TODO: Add a progress bar if appropriate
|
|
162
|
+
# TODO: Present some useful information: either tool outputs or logs
|
|
163
|
+
|
|
164
|
+
def run_gui_callback(self):
|
|
165
|
+
"""
|
|
166
|
+
Method to run the gui callback function.
|
|
167
|
+
|
|
168
|
+
This extracts the parameter values from the GUI and passes them to the run function. It is triggered using
|
|
169
|
+
the run button in the GUI.
|
|
170
|
+
"""
|
|
171
|
+
input_kwargs = {}
|
|
172
|
+
for input_param in self.parameters:
|
|
173
|
+
# Get the parameter value but subsetting the dictionary of GUI entry points (text boxes)
|
|
174
|
+
input_var = self.root_entries[input_param.name].get()
|
|
175
|
+
# Assert that the value (which is initially a string) is the right type
|
|
176
|
+
# insert the value to the input_kwargs dictionary to pass to the run function
|
|
177
|
+
input_kwargs[input_param.name] = input_param.dtype(input_var)
|
|
178
|
+
# Run the callback function
|
|
179
|
+
return self.run_function(**input_kwargs)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class FMTool:
|
|
183
|
+
"""
|
|
184
|
+
Compare two parameters by their name attribute.
|
|
185
|
+
|
|
186
|
+
Use the class by wrapping it in a child class which defines the parameters and function to call:
|
|
187
|
+
|
|
188
|
+
This class provides a consistent method to structure flood modeller tools that
|
|
189
|
+
use the API to automate processes, extract data and visualise results. The class also provides
|
|
190
|
+
methods to extend any tool with a command line interface or tkinter GUI.
|
|
191
|
+
|
|
192
|
+
We plan to add more extensions in the future.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
name (str): The name of the tool to display in the GUI or cmd line
|
|
196
|
+
description (str): A description of the tool and what it does.
|
|
197
|
+
parameters (list[Parameter]): the Tool parameters, one per input function
|
|
198
|
+
tool_function (function): The function to be called by the tool
|
|
199
|
+
|
|
200
|
+
.. code:: python
|
|
201
|
+
|
|
202
|
+
# concatenates strings
|
|
203
|
+
def concat(str1, str2):
|
|
204
|
+
return str1 + str2
|
|
205
|
+
class MyTool(FMTool):
|
|
206
|
+
name = "Name"
|
|
207
|
+
description = "Tool description"
|
|
208
|
+
parameters = [
|
|
209
|
+
Parameter("str1", str),
|
|
210
|
+
Parameter("str2", str)
|
|
211
|
+
]
|
|
212
|
+
tool_function = concat
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
parameters: list[Parameter] = []
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def name(self):
|
|
220
|
+
"""
|
|
221
|
+
A property method to ensure a tool name is provided in child class. Overwritten by child.
|
|
222
|
+
"""
|
|
223
|
+
raise NotImplementedError("Tools need a name")
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def description(self):
|
|
227
|
+
"""
|
|
228
|
+
A property method to ensure a tool description is provided in child class. Overwritten by child.
|
|
229
|
+
"""
|
|
230
|
+
raise NotImplementedError("Tools need a description")
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def tool_function(self):
|
|
234
|
+
"""
|
|
235
|
+
A property method to ensure an tool_function is provided in child class. Overwritten by child.
|
|
236
|
+
"""
|
|
237
|
+
raise NotImplementedError("You must provide an entry point function")
|
|
238
|
+
|
|
239
|
+
def __init__(self):
|
|
240
|
+
self.check_parameters()
|
|
241
|
+
|
|
242
|
+
def check_parameters(self):
|
|
243
|
+
"""
|
|
244
|
+
A function to check that all parameter names are unique.
|
|
245
|
+
|
|
246
|
+
Since parameter names correspond to function arguments, this function is required to check that all
|
|
247
|
+
are unique.
|
|
248
|
+
|
|
249
|
+
Raises:
|
|
250
|
+
ValueError: if parameter names aren't unique
|
|
251
|
+
"""
|
|
252
|
+
params = []
|
|
253
|
+
for parameter in self.parameters:
|
|
254
|
+
if parameter.name in params:
|
|
255
|
+
raise ValueError("Parameter names must be unique")
|
|
256
|
+
params.append(parameter.name)
|
|
257
|
+
|
|
258
|
+
# This is defined as a class method because of the use of **kwargs
|
|
259
|
+
# When using this approach to pass around function arguments, the self object is appended to the **kwargs
|
|
260
|
+
# passed into the function and this results in the wrong number of arguments being passed to the tool_function function
|
|
261
|
+
@classmethod
|
|
262
|
+
def run(cls, **kwargs):
|
|
263
|
+
"""
|
|
264
|
+
Method to run the entry point function.
|
|
265
|
+
|
|
266
|
+
This approach allows the function to be run either from the command line interface, the GUI or any other extensions
|
|
267
|
+
that we add in the future.
|
|
268
|
+
Args:
|
|
269
|
+
**kwargs: keyword arguments for the tool_function function.
|
|
270
|
+
"""
|
|
271
|
+
return cls.tool_function(**kwargs)
|
|
272
|
+
|
|
273
|
+
def run_from_command_line(self):
|
|
274
|
+
"""
|
|
275
|
+
Method to run the tool from the command line.
|
|
276
|
+
|
|
277
|
+
This method parses command line arguments (as defined in self.parameters) and passes them to run to execute the tool
|
|
278
|
+
"""
|
|
279
|
+
run_gui = False
|
|
280
|
+
try:
|
|
281
|
+
if sys.argv[1] == "gui":
|
|
282
|
+
# gui flag added so running as gui
|
|
283
|
+
run_gui = True
|
|
284
|
+
except IndexError:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
if run_gui:
|
|
288
|
+
self.run_gui()
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
# Create an argument parse and add each argument defined in the parameters
|
|
292
|
+
parser = argparse.ArgumentParser(description=self.description)
|
|
293
|
+
|
|
294
|
+
# Parse the aruments from the commandline
|
|
295
|
+
for input_param in self.parameters:
|
|
296
|
+
parser.add_argument(
|
|
297
|
+
f"--{input_param.name}",
|
|
298
|
+
required=input_param.required,
|
|
299
|
+
help=input_param.help_text,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
args = parser.parse_args()
|
|
303
|
+
# And then construct a dictionary of them that can be passed to the run function as keyword arguments
|
|
304
|
+
input_kwargs = {}
|
|
305
|
+
for input_param in self.parameters:
|
|
306
|
+
value = getattr(args, input_param.name)
|
|
307
|
+
input_kwargs[input_param.name] = input_param.dtype(value)
|
|
308
|
+
|
|
309
|
+
print(f"Running {self.name}")
|
|
310
|
+
self.run(**input_kwargs)
|
|
311
|
+
print("Completed")
|
|
312
|
+
# Return nothing
|
|
313
|
+
|
|
314
|
+
def generate_gui(self):
|
|
315
|
+
"""
|
|
316
|
+
Method to build the GUI
|
|
317
|
+
"""
|
|
318
|
+
root = tk.Tk()
|
|
319
|
+
self.app = Gui(
|
|
320
|
+
root,
|
|
321
|
+
title=self.name,
|
|
322
|
+
description=self.description,
|
|
323
|
+
parameters=self.parameters,
|
|
324
|
+
run_function=self.run,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def run_gui(self):
|
|
328
|
+
"""
|
|
329
|
+
Method to run the GUI
|
|
330
|
+
"""
|
|
331
|
+
self.generate_gui()
|
|
332
|
+
self.app.master.mainloop()
|