#!/usr/local/bin/python
#
#                               Copyright 2002
#                                     by
#                        The Board of Trustees of the
#                     Leland Stanford Junior University.
#                            All rights reserved.
#

__facility__ = "Online"
__abstract__ = "Status Monitor for Run Control"
__author__   = "Selim Tuvi <stuvi@slac.stanford.edu> SLAC - GLAST I&T/Online"
__date__     = ("$Date: 2004/09/07 20:52:54 $").split(' ')[1]
__version__  = "$Revision: 2.4 $"
__release__  = "$Name:  $"
__credits__  = "SLAC"

from qt import *

class StatusItem(QListViewItem):
  def __init__(self, lv, *args):
    QListViewItem.__init__(self, lv, *args)
    self.alarmExpr = None
    self.__lv = lv
    self.__combo = {}
    self.__setComboValue = {}

  def setAlarmExpr(self, alarmExpr):
    self.alarmExpr = alarmExpr
    
  def showEditor(self):
    for (col, combo) in self.__combo.items():
      self.placeEditor(col, combo)
      combo.show()
      combo.setFocus()
      
  def setText(self, col, text):
    if col in self.__combo:
      self.__combo[col].setCurrentText(text)
    QListViewItem.setText(self, col, text)
      
  def setCombo(self, col, strList):
    self.__setComboValue[col] = lambda: self.setValue(col)
    if col not in self.__combo:
      self.__combo[col] = QComboBox(False, self.__lv.viewport())
      self.__combo[col].hide()
      self.__combo[col].installEventFilter(self.__lv)
      QObject.connect(self.__combo[col], SIGNAL("activated(int)"), self.__setComboValue[col])   
    c = self.__combo[col]      
    c.blockSignals(1)
    c.clear()
    for txt in strList:
      c.insertItem(txt)
    self.setText(col, c.currentText())
    c.blockSignals(0)
    
  def setValue(self, col):
    if self.__combo[col]:
      self.setText(col, self.__combo[col].currentText())
      self.__lv.comboChanged(self, col)
    
  def placeEditor(self, col, w):
    r = self.__lv.itemRect(self)
    if not r.size().isValid():
      self.__lv.ensureItemVisible(self)
      self.__lv.repaintContents(False)
      r = self.__lv.itemRect(self)
    r.setX(self.__lv.header().sectionPos(col))
    r.setWidth(self.__lv.header().sectionSize(col) - 1)
    r = QRect(self.__lv.viewportToContents(r.topLeft()), r.size())
    w.resize(r.size())
    self.__lv.moveChild(w, r.x(), r.y())
    
  def hideEditor(self):
    for combo in self.__combo.values():
      combo.hide()

class WatchItem(object):
  def __init__(self, accessor, key, label, alarmExpr):
    self.__accessor = accessor
    self.__statusItem = None
    self.__key = key
    self.__label = label
    self.__alarmExpr = alarmExpr
    self.__inView = 0
    self.__cnt = 0


  def evaluate(self):
    if self.__key is not None:
      if self.__accessor.has_key(self.__key):
        return self.__accessor[self.__key]
      else:
        return 'N/A'
    else:
      val = self.__accessor()
      if val is None:
        return 'N/A'
      else:
        return val

  def getLabel(self):
    return self.__label

  def getAlarmExpr(self):
    return self.__alarmExpr

  def getStatusItem(self):
    return self.__statusItem

  def setStatusItem(self, si):
    self.__statusItem = si

  def isInView(self):
    return self.__inView

  def setInView(self):
    self.__inView = 1

  def setAccessor(self, accessor, key):
    self.__accessor = accessor
    self.__key      = key
    
class StatusListView(QListView):
  def __init__(self, *args):
    QListView.__init__(self, *args)  
    self.header().setStretchEnabled(True, -1)
    self.connect(self.header(),SIGNAL("sizeChange(int,int,int)"),
                    self.updateEditorSize)
    self.connect(self.header(),SIGNAL("clicked(int)"),
                    self.toggleSort)
    
  def setCurrentItem(self, item):
    if item:
      self.currentItem().hideEditor()
    QListView.setCurrentItem(self, item)
    self.currentItem().showEditor()
    
  def resizeEvent(self, e):
    QListView.resizeEvent(self, e)
    if self.currentItem():
      self.currentItem().showEditor()
      
  def updateEditorSize(self, section, oldSize, newSize):   
    if self.currentItem():     
      it = self.currentItem()
      self.currentItem().showEditor()
      
  def toggleSort(self, col):
    if self.currentItem():
      self.currentItem().hideEditor()
      self.currentItem().showEditor()
      
  def clear(self):
    if self.currentItem():
      self.currentItem().hideEditor()
    QListView.clear(self)

  def comboChanged(self, item, col):
    self.emit(PYSIGNAL("comboChanged(QListViewItem*,int)"), (item, col))
    
  def eventFilter(self, o, e):
    i = self.currentItem()
    if e.type() == QEvent.KeyPress:
      if (e.key() == Qt.Key_Up or e.key() == Qt.Key_Down) and \
       (o != self or o != self.viewport()) and not (e.state() & Qt.ControlButton):
        QApplication.sendEvent(self, e)
        return True
      elif (e.key() == Qt.Key_Return or e.key() == Qt.Key_Enter) and \
       o.__class__.__name__ == 'QComboBox':
        ke2 = QKeyEvent(QEvent.KeyPress, Qt.Key_Space, 0, 0)
        QApplication.sendEvent(o, ke2)        
    if not o or not e:
      return True
    return QListView.eventFilter(self, o, e)

class rcStatusPanel(QWidget):
  def __init__(self,parent = None,name = None,fl = 0):
    QWidget.__init__(self,parent,name,fl)

    self.contextMenu = QPopupMenu( self )
    self.contextMenu.insertItem( "&Copy", self.copyToClipboard, Qt.CTRL+Qt.Key_C)

    self.clip = QApplication.clipboard()

    if not name:
      self.setName("rcStatusPanel")


    rcStatusPanelLayout = QGridLayout(self,1,1,0,6,"rcStatusPanelLayout")

    self.statusView = StatusListView(self,"statusView")
    self.statusView.addColumn("Data")
    self.statusView.addColumn("Value")
    self.statusView.addColumn("Combo1")
    self.statusView.addColumn("Combo2")
    self.statusView.setAllColumnsShowFocus(1)

    rcStatusPanelLayout.addWidget(self.statusView,0,0)

    QObject.connect(self.statusView,
                    SIGNAL("contextMenuRequested(QListViewItem*,const QPoint&,int)"),
                    self.contextMenuShow
                   )
    QObject.connect(self.statusView,
                    PYSIGNAL("comboChanged(QListViewItem*,int)"),
                    self.getComboValue
                   )
                                      
  def getComboValue(self, item, col):
    if col == 2 and item.text(col) == 'Test1':
      item.setText(3, 'Test3')
    elif col == 2 and item.text(col) == 'Test2':
      item.setText(3, 'Test4')
    
  def contextMenuShow(self, item, point, column):
    self.selectedItem = item
    self.contextMenu.exec_loop(point)

  def copyToClipboard(self):
    self.clip.setText(self.statusView.selectedItem().text(1))


class rcStatusMonitor(object):
  def __init__(self, interval, gui=None):
    self.__interval = interval
    self.__gui = gui
    self.clear()
    self.__timer = QTimer(gui)
    QObject.connect(self.__timer, SIGNAL("timeout()"), self.updateWatchItems)

  def startWatch(self):
    self.__timer.start(self.__interval)

  def stopWatch(self):
    self.__timer.stop()
    
  def clear(self):
    self.__gui.statusView.clear()
    self.__watchList = {}
    self.__labelSeq = []
    self.__lastItem = None
    
  def addWatchItem(self, label, accessor, key=None, alarmExpr=None):
    if not self.__watchList.has_key(label):
      self.__watchList[label] = WatchItem(accessor, key, label, alarmExpr)
      self.__labelSeq.append(label)
    else:
      self.__watchList[label].setAccessor(accessor, key)

  def updateWatchItems(self):
    for label in self.__labelSeq:
      wi = self.__watchList[label]
      if self.__gui is None:
        print label, wi.evaluate()
      else:        
        if not wi.isInView():    
          if self.__lastItem is None:
            si = StatusItem(self.__gui.statusView, label)
          else:
            si = StatusItem(self.__gui.statusView, self.__lastItem, label)
          si.setAlarmExpr(wi.getAlarmExpr())
          self.__lastItem = si
          wi.setStatusItem(si)
          wi.setInView()
        else:
          si = wi.getStatusItem()
        si.setText(1, str(wi.evaluate()))
        si.setCombo(2, ['Test1', 'Test2'])
        si.setCombo(3, ['Test3', 'Test4'])



if __name__ == '__main__':

  from random import random
  import sys

  class Dummy(object):
    def __init__(self):
      pass

    def getValue(self):
      return random()

  app = QApplication(sys.argv)

  gui = rcStatusPanel()

  statMon = rcStatusMonitor(1000, gui)
  dummy = Dummy()
  statMon.clear()
  for i in range(10):
    statMon.addWatchItem('TestValue %02d ' % i, dummy.getValue)
  statMon.updateWatchItems()
  statMon.clear()
  for i in range(10):
    statMon.addWatchItem('TestValue %02d ' % i, dummy.getValue)
  statMon.updateWatchItems()
  #statMon.startWatch()
  app.setMainWidget(gui)
  gui.resize(QSize(500,271).expandedTo(gui.minimumSizeHint()))
  gui.show()
  app.exec_loop()


