#!/usr/bin/env python
#############################################################################
##
## This file is part of Taurus, a Tango User Interface Library
##
## http://www.tango-controls.org/static/taurus/latest/doc/html/index.html
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
## Taurus is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Taurus is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with Taurus. If not, see <http://www.gnu.org/licenses/>.
##
#############################################################################
import sys,re,traceback
import fandango,fandango.qt,taurus
from PyQt4 import Qt,Qwt5
from taurus.qt.qtgui.taurusgui.utils import PanelDescription
from taurus.core.taurusvalidator import DeviceNameValidator, AttributeNameValidator
from taurus.qt.qtcore.mimetypes import TAURUS_ATTR_MIME_TYPE, \
TAURUS_DEV_MIME_TYPE, TAURUS_MODEL_MIME_TYPE, TAURUS_MODEL_LIST_MIME_TYPE
from taurus import Manager
from taurus.qt import Qt
from fandango import partial,FakeLogger as FL
from taurus.qt.qtgui.graphic import TaurusJDrawSynopticsView,TaurusGraphicsScene,TaurusJDrawGraphicsFactory,TYPE_TO_GRAPHICS
from taurus.qt.qtgui.graphic.taurusgraphic import TaurusGroupItem
import traceback
[docs]def catched(f):
def new_f(*args,**kwargs):
try:
return f(*args,**kwargs)
except:
traceback.print_exc()
return new_f
[docs]class VaccaSynoptic(TaurusJDrawSynopticsView):
"""
Overrides TaurusJDrawSynopticsView methods to highlight multiple items in multiple colors.
It allows to setup a delayed setModel call to reduce "hungs" on heavy synoptics.
But!: This delayed call may cause problems in some versions of taurusGUI. (due to scene not-set yet)
"""
JDRAW_FILE = None
JDRAW_HOOK = None
JDRAW_TREE = None
def __init__(self, parent = None, delay=0, designMode = False, updateMode=None, alias = None, resizable = True, panelClass = None):
self.delay = delay
TaurusJDrawSynopticsView.__init__(self, parent,designMode,updateMode,alias,resizable,panelClass)
self.setModelInConfig(False)
[docs] def setModel(self, model):
if self.delay:
self.warning('VaccaSynoptic.setModel() delayed %d ms'%self.delay)
self._setter = Qt.QTimer.singleShot(
self.delay,
partial(self._setJDrawModel, model))
self.JDRAW_FILE = model
else:
TaurusJDrawSynopticsView.setModel(self, model)
def _setJDrawModel(self, model):
print '>'*80
print 'setJDrawModel(%s)' % model
TaurusJDrawSynopticsView.setModel(self, model)
[docs] def getModelMimeData(self):
""" Used for drag events """
model,mimeData = '',None
try:
#model = getattr(self.scene().itemAt(*self.mousePos),'_name','')
selected = self.scene()._selectedItems
if not selected:
self.debug('jdrawView.getModelMimeData(%s): nothing to drag'%model)
return
model = getattr(([s for s in selected if getattr(s,'_name','')] or [selected])[0],'_name','')
self.debug('getModelMimeData(%s)'%model)
mimeData = Qt.QMimeData()
if model:
# self.debug('getMimeData(): DeviceModel at %s: %s',self.mousePos,model)
mimeData.setText(model)
mimeData.setData(TAURUS_MODEL_MIME_TYPE, model)
if DeviceNameValidator().getParams(model):
self.debug('getMimeData(): DeviceModel at %s: %s',self.mousePos,model)
mimeData.setData(TAURUS_DEV_MIME_TYPE,model)
elif AttributeNameValidator().getParams(model):
self.debug('getMimeData(): AttributeModel at %s: %s',self.mousePos,model)
mimeData.setData(TAURUS_ATTR_MIME_TYPE,model)
mimeData.setData(TAURUS_DEV_MIME_TYPE,model.rsplit('/',1)[0])
else:
self.debug('getMimeData(): UnknownModel at %s: %s',self.mousePos,model)
except:
self.debug('jdrawView.getModelMimeData(%s): unable to get MimeData'%model)
self.debug(traceback.format_exc())
return mimeData
@staticmethod
[docs] def getPanelDescription(NAME='Synoptic',JDRAW_FILE='',JDRAW_HOOK=None,JDRAW_TREE=[]):
"""
:param NAME: Name for the Panel
:param JDRAW_FILE: synoptic configuration file
:param JDRAW_HOOK: ''
:param JDRAW_TREE: None
:return: PanelDescription Object
"""
# CONNECTING FAKELOGGER FOR DEBUGGING
try:
import vacca
sdm = vacca.utils.get_shared_data_manager()
if sdm:
v = sdm._SharedDataManager__models.get('SelectedInstrument')
# An inline instantiated FakeLogger will print selected instrument on each click
sdm.connectReader('SelectedInstrument',
FL('SDM.SelectedInstrument [%s,%s]' %
(v.readerCount(),v.writerCount()), True).info,
readOnConnect=False)
except:
print '#'*80
print('Shared Data Manager is not available! (no TaurusGUI instance?)')
traceback.print_exc()
sdm = None
print '#'*80
print '>'*20+'Loading Synoptic panel new ... %s, %s, %s'%\
(JDRAW_FILE,JDRAW_HOOK,JDRAW_TREE)
class_name,rsignal,wsignal = '',{},{}
if not JDRAW_FILE.endswith('.svg'): #Assuming a JDraw file
print '>'*20+'Creating JDW VaccaSynoptic'
if JDRAW_HOOK is not None:
print 'Enabling JDRAW_HOOK = %s'%JDRAW_HOOK
from fandango.qt import QSignalHook
in_hook = QSignalHook(JDRAW_HOOK)
out_hook = QSignalHook(JDRAW_HOOK)
#Synoptic will write this signal
wsignal = {'JDrawOut': 'graphicItemSelected(QString)', }
if sdm:
sdm.connectReader('JDrawOut', FL('SDM.JDrawOut',
True).info,
readOnConnect=False)
sdm.connectReader('JDrawOut', out_hook.setModel,
readOnConnect=False)
sdm.connectWriter('SelectedInstrument', out_hook,
'modelChanged')
v = sdm._SharedDataManager__models.get('JDrawOut')
sdm.connectReader('JDrawOut', FL('SDM.JDrawOut DONE [%s,'
'%s]'%(v.readerCount(),
v.writerCount()),
True).info,
readOnConnect=False)
sdm.connectWriter('JDrawIn', in_hook,'modelChanged')
sdm.connectReader('JDrawIn', FL('SDM.JDrawIn', True).info,
readOnConnect=False)
rsignal = {'JDrawIn': 'selectGraphicItem', }
sdm.connectReader('SelectedInstrument', in_hook.setModel,
readOnConnect=False)
v = sdm._SharedDataManager__models.get('JDrawIn')
sdm.connectReader('JDrawIn', FL('SDM.JDrawIn DONE [%s,'
'%s]' % (v.readerCount(),
v.writerCount()),
True).info,
readOnConnect=False)
else:
rsignal = {'SelectedInstrument': 'selectGraphicItem'}
rsignal['HighlightInstruments'] = 'setHighlightedItems'
wsignal = {'SelectedInstrument': 'graphicItemSelected(QString)'}
if JDRAW_TREE:
wsignal['LoadItems'] = 'modelsChanged'
class_name='vacca.synoptics.VaccaSynoptic'
elif JDRAW_FILE.endswith('.svg'):
try:
from svgsynoptic import SynopticWidget, Registry
rsignal = {'SelectedInstrument': 'select_devices'}
#@TODO: rsignal['HighlightInstruments'] = 'setHighlightedItems'
wsignal = {'SelectedInstrument': 'graphicItemSelected(QString)'}
class_name='synoptic.SynopticWidget'
except:
print('Horreur!: svgsynoptic MODULE NOT AVAILABLE!')
return None
print 'Out of VaccaSynoptic.getPanelDescription(%s,%s)'%(class_name,JDRAW_FILE)
return PanelDescription(NAME,
#classname = 'vacca.VacuumSynoptic',
classname=class_name,
model=JDRAW_FILE, #Model loading is delayed by
# VacuumSynoptic method
sharedDataRead=rsignal,
sharedDataWrite=wsignal,
)
###########################################################################
# OVERLOADED METHODS FOR CUSTOM HIGHLITHING
# It should be included in Taurus as patches
[docs] def setHighlightedItems(self, models = [], color=Qt.Qt.red):
"""
:param models: List of model to be Highlighted
:param color: Color, by default Qt.Qt.red
"""
if fandango.isSequence(models):
models = '(%s)'%')|('.join(models)
self.info('setHighLightedItems(%s)' % models)
try:
default = self.scene().selectionColor()
self.scene().setSelectionColor(color)
self.scene().selectGraphicItem(models)
self.scene().setSelectionColor(default)
except:
err = traceback.format_exc()
self.error(err)
finally:
self.scene().setSelectionColor(default)
[docs] def getGraphicsFactory(self,delayed=False):
return VaccaSynopticGraphicsFactory(self,alias=(self.alias or None),delayed=delayed)
@staticmethod
[docs] def getDefaultIcon():
"""
:return: The Default Icon Path.
"""
path = 'image/icons/Synoptic.png'
return path
#A decorator for QGraphics Objects
[docs]def GetClassWithExtensions(klass):
class ClassWithExtensions(klass):
def setName(self,name):
name = str(name or self.__class__.__name__)
self._name = name#srubio@cells.es: modified to store ._name since initialization (even if a model is not set)
def getExtensions(self):
"""
Any in
ExtensionsList,noPrompt,standAlone,noTooltip,noSelect,ignoreRepaint,shellCommand,className,classParams
"""
self._extensions = getattr(self,'_extensions',{})
if 'ExtensionsList' in self._extensions:
self._extensions.update((k.strip(),True) for k in self._extensions['ExtensionsList'].split(','))
self._extensions.pop('ExtensionsList')
for k in ('noPrompt','standAlone','noTooltip','ignoreRepaint','noSelect'):
if self._extensions.get(k,None)=='': self._extensions[k] = True
self.noPrompt = self._extensions.get('noPrompt',False)
self.standAlone = self._extensions.get('standAlone',False)
self.noTooltip = self._extensions.get('noTooltip',False)
self.ignoreRepaint = self._extensions.get('ignoreRepaint', getattr(self,'ignoreRepaint',False))
self.setName(self._extensions.get('name',self._name))
tooltip = '' if (self.noTooltip or self._name==self.__class__.__name__ or self._name is None) else str(self._name)
#self.debug('setting %s.tooltip = %s'%(self._name,tooltip))
self.setToolTip(tooltip)
#self.debug('%s.getExtensions(): %s'%(self._name,self._extensions))
return self._extensions
return ClassWithExtensions
[docs]class VaccaSynopticGraphicsFactory(TaurusJDrawGraphicsFactory):
[docs] def getSceneObj(self,items):
scene = VaccaGraphicsScene(self.myparent)
for item in items:
try:
if isinstance(item, Qt.QWidget):
scene.addWidget(item)
elif isinstance(item, Qt.QGraphicsItem):
scene.addItem(item)
except:
self.warning("Unable to add item %s to scene" % str(item))
self.debug("Details:", exc_info=1)
return scene
[docs] def getGraphicsItem(self,type_,params):
name = params.get(self.getNameParam())
#applying alias
for k,v in getattr(self,'alias',{}).items():
if k in name:
name = str(name).replace(k,v)
params[self.getNameParam()] = name
cls = None
if '/' in name:
#replacing Taco identifiers in %s'%name
if name.lower().startswith('tango:') and (name.count('/')==2 or not 'tango:/' in name.lower()):
nname = name.split(':',1)[-1]
params[self.getNameParam()] = name = nname
if name.lower().endswith('/state'): name = name.rsplit('/',1)[0]
cls = Manager().findObjectClass(name)
else:
if name: self.debug('%s does not match a tango name'%name)
klass = self.getGraphicsClassItem(cls, type_)
self.debug(str((cls,type_,klass,klass.__name__)))
if not hasattr(klass,'getExtensions'):
klass = GetClassWithExtensions(klass)
item = klass()
## It's here were Attributes are subscribed
self.set_common_params(item,params)
if hasattr(item,'getExtensions'):
item.getExtensions() #<= must be called here to take extensions from params
return item
[docs]class VaccaGraphicsScene(TaurusGraphicsScene):
[docs] def setSelectionColor(self,color):
self._selectioncolor = color
[docs] def selectionColor(self):
try:
assert self._selectioncolor
except:
self._selectioncolor = Qt.Qt.blue
return self._selectioncolor
def _displaySelectionAsOutline(self, items):
def _outline(shapes):
""""Compute the boolean union from a list of QGraphicsItem. """
shape = None
# TODO we can use a stack instead of recursivity
for s in shapes:
# TODO we should skip text and things like that
if isinstance(s, TaurusGroupItem):
s = _outline(s.childItems())
if s == None:
continue
s = s.shape()
if shape != None:
shape = shape.united(s)
else:
shape = s
if shape == None:
return None
return Qt.QGraphicsPathItem(shape)
# TODO we can cache the outline instead of computing it again and again
selectionShape = _outline(items)
if selectionShape:
# copy-paste from getSelectionMark
color = Qt.QColor(self.selectionColor())
color.setAlphaF(.10)
pen = Qt.QPen(Qt.Qt.SolidLine)
pen.setWidth(4)
pen.setColor(Qt.QColor(self.selectionColor()))
selectionShape.setBrush(color)
selectionShape.setPen(pen)
for item in items:
if item not in self._selectedItems: self._selectedItems.append(item)
# TODO i dont think this function work... or i dont know how...
#self.setSelectionMark(picture=selectionShape)
# ... Then do it it with hands...
# copy-paste from drawSelectionMark
self._selection.append(selectionShape)
# It's better to add it hidden to avoid resizings
selectionShape.hide()
self.addItem(selectionShape)
# Put on Top
selectionShape.setZValue(9999)
selectionShape.show()
self.updateSceneViews()
return True
return False
[docs] def getSelectionMark(self,picture=None,w=10,h=10):
if picture is None:
if self.SelectionMark:
SelectionMark = self.SelectionMark()
else:
SelectionMark = Qt.QGraphicsEllipseItem()
color = Qt.QColor(self.selectionColor())
color.setAlphaF(.10)
SelectionMark.setBrush(color)
pen = Qt.QPen(Qt.Qt.CustomDashLine)
pen.setWidth(4)
pen.setColor(Qt.QColor(self.selectionColor()))
SelectionMark.setPen(pen)
SelectionMark.hide() #It's better to add it hidden to avoid resizings
else:
try:
if isinstance(picture,Qt.QGraphicsItem):
SelectionMark = picture
SelectionMark.setRect(0,0,w,h)
SelectionMark.hide()
elif operator.isCallable(picture):
SelectionMark = picture()
else:
if isinstance(picture,Qt.QPixmap):
pixmap = picture
elif isinstance(picture,basestring) or isinstance(picture,Qt.QString):
picture = str(picture)
pixmap = Qt.QPixmap(os.path.realpath(picture))
SelectionMark = Qt.QGraphicsPixmapItem()
SelectionMark.setPixmap(pixmap.scaled(w,h))
SelectionMark.hide()
except:
self.debug('In setSelectionMark(%s): %s'%(picture,traceback.format_exc()))
picture = None
return SelectionMark
##################################################################
[docs] def getItemByPosition(self,x,y):
""" This method will try first with named objects; if failed then with itemAt """
pos = Qt.QPointF(x,y)
itemsAtPos = []
for z,o in sorted((i.zValue(),i) for v in self._itemnames.values() for i in v if i.contains(pos) or i.isUnderMouse()):
if not hasattr(o,'getExtensions'):
self.warning('getItemByPosition(%d,%d): adding Qt primitive %s'%(x,y,o))
itemsAtPos.append(o)
elif not o.getExtensions().get('noSelect'):
self.warning('getItemByPosition(%d,%d): adding GraphicsItem %s'%(x,y,o))
itemsAtPos.append(o)
else: self.warning('getItemByPosition(%d,%d): object ignored, %s'%(x,y,o))
if itemsAtPos:
obj = itemsAtPos[-1]
return self.getTaurusParentItem(obj) or obj
else:
#return self.itemAt(x,y)
self.debug('getItemByPosition(%d,%d): no items found!'%(x,y))
return None
#def getItemClicked(self,mouseEvent):
#pos = mouseEvent.scenePos()
#x,y = pos.x(),pos.y()
#self.emit(Qt.SIGNAL("graphicSceneClicked(QPoint)"),Qt.QPoint(x,y))
#obj = self.getItemByPosition(x,y)
##self.debug('mouse clicked on %s(%s) at (%s,%s)'%(type(obj).__name__,getattr(obj,'_name',''),x,y))
#return obj
#def mousePressEvent(self,mouseEvent):
##self.debug('In TaurusGraphicsScene.mousePressEvent(%s,%s))'%(str(type(mouseEvent)),str(mouseEvent.button())))
#try:
#obj = self.getItemClicked(mouseEvent)
#obj_name = getattr(obj,'_name', '')
#if not obj_name and isinstance(obj,QGraphicsTextBoxing): obj_name = obj.toPlainText()
#if (mouseEvent.button() == Qt.Qt.LeftButton):
### A null obj_name should deselect all, we don't send obj because we want all similar to be matched
#if self.selectGraphicItem(obj_name):
#self.debug(' => graphicItemSelected(QString)(%s)'%obj_name)
#self.emit(Qt.SIGNAL("graphicItemSelected(QString)"),obj_name)
#else:
## It should send None but the signature do not allow it
#self.emit(Qt.SIGNAL("graphicItemSelected(QString)"), "")
#def addMenuAction(menu,k,action,last_was_separator=False):
#try:
#if k:
#configDialogAction = menu.addAction(k)
#if action:
#self.connect(configDialogAction, Qt.SIGNAL("triggered()"), lambda dev=obj_name,act=action: act(dev))
#else: configDialogAction.setEnabled(False)
#last_was_separator = False
#elif not last_was_separator:
#menu.addSeparator()
#last_was_separator = True
#except Exception,e:
#self.warning('Unable to add Menu Action: %s:%s'%(k,e))
#return last_was_separator
#if (mouseEvent.button() == Qt.Qt.RightButton):
#''' This function is called when right clicking on TaurusDevTree area. A pop up menu will be shown with the available options. '''
#self.debug('RightButton Mouse Event on %s'%(obj_name))
#if isinstance(obj,TaurusGraphicsItem) and (obj_name or obj.contextMenu() or obj.getExtensions()):
#menu = Qt.QMenu(None)#self.parent)
#last_was_separator = False
#extensions = obj.getExtensions()
#if obj_name and (not extensions or not extensions.get('className')):
##menu.addAction(obj_name)
#addMenuAction(menu,'Show %s panel'%obj_name,lambda x=obj_name: self.showNewPanel(x))
#if obj.contextMenu():
#if obj_name:
#menu.addSeparator()
#last_was_separator = True
#for t in obj.contextMenu(): #It must be a list of tuples (ActionName,ActionMethod)
#last_was_separator = addMenuAction(menu,t[0],t[1],last_was_separator)
#if extensions:
#if not menu.isEmpty(): menu.addSeparator()
#className = extensions.get('className')
#if className and className!='noPanel':
#self.debug('launching className extension object')
#addMenuAction(menu,'Show %s'%className,lambda d,x=obj: self.showNewPanel(x))
#if extensions.get('shellCommand'):
#addMenuAction(menu,'Execute',lambda d,x=obj: self.getShellCommand(x))
#if not menu.isEmpty():
#menu.exec_(Qt.QPoint(mouseEvent.screenPos().x(),mouseEvent.screenPos().y()))
#del menu
#except Exception:
#self.warning( traceback.format_exc())
###############################################################################
import doc
__doc__ = doc.get_autodoc(__name__,vars())
[docs]def test(model,filters='',debug=False):
if debug:
taurus.setLogLevel(taurus.core.util.Logger.Debug)
form.setWindowTitle(model)
print 'loading synoptic: %s'%model
form = VaccaSynoptic(delay=1000,designMode=False)
#form = taurus.qt.qtgui.graphic.TaurusJDrawSynopticsView(designMode=False)
#designMode=False,updateMode=VaccaSynoptic.NoViewportUpdate)
form.show()
form.setModel(model)
form.setWindowTitle(model)
print 'showing ...'
return form
if __name__ == '__main__':
#!/usr/bin/python
assert len(sys.argv)>1, '\n\nUsage:\n\t> python synoptic [jdw file]'
app = Qt.QApplication([]) #sys.argv)
model = sys.argv[1]
filters = fandango.first(sys.argv[2:],'')
form = test(model,filters)
sys.exit(app.exec_())