rcStatusMonitor.py

Go to the documentation of this file.
00001 #!/usr/local/bin/python
00002 #
00003 #                               Copyright 2005
00004 #                                     by
00005 #                        The Board of Trustees of the
00006 #                     Leland Stanford Junior University.
00007 #                            All rights reserved.
00008 #
00009 
00010 __facility__ = "Online"
00011 __abstract__ = "Status Monitor for the Script Engine"
00012 __author__   = "Selim Tuvi <stuvi@slac.stanford.edu> SLAC - GLAST I&T/Online"
00013 __date__     = "2005/07/23 00:08:27"
00014 __updated__  = "$Date: 2006/04/28 01:41:21 $"
00015 __version__  = "$Revision: 1.9 $"
00016 __release__  = "$Name: HEAD $"
00017 __credits__  = "SLAC"
00018 
00019 import LICOS.copyright_SLAC
00020 
00021 
00022 from qt import *
00023 import logging as log
00024 
00025 class ListViewToolTip(QToolTip):
00026   """!\brief A class to allow tooltips in a listview.
00027 
00028   """
00029   def __init__(self, view, column, truncatedOnly=0):
00030     """!\brief ListViewToolTip constructor.
00031 
00032     \param view          QListView instance
00033     \param column        Listview column
00034     \param truncatedOnly Only display the tooltip if the column data is truncated
00035     """
00036     QToolTip.__init__(self, view.viewport())
00037     self.__view = view
00038     self.__col  = column
00039 
00040   def maybeTip(self, pos):
00041     """!\brief Draw the tooltip.
00042 
00043     \param pos Tooltip position.
00044     """
00045     item = self.__view.itemAt(pos)
00046     if item is not None:
00047       #if self.__view.header().sectionSize(self.__col) >= \
00048       #    item.width(self.__view.fontMetrics(), self.__view, self.__col):
00049       #  return
00050       tipString = item.text(self.__col)
00051       cr = self.__view.itemRect(item)
00052       headerPos = self.__view.header().sectionPos(self.__col)
00053       cr.setLeft(headerPos)
00054       cr.setRight(headerPos + self.__view.header().sectionSize(self.__col))
00055       self.tip(cr, tipString)
00056 
00057 class StatusItem(QListViewItem):
00058   """!\brief Custom QListViewItem for displaying the status panel items.
00059 
00060   """
00061   def __init__(self, *args):
00062     """!\brief StatusItem constructor.
00063 
00064     \param args QListViewItem arguments.
00065     """
00066     QListViewItem.__init__(self, *args)
00067     self.__alarmExpr = None
00068     self.__stale = False
00069 
00070   def paintCell(self, p, cg, column, width, align):
00071     """!\brief Draw the status panel item.
00072 
00073     Draw the item so that it appears to have line dividers between each item.
00074 
00075     If the item's alarm expression is true then paint the item in red.
00076 
00077     If the item's alarm expression starts with the character '@' then
00078     quote the value before adding it to the expression.
00079 
00080     \param p      Painter
00081     \param cg     Color group
00082     \param column Listview column
00083     \param width  Width of the cell
00084     \param align  Alignment of the cell data.
00085     """
00086     dg = qApp.palette().disabled()
00087     g = QColorGroup(cg)
00088     g.setColor( QColorGroup.Base, Qt.white)
00089     g.setColor( QColorGroup.Foreground, Qt.black)
00090     if self.__alarmExpr is not None:
00091       # If alarm expr starts with '@' then treat the monitored
00092       # value as a string in the expression.
00093       if self.__alarmExpr.startswith("@"):
00094         expr = "'"+str(self.text(1))+"'" + self.__alarmExpr[1:]
00095       else:
00096         expr = str(self.text(1)) + self.__alarmExpr
00097       try:
00098         if eval(expr):
00099           g.setColor( QColorGroup.Text, Qt.red)
00100         else:
00101           g.setColor( QColorGroup.Text, Qt.black)
00102       except:
00103         log.exception("Error evaluating expression \"%s\"" % expr)
00104 
00105     # Do the standard painting
00106     if self.__stale and column == 1:
00107       QListViewItem.paintCell(self,p,dg,column,width,align)
00108     else:
00109       QListViewItem.paintCell(self,p,g,column,width,align)
00110 
00111     p.setPen( QPen( cg.dark(), 1 ) )
00112     p.drawLine( 0, self.height() - 1, width, self.height() - 1 )
00113     p.drawLine( width - 1, 0, width - 1, self.height() )
00114 
00115   def setText(self, column, value):
00116     """!\brief Set the text of the item.
00117 
00118     \param column Listview column
00119     \param value  Listview item value
00120     """
00121     QListViewItem.setText(self, column, value)
00122 
00123   def setAlarmExpr(self, alarmExpr):
00124     """!\brief Set the alarm expression.
00125 
00126     \param alarmExpr Alarm expression
00127     """
00128     self.__alarmExpr = alarmExpr
00129 
00130   def setStale(self, stale):
00131     """!\brief Set stale flag.
00132 
00133     \param stale Stale flag
00134     """
00135     self.__stale = stale
00136 
00137   def isStale(self):
00138     """!\brief Check if the status item is stale or not.
00139 
00140     \return Boolean indicating whether the item is stale or not.
00141     """
00142     return self.__stale
00143 
00144 
00145 class WatchItem(object):
00146   """!\brief Watch item class that keeps track of status panel items.
00147 
00148   """
00149   def __init__(self, accessor, key, label, alarmExpr, isStale=None):
00150     """!\brief WatchItem constructor.
00151 
00152     \param accessor  Accessor method
00153     \param key       Item key
00154     \param label     Item label
00155     \param alarmExpr Alarm expression
00156     \param isStale   Optional isStale method
00157     """
00158     self.__accessor = accessor
00159     self.__statusItem = None
00160     self.__key = key
00161     self.__label = label
00162     self.__alarmExpr = alarmExpr
00163     self.__inView = 0
00164     self.__isStale = isStale
00165 
00166   def evaluate(self):
00167     """!\brief Evaluate the expression by calling the accessor method.
00168 
00169     \return Accessor method return value or "N/A" if the return value is None
00170     """
00171     if self.__key is not None:
00172       if self.__accessor.has_key(self.__key):
00173         return self.__accessor[self.__key]
00174       else:
00175         return 'N/A'
00176     else:
00177       val = self.__accessor()
00178       if val is None:
00179         return 'N/A'
00180       else:
00181         return val
00182 
00183   def getLabel(self):
00184     """!\brief Retrieve the label.
00185 
00186     \return Watch item label
00187     """
00188     return self.__label
00189 
00190   def getAlarmExpr(self):
00191     """!\brief Retrieve the alarm expression.
00192 
00193     \return Alarm expression value
00194     """
00195     return self.__alarmExpr
00196 
00197   def getStatusItem(self):
00198     """!\brief Retrieve the status listview item that is
00199     associated with this watch item.
00200 
00201     \return StatusItem instance
00202     """
00203     return self.__statusItem
00204 
00205   def setStatusItem(self, si):
00206     """!\brief Set the status item.
00207 
00208     \param si StatusItem instance
00209     """
00210     self.__statusItem = si
00211 
00212   def isInView(self):
00213     """!\brief Check if the status item for this watch item has
00214     already been instantiated.
00215 
00216     \return True if the status item for this watch item has been created,
00217             False otherwise.
00218     """
00219     return self.__inView
00220 
00221   def setInView(self):
00222     """!\brief Set the watch item as being in view.
00223 
00224     """
00225     self.__inView = 1
00226 
00227   def setNotInView(self):
00228     """!\brief Set the watch item as not being in view.
00229 
00230     """
00231     self.__inView = 0
00232 
00233 
00234   def setAccessor(self, accessor, key):
00235     """!\brief Set the accessor for this watch item.
00236 
00237     \param accessor Accessor method
00238     \param key      Key that uniquely identifies the item
00239     """
00240     self.__accessor = accessor
00241     self.__key      = key
00242 
00243   def isStale(self):
00244     """!\brief Check if the watch item is stale or not.
00245 
00246     \return Boolean indicating whether the item is stale or not.
00247     """
00248     if self.__isStale is not None:
00249       return self.__isStale()
00250     else:
00251       return False
00252 
00253 
00254 class rcStatusPanel(QWidget):
00255   """!\brief Status Panel widget.
00256 
00257   """
00258   def __init__(self,parent = None,name = None,fl = 0):
00259     """!\brief rcStatusPanel constructor.
00260 
00261     \param parent Parent GUI
00262     \param name   GUI name
00263     \param fl     GUI flags
00264     """
00265     QWidget.__init__(self,parent,name,fl)
00266 
00267     self.contextMenu = QPopupMenu( self )
00268     self.contextMenu.insertItem( "&Copy", self.copyToClipboard, Qt.CTRL+Qt.Key_C)
00269 
00270     self.clip = QApplication.clipboard()
00271 
00272     if not name:
00273       self.setName("rcStatusPanel")
00274 
00275 
00276     rcStatusPanelLayout = QGridLayout(self,1,1,0,6,"rcStatusPanelLayout")
00277 
00278     self.statusView = QListView(self,"statusView")
00279     self.statusView.addColumn(self.tr("Data"))
00280     self.statusView.addColumn(self.tr("Value"))
00281     self.statusView.setAllColumnsShowFocus(1)
00282     self.statusViewTip = ListViewToolTip(view=self.statusView, column=1,
00283                                          truncatedOnly=1)
00284 
00285     rcStatusPanelLayout.addWidget(self.statusView,0,0)
00286 
00287     QObject.connect(self.statusView,
00288                     SIGNAL("contextMenuRequested(QListViewItem*,const QPoint&,int)"),
00289                     self.contextMenuShow
00290                    )
00291 
00292   def contextMenuShow(self, item, point, column):
00293     """!\brief Bring up the context menu on right mouse click.
00294 
00295     \param item   Listview item
00296     \param point  Position where the mouse was clicked
00297     \param column Listview column
00298     """
00299     self.selectedItem = item
00300     self.contextMenu.exec_loop(point)
00301 
00302   def copyToClipboard(self):
00303     """!\brief Copy the item value to the clipboard.
00304 
00305     """
00306     if self.statusView.selectedItem() is not None:
00307       self.clip.setText(self.statusView.selectedItem().text(1))
00308 
00309 
00310 class rcStatusMonitor(object):
00311   """!\brief Monitoring class for the status panel.
00312 
00313   """
00314   def __init__(self, interval, gui=None):
00315     """!\brief rcStatusMonitor constructor.
00316 
00317     \param interval The interval that the watch items get updated
00318     \param gui      Parent GUI
00319     """
00320     self.__interval = interval
00321     self.__gui = gui
00322     self.__watchList = {}
00323     self.__labelSeq = []
00324     self.__timer = QTimer(gui)
00325     self.__lastItem = None
00326     QObject.connect(self.__timer, SIGNAL("timeout()"), self.updateWatchItems)
00327 
00328   def startWatch(self):
00329     """!\brief Start the watch.
00330 
00331     """
00332     if not self.__timer.isActive():
00333       self.__timer.start(self.__interval)
00334 
00335   def stopWatch(self):
00336     """!\brief Stop the watch.
00337 
00338     """
00339     if self.__timer.isActive():
00340       self.__timer.stop()
00341 
00342   def addWatchItem(self, label, accessor, key=None, alarmExpr=None, isStale=None):
00343     """!\brief Add the watch item to the list.
00344 
00345     \param label     Watch item label
00346     \param accessor  Accessor method
00347     \param key       Unique key for this watch item
00348     \param alarmExpr Alarm expression
00349     \param isStale   isStale method
00350     """
00351     if not self.__watchList.has_key(label):
00352       self.__watchList[label] = WatchItem(accessor, key, label, alarmExpr, isStale)
00353       self.__labelSeq.append(label)
00354     else:
00355       self.__watchList[label].setAccessor(accessor, key)
00356 
00357   def saveWatch(self):
00358     """!\brief Save watch items.
00359 
00360     """
00361     self.__saveWatchList = self.__watchList.copy()
00362     self.__saveLabelSeq = self.__labelSeq[:]
00363 
00364   def restoreWatch(self):
00365     """!\brief Restore watch items.
00366 
00367     """
00368     self.__watchList = self.__saveWatchList.copy()
00369     self.__labelSeq  = self.__saveLabelSeq[:]
00370     self.__gui.statusView.clear()
00371     self.__lastItem = None
00372     for watchItem in self.__watchList.values():
00373       watchItem.setNotInView()
00374 
00375   def updateWatchItems(self):
00376     """!\brief Watch item update loop which wakes up based on the interval.
00377 
00378     """
00379     for label in self.__labelSeq:
00380       wi = self.__watchList[label]
00381       if self.__gui is None:
00382         print label, wi.evaluate()
00383       else:
00384         if not wi.isInView():
00385           if self.__lastItem is None:
00386             si = StatusItem(self.__gui.statusView, label)
00387           else:
00388             si = StatusItem(self.__gui.statusView, self.__lastItem, label)
00389           si.setAlarmExpr(wi.getAlarmExpr())
00390           self.__lastItem = si
00391           wi.setStatusItem(si)
00392           wi.setInView()
00393         else:
00394           si = wi.getStatusItem()
00395         si.setText(1, str(wi.evaluate()))
00396         if si.isStale() != wi.isStale():
00397           si.setStale(wi.isStale())
00398           si.repaint()
00399 
00400 
00401 
00402 #~ if __name__ == '__main__':
00403 
00404   #~ from random import random
00405   #~ import sys
00406   #~ from RunControlMainGUIImpl import *
00407   #~ from RunControlCommon import *
00408 
00409   #~ class Dummy(object):
00410     #~ def __init__(self):
00411       #~ pass
00412 
00413     #~ def getValue(self):
00414       #~ return random()
00415 
00416   #~ app = QApplication(sys.argv)
00417 
00418   #~ rcCommon = RunControlCommon()
00419 
00420   #~ gui = RunControlMainGUIImpl(rcCommon)
00421 
00422   #~ #statMon = rcStatusMonitor(1000)
00423   #~ statMon = rcStatusMonitor(1000, gui)
00424   #~ dummy = Dummy()
00425   #~ statMon.addWatchItem('TestValue', dummy.getValue)
00426   #~ statMon.startWatch()
00427   #~ app.setMainWidget(gui)
00428   #~ gui.show()
00429   #~ app.exec_loop()
00430 
00431 

Generated on Thu Apr 27 20:52:43 2006 for LICOS L02-01-00 by doxygen 1.4.6-NO