#!/usr/bin/python # This script will open a small GUI window that simply # has a set of buttons that can be used to launch individual # monitoring programs. This provides a single point for # shift workers to access all of the different tools used # to monitor data quality. # To make this work from any gluon computer we # need to: # # 1. Make sure we are running on a "CONTROLS" gluon type # computer # # 2. Make sure TCL_LIBRARY is not set (which it is for CODA) # import os, sys if os.getenv('GLUON_TYPE') != 'CONTROLS': print '\n============ WARNING ===========' print 'You may need to run this on a computer' print 'with graphics software installed. If' print 'you get an error about tkinter then' print 'try this:' print '' print ' ssh gluon05 hdmonb.py' print '' # sys.exit(0) if "TCL_LIBRARY" in os.environ: print 'Removing TCL_LIBRARY from environment' del os.environ['TCL_LIBRARY'] import Tkinter print Tkinter.__file__ from Tkinter import * import subprocess from sys import platform class Tooltip: ''' It creates a tooltip for a given widget as the mouse goes on it. see: https://stackoverflow.com/questions/3221956/ what-is-the-simplest-way-to-make-tooltips- in-tkinter/36221216#36221216 http://www.daniweb.com/programming/software-development/ code/484591/a-tooltip-class-for-tkinter - Originally written by vegaseat on 2014.09.09. - Modified to include a delay time by Victor Zaccardo on 2016.03.25. - Modified - to correct extreme right and extreme bottom behavior, - to stay inside the screen whenever the tooltip might go out on the top but still the screen is higher than the tooltip, - to use the more flexible mouse positioning, - to add customizable background color, padding, waittime and wraplength on creation by Alberto Vassena on 2016.11.05. Tested on Ubuntu 16.04/16.10, running Python 3.5.2 TODO: themes styles support ''' def __init__(self, widget, bg='#FFFFEA', pad=(5, 3, 5, 3), text='widget info', waittime=400, wraplength=250): self.waittime = waittime # in miliseconds, originally 500 self.wraplength = wraplength # in pixels, originally 180 self.widget = widget self.text = text self.widget.bind("", self.onEnter) self.widget.bind("", self.onLeave) self.widget.bind("", self.onLeave) self.bg = bg self.pad = pad self.id = None self.tw = None def onEnter(self, event=None): self.schedule() def onLeave(self, event=None): self.unschedule() self.hide() def schedule(self): self.unschedule() self.id = self.widget.after(self.waittime, self.show) def unschedule(self): id_ = self.id self.id = None if id_: self.widget.after_cancel(id_) def show(self): def tip_pos_calculator(widget, label, tip_delta=(10, 5), pad=(5, 3, 5, 3)): w = widget s_width, s_height = w.winfo_screenwidth(), w.winfo_screenheight() width, height = (pad[0] + label.winfo_reqwidth() + pad[2], pad[1] + label.winfo_reqheight() + pad[3]) mouse_x, mouse_y = w.winfo_pointerxy() x1, y1 = mouse_x + tip_delta[0], mouse_y + tip_delta[1] x2, y2 = x1 + width, y1 + height x_delta = x2 - s_width if x_delta < 0: x_delta = 0 y_delta = y2 - s_height if y_delta < 0: y_delta = 0 offscreen = (x_delta, y_delta) != (0, 0) if offscreen: if x_delta: x1 = mouse_x - tip_delta[0] - width if y_delta: y1 = mouse_y - tip_delta[1] - height offscreen_again = y1 < 0 # out on the top if offscreen_again: # No further checks will be done. # TIP: # A further mod might automagically augment the # wraplength when the tooltip is too high to be # kept inside the screen. y1 = 0 return x1, y1 bg = self.bg pad = self.pad widget = self.widget # creates a toplevel window self.tw = Toplevel(widget) # Leaves only the label and removes the app window self.tw.wm_overrideredirect(True) win = Frame(self.tw, background=bg, borderwidth=0) label = Label(win, text=self.text, justify=LEFT, background=bg, relief=SOLID, borderwidth=0, wraplength=self.wraplength) label.grid(padx=(pad[0], pad[2]), pady=(pad[1], pad[3]), sticky=NSEW) win.grid() x, y = tip_pos_calculator(widget, label) self.tw.wm_geometry("+%d+%d" % (x, y)) def hide(self): tw = self.tw if tw: tw.destroy() self.tw = None ############################################################################################ if __name__ == '__main__': def RunProg(cmd): print 'Running: %s' % ' '.join(cmd) subprocess.Popen(cmd) def AddButton(label, help, cmd): global frame, wraplength cmdstr = ' '.join(cmd) my_wraplength = wraplength min_wraplength = int(float(len(cmdstr))*7.2) if my_wraplength < min_wraplength: my_wraplength = min_wraplength tooltip = help + '\n\nCommand to be run:\n' + cmdstr btn = Button(frame, text=label, width=20, command=lambda: RunProg(cmd)) Tooltip(btn, text=tooltip, wraplength=my_wraplength) btn.pack({"side": "top","ipadx":"20", "padx":"30", "pady":"5"}) global frame, wraplength # A couple of things use firefox. Make this also work on my mac browser = 'firefox' if platform == 'darwin' : browser = '/Applications/Firefox.app/Contents/MacOS/firefox-bin' wraplength=200 root = Tk() frame = Frame(root) frame["bg"] = "purple" title = Label(frame, text='Hall-D Monitoring Process Launcher', height=2, font=("Helvetica", 20)) title["bg"] = "purple" title["fg"] = "white" title.pack({"side": "top", "pady":"20", "padx":"10"}) #--------------------------------------------------------------------------------- # Add All buttons here AddButton("RootSpy", 'Start the RootSpy GUI for viewing online histograms' , ['RootSpy']) AddButton("HDvis", 'Browser-driven 3D event viewer' , [browser, 'https://halldweb.jlab.org/talks/2017/HDvis2/js/event.html#']) AddButton("hdview2", 'Old ROOT-based 2D event viewer.' , ['start_hdview2']) AddButton("Plot Browser", 'This will open a browser pointing to the Plot Browser ' 'website. From there, you can view archives of histograms ' 'from previous runs. Those are generated in various flavors ' 'including the "near-time" monitoring which is done using ' 'jobs on the scientific computing farm which are started ' 'automatically after a run ends. ' , [browser, 'https://halldweb.jlab.org/cgi-bin/data_monitoring/monitoring/plotBrowser.py']) AddButton("hdmongui", 'hdmongui is a program that shows the status of the ' 'online monitoring system (i.e. the one that feeds RootSpy). ' 'It will also allow you to ' 'start and stop the monitoring system. Note that the ' 'you typically won\'t need to start it yourself since ' 'the system is ' 'started automatically from the DAQ (with the exception ' 'of the RootSpy GUI)' , ['hdmongui.py']) AddButton("DAQmon", 'The DAQmon program produces some plots from very ' 'low-level values obtained from the DAQ system. These ' 'include scalers read directly from the SBCs (aka ROCs) ' , ['xterm', '-geometry', '+0+0', '-e', '/home/hdops/CDAQ/daq_dev_v0.31/daq/vme/src/rcm/daqmon/daqmon.sh']) AddButton("Triggermon", 'The Triggermon program analyzes the live data stream ' 'to give a near-real time view of the rates of each ' 'of the trigger bits while the DAQ is running. ' , ['echo', 'not yet implemented']) #--------------------------------------------------------------------------------- quit = Button(frame, text="Quit", command=root.quit) quit.pack({"side": "bottom", "pady":"20"}) root.title('Hall-D Monitoring Program Launcher') frame.pack() root.mainloop()