00001
00002
00003
00004
00005
00006
00007
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
00048
00049
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
00092
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
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
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431