tpism_config_editor - pism - [fork] customized build of PISM, the parallel ice sheet model (tillflux branch)
(HTM) git clone git://src.adamsgaard.dk/pism
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
tpism_config_editor (10520B)
---
1 #!/usr/bin/env python3
2
3 data = {}
4
5
6 def text_height(width, value):
7 assert width > 0
8
9 if len(value) < width:
10 return 1
11 else:
12 return len(value) / width + 2
13
14
15 def load_config(filename):
16 global data
17
18 import json
19
20 with open(filename) as f:
21 data = json.load(f)
22
23
24 def is_special(key):
25 return key.endswith("_doc") or key.endswith("_type") or key.endswith("_units") or key.endswith("_option") or key.endswith("_choices") or key == "long_name"
26
27
28 def path(event):
29 return event.widget.focus().split(".")
30
31
32 def parameter_info(event):
33 d = data.copy()
34 p = path(event)
35
36 name = p[-1]
37 parent = p[:-1]
38
39 for k in parent:
40 d = d[k]
41
42 result = {}
43
44 result["name"] = ".".join(p)
45 result["value"] = d[name]
46 result["type"] = d[name + "_type"]
47 result["doc"] = d[name + "_doc"]
48
49 try:
50 result["option"] = "-" + d[name + "_option"]
51 except KeyError:
52 result["option"] = "-" + result["name"]
53
54 if result["type"] == "keyword":
55 result["choices"] = d[name + "_choices"]
56
57 if result["type"] not in ("keyword", "flag", "string"):
58 result["units"] = d[name + "_units"]
59
60 return result
61
62
63 def keyword_handler(info, window, first_row):
64 value = tk.StringVar()
65 value.set(info["value"])
66
67 window.value = value
68
69 ttk.Label(window, text="Value:").grid(row=first_row, column=0, sticky=tk.W, padx=10)
70
71 j = first_row
72 for v in info["choices"].split(","):
73 ttk.Radiobutton(window, text=v, variable=value, value=v).grid(row=j, column=1, sticky=tk.W, padx=10)
74 j += 1
75
76 return j
77
78
79 def keyword_dialog(event):
80 generic_dialog(event, keyword_handler)
81
82
83 def generic_dialog(event, func):
84
85 info = parameter_info(event)
86
87 treeview = event.widget
88 item_id = treeview.focus()
89
90 root = treeview.master.master
91
92 window = tk.Toplevel(root)
93 window.wm_title(info["name"])
94
95 frame = ttk.Frame(window)
96 frame.grid(padx=5, pady=5)
97
98 j = 0
99
100 ttk.Label(frame, text="Name:").grid(row=j, sticky=tk.W, padx=10)
101 ttk.Label(frame, text=info["name"]).grid(row=j, column=1, sticky=tk.W, padx=10)
102 j += 1
103
104 ttk.Label(frame, text="Command-line option:").grid(row=j, sticky=tk.W, padx=10)
105 ttk.Label(frame, text=info["option"]).grid(row=j, column=1, sticky=tk.W, padx=10)
106 j += 1
107
108 doc_text = info["doc"]
109 ttk.Label(frame, text="Description:").grid(row=j, column=0, sticky=tk.W+tk.N, padx=10)
110 doc = tk.Text(frame, width=60, height=text_height(60, doc_text), wrap="word")
111 doc.insert(tk.END, doc_text)
112 doc.config(state=tk.DISABLED)
113 doc.grid(row=j, column=1, sticky=tk.W, padx=10)
114 j += 1
115
116 # spacer
117 ttk.Frame(frame, height=5).grid(row=j, column=0, columnspan=2)
118 j += 1
119
120 j = func(info, frame, j)
121
122 # the footer contains "OK" and "Cancel" buttons
123 footer = ttk.Frame(frame)
124 footer.grid(row=j, column=0, columnspan=2)
125
126 def is_valid(T, value):
127 if T == "number":
128 try:
129 dummy = float(value)
130 return True
131 except:
132 return False
133 elif T == "integer":
134 try:
135 dummy = int(value)
136 return True
137 except:
138 return False
139 elif T == "flag":
140 return value in ("True", "False")
141 else:
142 return True # all strings are valid
143
144 def set_item(name, value):
145 print "setting %s to %s" % (name, value)
146
147 def update_treeview(tree, name, value):
148 print "setting %s to %s in the tree view" % (name, value)
149
150 def ok_handler():
151 if info["type"] in ("keyword", "flag"):
152 value = frame.value.get()
153 else:
154 value = frame.value.get(1.0, "end").strip()
155
156 if is_valid(info["type"], value):
157 if value != str(info["value"]):
158 set_item(info["name"], value)
159 update_treeview(treeview, info["name"], value)
160 window.destroy()
161 else:
162 tkMessageBox.showerror("Invalid %s" % info["name"],
163 "'%s' is not a valid %s" % (value, info["type"]),
164 parent=window)
165
166 ttk.Button(footer, text="OK", width=20,
167 command=ok_handler).grid(pady=10, ipady=5, sticky=tk.W)
168 ttk.Button(footer, text="Cancel", width=20,
169 command=window.destroy).grid(row=0, column=1, pady=10, ipady=5, sticky=tk.W)
170
171 window.transient(root)
172 window.grab_set()
173 root.wait_window(window)
174
175
176 def scalar_handler(info, window, first_row, integer):
177
178 j = first_row
179
180 ttk.Label(window, text="Units:").grid(row=j, column=0, sticky=tk.W, padx=10)
181 ttk.Label(window, text=info["units"]).grid(row=j, column=1, sticky=tk.W, padx=10)
182 j += 1
183
184 ttk.Label(window, text="Value:").grid(row=j, column=0, sticky=tk.W+tk.N, padx=10)
185 text = tk.Text(window, width=60, height=1)
186 text.grid(row=j, column=1, sticky=tk.W, padx=10)
187 value = info["value"]
188 if integer:
189 text.insert("end", str(int(value)))
190 else:
191 text.insert("end", str(value))
192 j += 1
193
194 window.value = text
195
196 return j
197
198
199 def scalar_dialog(event):
200 generic_dialog(event, lambda a, b, c: scalar_handler(a, b, c, False))
201
202
203 def integer_dialog(event):
204 generic_dialog(event, lambda a, b, c: scalar_handler(a, b, c, True))
205
206
207 def string_handler(info, window, first_row):
208
209 ttk.Label(window, text="Value:").grid(row=first_row, column=0, sticky=tk.W+tk.N, padx=10)
210
211 value = info["value"]
212
213 j = first_row
214 text = tk.Text(window, width=60, height=text_height(60, value))
215 text.grid(row=j, column=1, sticky=tk.W, padx=10)
216 text.insert("end", value)
217 j += 1
218
219 window.value = text
220
221 return j
222
223
224 def string_dialog(event):
225 generic_dialog(event, string_handler)
226
227
228 def flag_handler(info, window, first_row):
229
230 value = tk.StringVar()
231 value.set(str(info["value"]))
232
233 ttk.Label(window, text="Value:").grid(row=first_row, column=0, sticky=tk.W, padx=10)
234
235 window.value = value
236
237 j = first_row
238 for v in ["True", "False"]:
239 ttk.Radiobutton(window, text=v, variable=value, value=v).grid(row=j, column=1, sticky=tk.W, padx=10)
240 j += 1
241
242 return j
243
244
245 def flag_dialog(event):
246 generic_dialog(event, flag_handler)
247
248
249 class App(object):
250 def __init__(self, master):
251 self.master = master
252 self.create_widgets(master)
253
254 def create_widgets(self, master):
255 frame = ttk.Frame(master, width=1000)
256 frame.pack(side="left", fill="both", expand=True, padx=10, pady=10)
257
258 tree = ttk.Treeview(frame)
259 tree.tag_bind('string', '<Double-Button-1>', string_dialog)
260 tree.tag_bind('keyword', '<Double-Button-1>', keyword_dialog)
261 tree.tag_bind('scalar', '<Double-Button-1>', scalar_dialog)
262 tree.tag_bind('integer', '<Double-Button-1>', integer_dialog)
263 tree.tag_bind('flag', '<Double-Button-1>', flag_dialog)
264
265 tree.pack(side="left", fill="both", expand=True)
266 tree["height"] = 40
267
268 tree['columns'] = ["value", "units", "description"]
269
270 tree.column('description', minwidth=500)
271 tree.column('units', minwidth=150, stretch=False)
272 tree.column('value', minwidth=150, stretch=False)
273 tree.column('#0', minwidth=200)
274
275 tree.heading("value", text="Value")
276 tree.heading("units", text="Units")
277 tree.heading("description", text="Description")
278
279 scrollbar = tk.Scrollbar(master, orient=tk.VERTICAL, command=tree.yview)
280 scrollbar.pack(expand=True, fill="y")
281
282 tree["yscrollcommand"] = scrollbar.set
283
284 self.load_config(master)
285
286 self.add_items("", tree, "", data)
287
288 def add_items(self, id, tree, root, config):
289 keys = config.keys()
290 keys.sort()
291
292 for k in keys:
293 if id == "":
294 parameter_name = k
295 else:
296 parameter_name = id + "." + k
297 if type(config[k]) == dict:
298 self.add_items(parameter_name, tree,
299 tree.insert(root, 'end', parameter_name, text=k,
300 values=("---", "---", "---")),
301 config[k])
302 else:
303 if not is_special(k):
304 parameter_value = config[k]
305 parameter_type = config[k + "_type"]
306 if parameter_type == "integer":
307 parameter_value = int(parameter_value)
308 if parameter_type not in ("string", "keyword", "flag"):
309 parameter_units = config[k + "_units"]
310 else:
311 parameter_units = "---"
312 doc = config[k + "_doc"]
313 tree.insert(root, 'end', parameter_name, text=k, values=(parameter_value, parameter_units, doc),
314 tags=parameter_type)
315
316 def load_config(self, master):
317 from argparse import ArgumentParser
318
319 parser = ArgumentParser()
320 parser.description = "PISM Configuration file editor"
321 parser.add_argument("FILE", nargs=1)
322 options = parser.parse_args()
323 args = options.FILE
324
325 if len(args) > 0:
326 self.input_file = args[0]
327 else:
328 master.update()
329 self.input_file = tkFileDialog.askopenfilename(parent=master,
330 filetypes=["JSON .json"],
331 title='Choose an input file')
332
333 if len(self.input_file) == 0:
334 print "No input file selected. Exiting..."
335 import sys
336 sys.exit(0)
337
338 load_config(self.input_file)
339
340
341 def create_nesting(config, name):
342 p = name.split(".")
343
344 d = config
345 for j in p[:-1]:
346 if j not in d.keys():
347 d[j] = {}
348
349 d = d[j]
350
351
352 def copy_parameter(source, destination, name):
353 create_nesting(destination, name)
354
355 p = name.split(".")
356
357 s = source
358 d = destination
359 for j in p[:-1]:
360 s = s[j]
361 d = d[j]
362
363 for k in s.keys():
364 if k.startswith(p[-1] + "_"):
365 d[k] = s[k]
366
367
368 if __name__ == "__main__":
369
370 import Tkinter as tk
371 import tkFileDialog
372 import ttk
373 import tkMessageBox
374
375 root = tk.Tk()
376 root.geometry("%dx%d" % (1200, 700))
377 root.wm_title("PISM configuration editor")
378
379 a = App(root)
380
381 root.lift()
382 root.mainloop()