...
Some time may be dedicated learning the basics (Tkinter, Tkinterbook)
Application skeleton starts with:
...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
import ecflow as ec from threading import Thread import Queue import sys try: import Tkinter as tk import tkFont except ImportError: # 3.x import tkinter as tk # Tkinter import tkinter.font as tkFont import ttk PROGRAM_NAME = "ecflowview-treeview" BUTTON_FONT = ('times', 12) MONO_FONT = ('lucidatypewriter', 14, 'bold') tree_columns = (# 'status', # 'since', 'path', "host", "id", "checked", "node") colors = { 'complete': "yellow", 'active': "green", 'aborted': "red", 'submitted': "turquoise", "shutdown": "pink", "halted": "violet", "queued": "lightblue", "unknown": "grey", "suspended": "orange" } displays = dict() running = True PACE = 30 def guess_host(node, kind=None): if node is None: return "" value = None for var in node.variables: name = var.name() if kind is None and name == "ECF_JOB_CMD": if "%WSHOST%" in var.value(): return guess_host(node, "WSHOST") elif "%SCHOST%" in var.value(): return guess_host(node, "SCHOST") elif "%HOST%" in var.value(): return guess_host(node, "HOST") else: value = var.value() elif kind == "SCHOST" and name == "SCHOST": return var.value() elif kind == "WSHOST" and name == "WSHOST": return var.value() elif kind == "HOST" and name == "HOST": return var.value() else: pass if value is None and node.get_parent(): return guess_host(node.get_parent(), kind) return "???" def guess_qid(node, path=None): if node is None: return "" if path is None and not isinstance(node, ec.Task): return "" if 0: content = "%s" % node for line in content.split("\n"): print line if "edit ECF_JOB " in line: dummy, name, value = line.split() print line, dummy, name, value return value elif 0: for var in node.variables: if var.name() == "ECF_JOB": return var.value() + ".sub" elif path is None: return guess_qid(node.get_parent(), node.get_abs_node_path()) else: pass loc = None for var in node.variables: if var.name() == "ECF_HOME": loc = var.value() + path; break if loc is None and node.get_parent() is not None: return guess_qid(node.get_parent(), path) elif loc is None: return "???" import commands a = commands.getstatusoutput("ls -ltr %s*.sub | awk '{print $8}'" % loc) item =a[1].split('\n') print path, item[0] fd = open(item[0]) line = "???" for line in fd.readlines(): if "has been submitted" in line: full = line.split() if "llsubmit:" in line: return full[3] return full[2] print "# last: ", line return line def guess_check(master, item, node, host, qid): SLURM_ROOT="/usr/local/apps/slurm/current/bin" qstat="/usr/local/apps/propbs/bin/qstat" # PBS llq="/usr/lpp/LoadL/full/bin/llq -f %id %jn %o %c %st %nh" import commands if "lxa" in host or "lxb" in host or host=="linux_cluster": print node, host, qid SGE_ROOT="/usr/local/apps/sge" SGE_ROOT="/usr/local/apps/sge/sge6_2u5" sge="/usr/local/apps/sge/sge6_2u5/bin/lx24-amd64/qstat -u emos -f" cmd = "ssh lxab 'SGE_ROOT=%s %s' | grep %d" % (SGE_ROOT, sge, int(qid)) a = commands.getstatusoutput(cmd) print cmd, a if a[0] != 0: return full = a[1].split() if len(full) > 6 and full[4]=='r': print "#YES %s is running since %s %s" % (node, full[5], full[6]) master._tree.set(item, column="checked", value="%s" % full[6]) else: print full elif "c2a" in host or "c2b" in host or "ecg" in host: print node, host, qid a = commands.getstatusoutput("rsh %s %s %s | grep %s" % (host, llq, qid, qid)) print a elif "cc" in host: print node, host, qid a = commands.getstatusoutput("ssh %s qstat %s" % (host, qid)) print a else: print node, host, qid def textw(clnt, fullpath, kind, node=None): # create child window win = tk.Toplevel() # display message if kind == "variables": msg = "" for var in node.variables: msg += "%-20s: %s\n" %(var.name(), var.value()) message = msg elif kind == "info": message = "%s" % node else: message = clnt.client.get_file(fullpath, kind) # text = tk.Label(win, text=message) win.title(fullpath + " - " + kind) s = tk.Scrollbar(win) T = tk.Text(win) T.focus_set() s.pack(side=tk.RIGHT, fill=tk.Y) T.pack(side=tk.LEFT, fill=tk.Y) s.config(command=T.yview) T.config(yscrollcommand=s.set) for i in message.split("\n"): T.insert(tk.END, i + "\n") T.yview(tk.MOVETO, 1.0) # ysb = ttk.Scrollbar(text, orien='vertical', # command=text.yview) # xsb = ttk.Scrollbar(self, orien='horizontal', # command=text.xview) # text.configure(yscrollcommand=ysb.set, # xscrollcommand=xsb.set) # text.pack() # quit child window and return to root window # the button is optional here, simply use the corner x of the child window # tk.Button(win, text='close', command=win.destroy).pack() class PaceKeeper(): def __init__(self, master, queue): self.thr = Thread(target=self.process, args=(queue, running)) self._master = master self.thr.start() def process(self, queue, running): import time if not running: self.thr.stop(); return queue.put(self._master.update) time.sleep(PACE) def run(self): self.update() def update(self, verbose=False): import time if 1: # while running: print time.clock() self._master.update() time.sleep(PACE) class ContextMenu(object): def __init__(self, dad): self.dad = dad self.cmn = tk.Menu(dad, tearoff=0) self.cmn.add_command(label="Info", command=dad.info) self.cmn.add_command(label="Script", command=dad.script) self.cmn.add_command(label="Manual", command=dad.manual) self.cmn.add_command(label="Job", command=dad.job) self.cmn.add_command(label="Output", command=dad.jobout) self.cmn.add_command(label="Why", command=dad.why) self.cmn.add_command(label="TimeLine", command=dad.timeline) self.cmn.add_command(label="Variables", command=dad.variables) self.cmn.add_command(label="Messages", command=dad.messages) class MenuBar(tk.Frame): def __init__(self, parent): tk.Frame.__init__(self, parent) self.__helpButton = self.__createHelp() self.__updateButton = tk.Button(self, text='Update', font= BUTTON_FONT, command= parent.update) self.__checkButton = tk.Button(self, text='Check', font= BUTTON_FONT, command= parent.check) self.__quitButton = tk.Button( self, text='Quit', font= BUTTON_FONT, command= parent.quit) col = 0 self.__quitButton.grid(row=0, column=col) col += 1 self.__updateButton.grid(row=0, column=col) col += 1 self.__checkButton.grid(row=0, column=col) col += 1 self.__helpButton.grid(row=0, column=col) col += 1 timed = tk.Label(self, font=BUTTON_FONT, textvariable= parent.last_update, ) timed.grid(row=0, column=col) self.__createCheck(col+1) def __createHelp(self): mb = tk.Menubutton(self, font=BUTTON_FONT, relief= tk.RAISED, text= 'Help') menu = tk.Menu(mb) mb['menu'] = menu menu.add_command(command= self.__url, label="confluence tutorial?") menu.add_command(command= self.__url2, label="tkinter?") return mb def __url2(self): self.__url(1) def __url(self, num=0): import os url = "https://softwareconfluence.ecmwf.int/wiki/display/ECFLOW/Documentation" if num == 1: url = "http://effbot.org/tkinterbook/" os.system("firefox " + url) def __createCheck(self, col): col=4 keys = colors.keys() keys.append("unfold") for kind in sorted(keys): var = tk.IntVar() displays[kind] = var check = tk.Checkbutton(self, text= kind, variable= var, onvalue= 1, offvalue= 0,) if kind in ("active", "aborted", "submitted", "unfold"): check.select() check.grid(row=0, column=col) col += 1 def sortby(tree, col, desc, node=''): data = [] for item in tree.get_children(node): for kid in tree.get_children(item): data.append((tree.set(kid, col), kid, item)) data.sort(reverse=desc) for idx, item in enumerate(data): tree.move(item[1], item[2], idx) tree.heading( col, command= lambda col= col: sortby(tree, col, int(not desc))) class Client(object): def __init__(self, host, port): self.client = ec.Client(host, port) self.host = host self.port = port def sync(self, top, parent): self.client.sync_local() load = self.defs() if load is None: print "empty server %s %d" %(self.host, self.port) for item in load.suites: self.process(top, parent, item) def defs(self): return self.client.get_defs() def process(self, top, parent, item): status = "%s" % item.get_state() fullpath = item.get_abs_node_path() if not displays[status].get(): return unfold = displays["unfold"].get() if isinstance(item, ec.Task) or not unfold: name = item.name() if isinstance(item, ec.Task): host = guess_host(item) checked = "nope" qid = guess_qid(item) else: host= ""; qid = ""; checked = "" if unfold: name = fullpath; fullpath = "" dad = top._tree.insert(parent, 'end', values= (fullpath, host, qid, checked, ), tags= status, text=name, open=unfold) else: dad = parent if 0: ilen = tkFont.Font().measure(fullpath) if ilen > 80: self._tree.column(1, width=ilen) else: pass if not isinstance(item, ec.Task): for kid in item.nodes: self.process(top, dad, kid) else: pass class Application(tk.Frame): def __init__(self, master=None, client=None, queue=None): width = 768 height = 480 tk.Frame.__init__(self, master, width=width, height=height,) self.__queue = queue if client is None: HOST = "vsms1"; self.__client = [ ] PORT = 43333; self.__client.append(Client(HOST, PORT)) PORT = 32222; self.__client.append(Client(HOST, PORT)) PORT = 31415; self.__client.append(Client(HOST, PORT)) print self.__client else: self.__client = [client ] # self.canvas = tk.Canvas(width=width, height=height, bg='black') self.grid() self.last_update = tk.StringVar() self.last_update.set("00:00") self.createWidgets() self.cwn = None # self.canvas.after(50, self.check_queue) def createWidgets(self): self.__menuBar = MenuBar(self) row = 0 self.__menuBar.grid(row=row, column=0, columnspan=3, sticky=tk.W) row += 1 self.__wins = dict() rowx = 1 colx = 0 if 1: self._tree = ttk.Treeview(self, columns=tree_columns, selectmode='extended', # justify="right", # show="headings" # path disappear ) ysb = ttk.Scrollbar(self, orien='vertical', command=self._tree.yview) xsb = ttk.Scrollbar(self, orien='horizontal', command=self._tree.xview) self._tree.configure(yscrollcommand=ysb.set, # justify="right", xscrollcommand=xsb.set) row=1 self._tree.grid(row=row, column=0, sticky='nsew', in_=self) ysb.grid(row=row, column=len(tree_columns)+1, sticky='wens') xsb.grid(sticky='ewns', columnspan=len(tree_columns),) self.grid_columnconfigure(0, weight=1,) self.grid_rowconfigure(row, weight=1) pos = 0 self.process() for kind, color in colors.items(): self._tree.tag_configure(kind, background=color) self._tree.bind("<Double-1>", self.OnDoubleClick) # create a popup menu self.cmn = ContextMenu(self) self._tree.bind("<Button-3>", self.popup) for col in tree_columns: self._tree.heading( col, text=col.title(), command= lambda c=col: sortby(self._tree, c, 0)) width = tkFont.Font().measure(col.title()) if col == "status": ilen = tkFont.Font().measure(kind) if ilen > width: width = ilen else: width = 80 self._tree.column(col, width= width, stretch= True, anchor=tk.E) pos += 1 self.grid() self.pack(fill='both', expand=True) self.update() def info(self): return self.cmgen("info") def manual(self): return self.cmgen("manual") def script(self): return self.cmgen("script") def job(self): return self.cmgen("job") def jobout(self): return self.cmgen("jobout") def why(self): return self.cmgen("why") def timeline(self): return self.cmgen("timeline") def variables(self): return self.cmgen("variables") def messages(self): return self.cmgen("messages") def cmgen(self, kind="info"): item = self.cwn full = self._tree.item(item,"values") path = self._tree.item(item,"text") print path, full, item print "#hello:" # , node.get_abs_node_path() upp = item; print self._tree.parent(upp) while self._tree.parent(upp) != '': upp = self._tree.parent(upp) idx = self._tree.index(upp) if path[0] == '/': fullpath = path else: fullpath = full[0] if idx < 0 or idx >= len(self.__client): return clnt = self.__client[idx] defs = clnt.defs() node = defs.find_abs_node(fullpath) if kind in ("script", "manual", "job", "output", "jobout"): textw(clnt, fullpath, kind) elif kind in ("variables",): textw(clnt, fullpath, kind, node) elif kind in ("info"): textw(clnt, fullpath, kind, node) else: print "# not yet", node def popup(self, event): item = self._tree.identify('row',event.x,event.y) if item is None: print "???"; return print "context for", self._tree.item(item,"values"), \ self._tree.item(item,"text") self.cwn = item self.cmn.cmn.post(event.x_root, event.y_root) def OnDoubleClick(self, event): item = self._tree.identify('row',event.x,event.y) if item is None: print "???"; return print "you clicked on", self._tree.item(item,"values"), \ self._tree.item(item,"text") def check_queue(self): try: self.__queue.get(block=False) except Queue.Empty: pass else: self.update() self.canvas.after(50, self.check_queue) def unfold(self, item=None): if item is None: for item in self._tree.get_children(''): self.unfold(item) return item.open() for kid in self._tree.get_children(item): self.unfold(kid) def quit(self): global running running = False self.destroy() sys.exit(0) def check(self, item=None): if item is None: for item in self._tree.get_children(''): self.check(item) else: pass else: for kid in self._tree.get_children(item): self.check(kid) full = self._tree.item(item, 'values'), text = self._tree.item(item, 'text'), if len(full[0]) > 3: full = full[0] if full[1] != "???": unfold = displays["unfold"].get() if unfold: path = text else: path = full[0] guess_check(self, item, path, full[1], full[2]) def process(self): for clt in self.__client: parent = self._tree.insert('', 'end', text="%s@%s" % (clt.host, clt.port), open= True) clt.sync(self, parent) def update(self): import time now = time.localtime(time.time()) hhmmss = "%02d:%02d:%02d" % (now.tm_hour, now.tm_min, now.tm_sec) self.last_update.set(hhmmss) for item in self._tree.get_children(''): self._tree.delete(item) self.process() if __name__ == '__main__': if len(sys.argv) > 2: HOST = sys.argv[1] PORT = int(sys.argv[2]) print "# " + PROGRAM_NAME + ": contact %s %d" % (HOST, PORT) client = Client(HOST, PORT) else: client = None queue = Queue.Queue() app = Application(client=client, queue=queue) app.master.title(PROGRAM_NAME) PaceKeeper(app, queue) app.mainloop() |
...