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()