#!/usr/bin/env python
#############################################################################
##
# This file is part of Taurus
##
# http://taurus-scada.org
##
# 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/>.
##
#############################################################################
"""
taurusgraphic.py:
"""
from __future__ import print_function
# TODO: Tango-centric
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import range
from builtins import object
import re
import os
import subprocess
import traceback
import collections
from future.utils import string_types
from queue import Queue
from taurus import Manager
from taurus.core import AttrQuality, DataType
from taurus.core.util.containers import CaselessDefaultDict
from taurus.core.util.log import Logger, deprecation_decorator
from taurus.core.taurusdevice import TaurusDevice
from taurus.core.taurusattribute import TaurusAttribute
from taurus.core.util.enumeration import Enumeration
from taurus.external.qt import Qt, compat
from taurus.qt.qtgui.base import TaurusBaseComponent
from taurus.qt.qtgui.util import (QT_ATTRIBUTE_QUALITY_PALETTE, QT_DEVICE_STATE_PALETTE,
ExternalAppAction, TaurusWidgetFactory)
__all__ = ['SynopticSelectionStyle',
'parseTangoUri',
'QEmitter', # TODO: QEmitter should probably be removed (kept priv)
'TaurusGraphicsUpdateThread',
'TaurusGraphicsScene',
'QGraphicsTextBoxing',
'QSpline',
'TaurusGraphicsItem',
'TaurusGraphicsAttributeItem',
'TaurusGraphicsStateItem',
'TaurusEllipseStateItem',
'TaurusRectStateItem',
'TaurusSplineStateItem',
'TaurusRoundRectItem',
'TaurusRoundRectStateItem',
'TaurusGroupItem',
'TaurusGroupStateItem',
'TaurusPolygonStateItem',
'TaurusLineStateItem',
'TaurusTextStateItem',
'TaurusTextAttributeItem',
'TYPE_TO_GRAPHICS',
'TaurusBaseGraphicsFactory',
]
__docformat__ = 'restructuredtext'
SynopticSelectionStyle = Enumeration("SynopticSelectionStyle", [
# A blue ellipse is displayed around the selected objects
"ELLIPSE",
# The own outline of selected object is displayed in blue and bolder
"OUTLINE",
])
[docs]def parseTangoUri(name):
# TODO: Tango-centric
from taurus.core import tango
from taurus.core.tango.tangovalidator import (TangoDeviceNameValidator,
TangoAttributeNameValidator)
validator = {tango.TangoDevice: TangoDeviceNameValidator,
tango.TangoAttribute: TangoAttributeNameValidator}
try:
val = validator[tango.TangoFactory().findObjectClass(name)]()
params = val.getUriGroups(name)
return (params if '_devslashname' in params else None)
except:
return None
[docs]class QEmitter(Qt.QObject):
updateView = Qt.pyqtSignal(compat.PY_OBJECT)
[docs]class TaurusGraphicsUpdateThread(Qt.QThread):
def __init__(self, parent=None, period=3):
"""Parent most not be None and must be a TaurusGraphicsScene!"""
if not isinstance(parent, TaurusGraphicsScene):
raise RuntimeError("Illegal parent for TaurusGraphicsUpdateThread")
Qt.QThread.__init__(self, parent)
self.period = period
self.log = Logger('TaurusGraphicsUpdateThread')
def _updateView(self, v):
# The first one is the prefered one because it improves performance
# since updates don't come very often in comparison to with the refresh
# rate of the monitor (~70Hz)
if v.viewportUpdateMode() == Qt.QGraphicsView.NoViewportUpdate:
# We call the update to the viewport instead of the view
# itself because apparently there is a bug in QT 4.3 that
# prevents a proper update when the view is inside a QTab
v.viewport().update()
else:
# @todo This is probably a bug (item_rects is not defined). But it is defined in .run(), see "todo" below...
v.updateScene(item_rects)
# v.invalidateScene(item.boundingRect())
return
[docs] def run(self):
self.log.debug("run... - TaurusGraphicsUpdateThread")
emitter = QEmitter()
emitter.moveToThread(Qt.QApplication.instance().thread())
emitter.setParent(Qt.QApplication.instance())
emitter.updateView.connect(self._updateView)
p = self.parent()
while True:
item = p.getQueue().get(True)
if type(item) in (str,):
if item == "exit":
break
else:
continue
if not isinstance(item, collections.Sequence):
item = (item,)
# @todo: Unless the call to boundingRect() has a side effect, this line is useless.. probably related to todo in _updateView()
item_rects = [i.boundingRect() for i in item]
for v in p.views():
# p.debug("emit('updateView')")
# emitter.updateView.emit(v)
emitter.updateView.emit(v)
# This sleep is needed to reduce CPU usage of the application!
self.sleep(self.period)
# End of while
# End of Thread
[docs]class TaurusGraphicsScene(Qt.QGraphicsScene):
'''
This class encapsulates TaurusJDrawSynopticsView and TaurusGraphicsScene signals/slots
External events::
Slot selectGraphicItem(const QString &) displays a selection
mark around the TaurusGraphicsItem that matches the argument passed.
Mouse Left-button events::
Signal graphicItemSelected(QString) is triggered, passing the
selected TaurusGraphicsItem.name() as argument.
Mouse Right-button events::
TaurusGraphicsItem.setContextMenu([(ActionName,ActionMethod(device_name))]
allows to configure custom context menus for graphic items using a list
of tuples. Empty tuples will insert separators in the menu.
'''
ANY_ATTRIBUTE_SELECTS_DEVICE = True
TRACE_ALL = False
refreshTree2 = Qt.pyqtSignal()
graphicItemSelected = Qt.pyqtSignal('QString')
graphicSceneClicked = Qt.pyqtSignal('QPoint')
def __init__(self, parent=None, strt=True):
name = self.__class__.__name__
# self.call__init__(Logger, name, parent) #Inheriting from Logger
# caused exceptions in CONNECT
Qt.QGraphicsScene.__init__(self, parent)
self.updateQueue = None
self.updateThread = None
self._itemnames = CaselessDefaultDict(lambda k: set())
self._selection = []
self._selectedItems = []
self._selectionStyle = SynopticSelectionStyle.OUTLINE
self.threads = []
self.pids = []
self.panels = []
self.panel_launcher = None
try:
self.logger = Logger(name)
# self.logger.setLogLevel(self.logger.Info)
if not self.TRACE_ALL:
self.debug = lambda l: self.logger.debug(l)
self.info = lambda l: self.logger.info(l)
self.warning = lambda l: self.logger.warning(l)
else:
self.debug = self.info = self.warning = self.error = lambda l: self.logger.warning(
l)
except:
print('Unable to initialize TaurusGraphicsSceneLogger: %s' % traceback.format_exc())
try:
if parent and parent.panelClass() is not None:
defaultClass = parent.panelClass()
if defaultClass and isinstance(defaultClass, string_types):
self.panel_launcher = self.getClass(defaultClass)
if self.panel_launcher is None:
self.panel_launcher = ExternalAppAction(
defaultClass.split())
else:
self.panel_launcher = defaultClass
else:
from taurus.qt.qtgui.graphic.jdraw import TaurusJDrawSynopticsView
self.panel_launcher = TaurusJDrawSynopticsView.defaultPanelClass()
except:
self.warning(traceback.format_exc())
self.panel_launcher = None
self.setSelectionMark()
if strt:
self.start()
def __del__(self):
self.closeAllPanels()
Qt.QGraphicsScene.__del__(self)
[docs] def showNewPanel(self, args=None, standAlone=False):
try:
if isinstance(args, TaurusGraphicsItem):
objName = args._name
clName = args.getExtensions().get('className') or self.panel_launcher
# classParams extension overrides Model; if there's no
# extension then object name is used
clParam = args.getExtensions().get('classParams') or objName
standAlone = args.standAlone
else:
clName, clParam, objName = self.panel_launcher, args, args
if not clName or clName == 'noPanel':
return
self.debug('TaurusGraphicsScene.showNewPanel(%s,%s,%s)' %
(clName, clParam, objName))
if isinstance(clName, ExternalAppAction):
clName.actionTriggered(clParam if isinstance(
clParam, (list, tuple)) else [clParam])
else:
if isinstance(clName, string_types):
klass = self.getClass(clName)
if klass is None:
self.warning("%s Class not found!" % clName)
klass = self.getClass("TaurusDevicePanel")
else:
klass, clName = clName, getattr(
clName, '__name__', str(clName))
widget = klass() # self.parent())
# if isinstance(widget,taurus.qt.qtgui.panel.TaurusDevicePanel):
# widget.setSpectraAtkMode(True) #Method renamed or deprecated
try:
widget.setClasses(clParam)
except:
pass
try:
widget.setModel(clParam)
except:
pass
try:
widget.setTable(clParam)
except:
pass
# if isinstance(widget,Qt.QWidget):
# if not standAlone:
#obj = newDialog(self.parent())
# else:
#obj = newDialog()
# obj.initComponents(widget,objName,clName)
# obj.setModal(False)
# obj.setVisible(True)
widget.setWindowTitle('%s - %s' % (clName, objName))
self.panels.append(widget)
widget.show() # exec_()
return widget
except:
self.warning(traceback.format_exc())
[docs] def closeAllPanels(self):
""" This method replaces killProcess, using taurus.qt.qtgui.util.ExternalAppAction instead! """
try:
self.debug('In closeAllPanels(%s,%s)' %
(self.panel_launcher, self.panels))
if isinstance(self.panel_launcher, ExternalAppAction):
self.panel_launcher.kill()
for p in self.panels:
try:
if hasattr(p, 'setModel'):
p.setModel(None)
p.close()
except:
pass
while self.panels:
self.panels.pop(0)
except:
self.warning(traceback.format_exc())
[docs] def addItem(self, item):
# self.debug('addItem(%s)'%item)
def expand(i):
name = str(getattr(i, '_name', '')).lower()
if name:
self._itemnames[name].add(i)
#self.debug('addItem(%s): %s'%(name,i))
if isinstance(i, Qt.QGraphicsItemGroup):
for j in i.childItems():
expand(j)
expand(item)
Qt.QGraphicsScene.addItem(self, item)
[docs] def getItemByName(self, item_name, strict=None):
"""
Returns a list with all items matching a given name.
:param strict: (bool or None) controls whether full_name (strict=True) or only device name (False) must match
:return: (list) items
"""
# TODO: Tango-centric
from taurus.core.tango.tangovalidator import (
TangoDeviceNameValidator, TangoAttributeNameValidator)
strict = (
not self.ANY_ATTRIBUTE_SELECTS_DEVICE) if strict is None else strict
alnum = '(?:[a-zA-Z0-9-_\*]|(?:\.\*))(?:[a-zA-Z0-9-_\*]|(?:\.\*))*'
target = str(item_name).strip().split()[0].lower().replace(
'/state', '') # If it has spaces only the first word is used
# Device names should match also its attributes or only state?
if not strict and TangoAttributeNameValidator().getUriGroups(target):
target = target.rsplit('/', 1)[0]
if TangoDeviceNameValidator().getUriGroups(target):
if strict:
target += '(/state)?'
else:
target += '(/' + alnum + ')?'
if not target.endswith('$'):
target += '$'
result = []
for k in list(self._itemnames.keys()):
if re.match(target.lower(), k.lower()):
#self.debug('getItemByName(%s): _itemnames[%s]: %s'%(target,k,self._itemnames[k]))
result.extend(self._itemnames[k])
return result
[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.debug(
'getItemByPosition(%d,%d): adding Qt primitive %s' % (x, y, o))
itemsAtPos.append(o)
elif not o.getExtensions().get('noSelect'):
self.debug(
'getItemByPosition(%d,%d): adding GraphicsItem %s' % (x, y, o))
itemsAtPos.append(o)
else:
self.debug(
'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
[docs] def getItemClicked(self, mouseEvent):
pos = mouseEvent.scenePos()
x, y = pos.x(), pos.y()
self.graphicSceneClicked.emit(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
[docs] 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.graphicItemSelected.emit(obj_name)
else:
# It should send None but the signature do not allow it
self.graphicItemSelected.emit("")
def addMenuAction(menu, k, action, last_was_separator=False):
try:
if k:
configDialogAction = menu.addAction(k)
if action:
configDialogAction.triggered.connect(
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 as 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())
[docs] def mouseDoubleClickEvent(self, event):
try:
obj = self.getItemClicked(event)
obj_name = getattr(obj, '_name', '')
try:
class_name = obj.getExtensions().get('className')
except:
class_name = 'noPanel'
self.debug('Clicked (%s,%s,%s)' % (obj, obj_name, class_name))
if obj_name and class_name != 'noPanel':
self.showNewPanel(obj)
except:
self.warning(traceback.format_exc())
[docs] def setSelectionStyle(self, selectionStyle):
# TODO We should test that selectionStyle is part of
# SynopticSelectionStyle but there is nothing about it
self._selectionStyle = selectionStyle
[docs] def selectGraphicItem(self, item_name):
"""
A blue circle is drawn around the matching item name.
If the item_name is empty, or it is a reserved keyword, or it has the "noSelect" extension, then the blue circle is removed from the synoptic.
"""
#self.debug('In TaurusGraphicsScene.selectGraphicItem(%s))'%item_name)
selected = [str(getattr(item, '_name', item))
for item in self._selectedItems if item]
if selected:
iname = str(getattr(item_name, '_name', item_name))
#self.debug('In TauGraphicsScene.selectGraphicItem(%s): already selected: %s'%(iname,selected))
if not iname.strip():
self.clearSelection()
return False
elif any(iname not in i for i in selected):
self.clearSelection()
else:
self.debug(
'In TauGraphicsScene.selectGraphicItem(%s): already selected!' % item_name)
return True
if any(isinstance(item_name, t) for t in (TaurusGraphicsItem, Qt.QGraphicsItem)):
if not getattr(item_name, '_name', ''):
self.debug(
'In TauGraphicsScene.selectGraphicItem(%s): item name not found.' % item_name)
return False
items = [item_name]
else:
from taurus.qt.qtgui.graphic import jdraw_parser
if not item_name or (str(item_name).startswith('JD') and str(item_name) in jdraw_parser.reserved):
self.debug(
'In TauGraphicsScene.selectGraphicItem(%s): item name not found or name is a reserved keyword.' % item_name)
return False
items = self.getItemByName(item_name) or []
items = [i for i in items if self.getTaurusParentItem(
i) not in (items + self._selectedItems)]
self.debug('In TaurusGraphicsScene.selectGraphicItem(%s)): matched %d items' % (
item_name, len(items)))
if self._selectionStyle == SynopticSelectionStyle.ELLIPSE:
displaySelection = self._displaySelectionAsEllipse
elif self._selectionStyle == SynopticSelectionStyle.OUTLINE:
displaySelection = self._displaySelectionAsOutline
else:
raise Exception("Unexpected selectionStyle '%s'" %
SynopticSelectionStyle.whatis(self._selectionStyle))
return displaySelection(items)
def _displaySelectionAsEllipse(self, items):
retval = False
for item in items:
try:
if ((isinstance(item, TaurusGraphicsItem) and item.getExtensions().get('noSelect'))
or (item in self._selection)
# or (item in tangoGroup)
):
continue
x, y = item.x(), item.y()
rect = item.boundingRect()
srect = self.sceneRect()
# 0 has to be excluded to check grouped element
if not (0 < x <= self.sceneRect().width() and 0 < y <= srect.height()):
rx, ry = rect.topLeft().x(), rect.topLeft().y()
self.debug('\tposition not well mapped (%s,%s), using rect bound (%s,%s) instead' % (
x, y, rx, ry))
x, y = rx, ry # If the object is in the corner it will be also 0
w, h = rect.width(), rect.height()
if x < 0 or y < 0:
self.debug('Cannot draw SelectionMark for %s(%s)(%s,%s) in a negative position (%f,%f)' % (
type(item).__name__, item._name, w, h, x, y))
else:
if type(item) in (TaurusTextAttributeItem, TaurusTextStateItem) and isinstance(self.getSelectionMark(), Qt.QGraphicsPixmapItem):
x, y, w, h = x - 20, y, 20, 20
self.drawSelectionMark(x, y, w, h)
self.debug('> Moved the SelectionMark to item %s(%s)(%s,%s) at %f,%f' % (
type(item).__name__, item._name, w, h, x, y))
if item not in self._selectedItems:
self._selectedItems.append(item)
retval = True
except Exception as e:
self.warning('selectGraphicsItem(%s) failed! %s' %
(getattr(item, '_name', item), str(e)))
self.warning(traceback.format_exc())
# return False
return retval
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 is None:
continue
s = s.shape()
if shape is not None:
shape = shape.united(s)
else:
shape = s
if shape is 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(Qt.Qt.blue)
color.setAlphaF(.10)
pen = Qt.QPen(Qt.Qt.SolidLine)
pen.setWidth(4)
pen.setColor(Qt.QColor(Qt.Qt.blue))
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 clearSelection(self):
#self.debug('In clearSelection([%d])'%len(self._selectedItems))
for i in self._selection:
i.hide()
self.removeItem(i)
self._selection = []
self._selectedItems = []
self.updateSceneViews()
[docs] def setSelectionMark(self, picture=None, w=10, h=10):
"""This method allows to set a callable, graphic item or pixmap as selection mark (by default creates a blue circle).
If picture is a callable, the object returned will be used as selection mark.
If picture is a QGraphicsItem it will be used as selection mark.
If picture is a QPixmap or a path to a pixmap a QGraphicsPixmapItem will be created.
If no picture is provided, a blue ellipse will be drawn around the selected object.
h/w will be used for height/width of the drawn object.
"""
#self.debug('In setSelectionMark(%s,%d,%d)'%(picture,w,h))
if picture is None:
self.SelectionMark = None # Reset of previous icon generators
else:
self.SelectionMark = (lambda p=picture, x=w,
y=h: self.getSelectionMark(p, x, y))
return self.SelectionMark
[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(Qt.Qt.blue)
color.setAlphaF(.10)
SelectionMark.setBrush(color)
pen = Qt.QPen(Qt.Qt.CustomDashLine)
pen.setWidth(4)
pen.setColor(Qt.QColor(Qt.Qt.blue))
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 hasattr(picture, '__call__'):
SelectionMark = picture()
else:
if isinstance(picture, Qt.QPixmap):
pixmap = picture
elif isinstance(picture, string_types):
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 drawSelectionMark(self, x, y, w, h, oversize=1):
"""
If h or w are None the mark is drawn at x,y
If h or w has a value the mark is drawn in the center of the region ((x,y)(x+w,y+h))
"""
#self.debug('%s has parent %s' % (item_name,getattr(item.parentItem(),'_name','ung') if item.parentItem() else 'None'))
#self.debug('drawSelectionMark(): center and width,height are: (%d,%d),(%d,%d)' % (x,y,w,h))
mark = self.getSelectionMark()
self._selection.append(mark)
srect = self.itemsBoundingRect()
MAX_CIRCLE_SIZE = srect.width(), srect.height() # 500,500 #20,20
LIMITS = (0, 0, srect.width(), srect.height())
def bound(coords, bounds=LIMITS):
""" x,y,w,h """
x, y, w, h = coords
if x < bounds[0]:
w, x = w - (bounds[0] - x), bounds[0]
if y < bounds[1]:
h, y = h - (bounds[1] - y), bounds[1]
if x + w > bounds[2]:
w, x = (bounds[2] - x), x
if y + h > bounds[3]:
h, y = (bounds[3] - y), y
return x, y, w, h
if isinstance(mark, Qt.QGraphicsEllipseItem):
if None not in [w, h]:
if w > MAX_CIRCLE_SIZE[0] or h > MAX_CIRCLE_SIZE[1]:
# Applying correction if the file is too big, half max
# circle size around the center
x, y = (x + w / 2.) - .5 * \
MAX_CIRCLE_SIZE[0], (y + h / 2.) - .5 * \
MAX_CIRCLE_SIZE[1],
w, h = [.5 * t for t in MAX_CIRCLE_SIZE]
else:
x, y = x - .5 * w, y - .5 * h
else:
w, h = [.5 * t for t in MAX_CIRCLE_SIZE]
#self.debug('drawSelectionMark(): center and width,height are: (%d,%d),(%d,%d)' % (x,y,w,h))
mark.setRect(*bound((x, y, w * 2, h * 2)))
# mark.setRect(x,y,w*2,h*2)
elif isinstance(mark, Qt.QGraphicsPixmapItem):
rect = mark.boundingRect()
if None not in [w, h]:
x, y = x + .5 * w, y + .5 * h
mark.setOffset(x - .5 * rect.width(), y - .5 * rect.height())
elif isinstance(mark, Qt.QGraphicsItem):
mark.setRect(x, y, w, h)
mark.hide() # It's better to add it hidden to avoid resizings
self.addItem(mark)
mark.setZValue(9999) # Put on Top
mark.show()
self.updateSceneViews()
return
[docs] def getShellCommand(self, obj, wait=False):
shellCom = obj.getExtensions().get('shellCommand').replace(
'$NAME', obj._name).replace('$MODEL', obj._name)
if not wait and not shellCom.endswith('&'):
shellCom += ' &'
if obj.noPrompt:
subprocess.call(shellCom, shell=True)
else:
yes = Qt.QMessageBox.Ok
no = Qt.QMessageBox.Cancel
result = Qt.QMessageBox.question(self.parent(
), "Shell command", "Would you like to call shell command '" + shellCom + "' ?", yes, no)
if result == yes:
subprocess.call(shellCom, shell=True)
return
[docs] def getClass(self, clName):
if not clName or clName == 'noPanel':
return None
elif clName in ('atkpanel.MainPanel', 'atkpanel'):
clName = 'TaurusDevicePanel'
# TODO: allow passing class names including module, e.g.: 'foo.Bar'
if clName in globals():
return globals()[clName]
elif clName in locals():
return locals()[clName]
elif clName in dir(Qt):
return getattr(Qt, clName)
else:
wf = TaurusWidgetFactory()
try:
return wf.getTaurusWidgetClass(clName)
except:
return None
[docs] @staticmethod
def getTaurusParentItem(item, top=True):
""" Searches within a group hierarchy and returns a parent Taurus component or None if no parent TaurusBaseComponent
is found."""
if item is None:
return None
first, p = None, item.parentItem()
while p:
if isinstance(p, TaurusGraphicsItem):
if first is None:
first = p
if not top:
break
elif str(p.getModel()) != str(first.getModel()):
break
else:
first = p
p = p.parentItem()
return first
[docs] def getAllChildren(self, item, klass=None):
""" Returns all children elements, filtering by klass if wanted """
result = []
try:
children = item.childItems()
result.extend(
c for c in children if not klass or isinstance(c, klass))
result.extend(c.childItems()
for c in children if not klass or isinstance(c, klass))
except:
pass
return result
[docs] def start(self):
if self.updateThread:
return
self.updateQueue = Queue()
self.updateThread = TaurusGraphicsUpdateThread(self)
self.updateThread.start() # Qt.QThread.HighPriority)
[docs] def getQueue(self):
return self.updateQueue
[docs] def updateSceneItem(self, item):
self.updateQueue.put(item)
[docs] def updateSceneItems(self, items):
self.updateQueue.put(items)
[docs] def updateScene(self):
self.update()
[docs] def updateSceneViews(self):
for v in self.views():
v.viewport().update()
# v.invalidateScene(self.SelectionCircle.boundingRect())
return
[docs]class QGraphicsTextBoxing(Qt.QGraphicsItemGroup):
"""Display a text inside a virtual box. Support horizontal and vertical alignment"""
_TEXT_RATIO = 0.8
def __init__(self, parent=None, scene=None):
Qt.QGraphicsItemGroup.__init__(self, parent)
if scene is not None:
scene.addItem(self)
self._rect = Qt.QGraphicsRectItem(self)
if scene is not None:
scene.addItem(self._rect)
self._rect.setBrush(Qt.QBrush(Qt.Qt.NoBrush))
self._rect.setPen(Qt.QPen(Qt.Qt.NoPen))
self._text = Qt.QGraphicsTextItem(self)
if scene is not None:
scene.addItem(self._text)
self._text.setTransform(
Qt.QTransform.fromScale(self._TEXT_RATIO, self._TEXT_RATIO),
True)
self._validBackground = None
# using that like the previous code create a worst result
self.__layoutValide = True
self._alignment = Qt.Qt.AlignCenter | Qt.Qt.AlignVCenter
[docs] def setRect(self, x, y, width, height):
self._rect.setRect(x, y, width, height)
self._invalidateLayout()
[docs] def setPlainText(self, text):
self._text.setPlainText(text)
self._invalidateLayout()
[docs] def setValidBackground(self, color):
self._validBackground = color
[docs] def toPlainText(self):
return self._text.toPlainText()
[docs] def brush(self):
return self._rect.brush()
[docs] def setBrush(self, brush):
self._rect.setBrush(brush)
[docs] def pen(self):
return self._rect.pen()
[docs] def setPen(self, pen):
self._rect.setPen(pen)
[docs] def setDefaultTextColor(self, color):
self._text.setDefaultTextColor(color)
[docs] def setHtml(self, html):
self._text.setHtml(html)
self._invalidateLayout()
[docs] def setFont(self, font):
self._text.setFont(font)
self._invalidateLayout()
[docs] def setAlignment(self, alignment):
self._alignment = alignment
self._invalidateLayout()
def _invalidateLayout(self):
"""Invalidate the current location of the text"""
if not self.__layoutValide:
return
self.__layoutValide = False
self.update()
def _validateLayout(self):
"""Compute the text location"""
if self.__layoutValide:
return
rect = self._rect.rect()
width, height = rect.width(), rect.height()
textRect = self._text.boundingRect()
# horizontal layout
x = rect.x()
alignment = int(self._alignment)
if (alignment & int(Qt.Qt.AlignLeft)) != 0:
x += 0
elif (alignment & int(Qt.Qt.AlignHCenter)) != 0:
x += width * 0.5 - textRect.width() * 0.5 * self._TEXT_RATIO
elif (alignment & int(Qt.Qt.AlignRight)) != 0:
x += width - textRect.width() * self._TEXT_RATIO
# vertical layout
y = rect.y()
if (alignment & int(Qt.Qt.AlignTop)) != 0:
y += 0
elif (alignment & int(Qt.Qt.AlignVCenter)) != 0:
y += height * 0.5 - textRect.height() * 0.5 * self._TEXT_RATIO
elif (alignment & int(Qt.Qt.AlignBottom)) != 0:
y += height - textRect.height() * self._TEXT_RATIO
self._text.setPos(x, y)
self.__layoutValide = True
[docs] def paint(self, painter, option, widget):
self._validateLayout()
Qt.QGraphicsItemGroup.paint(self, painter, option, widget)
[docs]class QSpline(Qt.QGraphicsPathItem):
def __init__(self, parent=None, closed=False, control_points=None):
super(QSpline, self).__init__(parent)
self.__closed = closed
if control_points is None:
control_points = []
self.setControlPoints(control_points)
[docs] def setControlPoints(self, control_points):
self.__control_points = control_points
self.updateSplinePath()
[docs] def setClose(self, isClosed):
if self.__closed == isClosed:
return
self.__closed = isClosed
self.updateSplinePath()
[docs] def updateSplinePath(self):
path = Qt.QPainterPath()
cp = self.__control_points
nb_points = len(cp)
if nb_points <= 1:
pass
elif nb_points == 2:
path.moveTo(cp[0])
path.lineTo(cp[1])
else:
path.moveTo(cp[0])
for i in range(1, nb_points - 1, 3):
p1 = cp[i + 0]
p2 = cp[i + 1]
end = cp[i + 2]
path.cubicTo(p1, p2, end)
if self.__closed:
path.lineTo(cp[0])
self.setPath(path)
[docs]class TaurusGraphicsItem(TaurusBaseComponent):
"""Base class for all Taurus Graphics Items"""
def __init__(self, name=None, parent=None):
self.call__init__(TaurusBaseComponent, name,
parent) # <- log created here
#self.debug('TaurusGraphicsItem(%s,%s)' % (name,parent))
self.ignoreRepaint = False
self.setName(name)
self._currFgBrush = None
self._currBgBrush = None
self._currText = None
self._currHtmlText = None
self._map = None
self._default = None
self._visible = None
# self.getExtensions() <= It must be called AFTER set_common_params()
# in getGraphicsItem()
self._contextMenu = []
[docs] def setName(self, name):
name = str(name or self.__class__.__name__)
# srubio@cells.es: modified to store ._name since initialization (even
# if a model is not set)
self._name = name
[docs] def getName(self):
return self._name
[docs] 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', self.ignoreRepaint)
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
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
# Mandatory methods to be implemented in any subclass of TaurusComponent
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
[docs] def setModel(self, model):
#self.debug('In %s.setModel(%s)'%(type(self).__name__,model))
self.setName(model)
if issubclass(Manager().findObjectClass(self._name), TaurusDevice):
model = self._name + '/state'
TaurusBaseComponent.setModel(self, model)
[docs] def getParentTaurusComponent(self):
""" Returns a parent Taurus component or None if no parent TaurusBaseComponent
is found."""
p = self.parentItem()
while p and not isinstance(p, TaurusGraphicsItem):
p = self.parentItem()
return p
# def fireEvent(self, type):
[docs] def fireEvent(self, evt_src=None, evt_type=None, evt_value=None):
"""fires a value changed event to all listeners"""
self.updateStyle()
[docs] def updateStyle(self):
""" Method called when the component detects an event that triggers a change
in the style."""
if self.scene():
self.scene().updateSceneItem(self)
# getDisplayValue is already (and better) implemented in TaurusBaseComponent
# def getDisplayValue(self):
#attrvalue = self.getModelValueObj()
# if not attrvalue:
# return self.getNoneValue()
# return str(attrvalue.value)
[docs] def isReadOnly(self):
return True
def __str__(self):
return self.log_name + "(" + self.modelName + ")"
[docs] def getModelClass(self):
return TaurusAttribute
[docs]class TaurusGraphicsAttributeItem(TaurusGraphicsItem):
"""
This class show value->text conversion in label widgets.
Quality is shown in background
"""
def __init__(self, name=None, parent=None):
name = name or self.__class__.__name__
self._unitVisible = True
self._currValue = None
self._userFormat = None
self._unitVisible = True
self.call__init__(TaurusGraphicsItem, name, parent)
@deprecation_decorator(alt=".getDisplayValue(fragmentName='rvalue.units')",
rel="4.0.3")
def getUnit(self):
return self.getDisplayValue(fragmentName='rvalue.units')
[docs] def updateStyle(self):
v = self.getModelValueObj()
if self.getShowQuality():
try:
quality = None
if v:
quality = v.quality
if quality == AttrQuality.ATTR_VALID and self._validBackground:
background = self._validBackground
else:
background, _ = QT_ATTRIBUTE_QUALITY_PALETTE.qcolor(
quality)
self.setBrush(Qt.QBrush(background))
except:
self.warning('In TaurusGraphicsAttributeItem(%s).updateStyle(%s): colors failed!' % (
self._name, self._currText))
self.warning(traceback.format_exc())
if v and self._userFormat:
# TODO: consider extending this to use newer pyhon formatting syntax
if hasattr(v.rvalue, 'magnitude'):
text = self._userFormat % v.rvalue.magnitude
else:
text = self._userFormat % v.rvalue
if self._unitVisible:
text = "{0} {1.rvalue.units:~s}".format(text, v)
else:
if self._unitVisible:
_frName = None
else:
_frName = 'rvalue.magnitude'
text = self.getDisplayValue(fragmentName=_frName)
self._currText = text
self._currHtmlText = None
TaurusGraphicsItem.updateStyle(self)
[docs] def setUnitVisible(self, yesno):
self._unitVisible = yesno
[docs]class TaurusGraphicsStateItem(TaurusGraphicsItem):
"""
In State Item the displayValue should not override the label
This item will modify only foreground/background colors
"""
def __init__(self, name=None, parent=None):
name = name or self.__class__.__name__
self.call__init__(TaurusGraphicsItem, name, parent)
[docs] def updateStyle(self):
from taurus.core.tango import DevState # Tango-centric
v = self.getModelValueObj()
self._currBrush = Qt.QBrush(Qt.Qt.NoBrush)
if v: # or self.getShowState():
try:
bg_brush, fg_brush = None, None
if self.getModelObj().getType() == DataType.DevState:
bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush(
v.value)
elif self.getModelObj().getType() == DataType.Boolean:
bg_brush, fg_brush = QT_DEVICE_STATE_PALETTE.qbrush(
(DevState.FAULT, DevState.ON)[v.value])
elif self.getShowQuality():
bg_brush, fg_brush = QT_ATTRIBUTE_QUALITY_PALETTE.qbrush(
v.quality)
if None not in (bg_brush, fg_brush):
self._currBgBrush = bg_brush
self._currFgBrush = fg_brush
# If there's no filling, applying background brush to
# foreground
if Qt.Qt.NoBrush != getattr(self, '_fillStyle', Qt.Qt.NoBrush):
#self.debug('In TaurusGraphicsStateItem(%s).updateStyle(%s): switching background to foreground'%(self._name,self._currText))
self._currFgBrush = bg_brush
if self._currText:
self._currHtmlText = '<p style="color:%s">%s</p>' % (
self._currBgBrush.color().name(), self._currText)
except:
self.warning('In TaurusGraphicsStateItem(%s).updateStyle(%s): colors failed!' % (
self._name, self._currText))
self.warning(traceback.format_exc())
# Parsing _map to manage visibility (a list of values for which the
# item is visible or not)
if (v and not self._map is None and
self._currText in DevState.__members__):
#self.debug('In TaurusGraphicsStateItem.updateStyle(): mapping %s'%self._currText)
if DevState[self._currText] == self._map[1]:
self.setVisible(self._map[2])
self._visible = self._map[2]
else:
self.setVisible(self._default)
self._visible = self._default
TaurusGraphicsItem.updateStyle(self)
[docs]class TaurusEllipseStateItem(Qt.QGraphicsEllipseItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
Qt.QGraphicsEllipseItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget=None):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
Qt.QGraphicsEllipseItem.paint(self, painter, option, widget)
[docs]class TaurusRectStateItem(Qt.QGraphicsRectItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
Qt.QGraphicsRectItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
Qt.QGraphicsRectItem.paint(self, painter, option, widget)
[docs]class TaurusSplineStateItem(QSpline, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
QSpline.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
QSpline.paint(self, painter, option, widget)
[docs]class TaurusRoundRectItem(Qt.QGraphicsPathItem):
def __init__(self, name=None, parent=None, scene=None):
Qt.QGraphicsPathItem.__init__(self, parent)
if scene is not None:
scene.addItem(self)
self.__rect = None
self.setCornerWidth(0, 0)
def __updatePath(self):
if self.__rect is None:
return
if self.__corner is None:
return
path = Qt.QPainterPath()
cornerWidth, nbPoints = self.__corner
if cornerWidth == 0 or nbPoints == 0:
path.addRect(self.__rect)
elif cornerWidth * 2 > self.__rect.width():
path.addRect(self.__rect)
elif cornerWidth * 2 > self.__rect.height():
path.addRect(self.__rect)
else:
path.addRoundedRect(self.__rect, cornerWidth, cornerWidth)
self.setPath(path)
[docs] def setRect(self, x, y, width, height):
self.__rect = Qt.QRectF(x, y, width, height)
self.__updatePath()
[docs] def setCornerWidth(self, width, nbPoints):
self.__corner = width, nbPoints
self.__updatePath()
[docs]class TaurusRoundRectStateItem(TaurusRoundRectItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
TaurusRoundRectItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
TaurusRoundRectItem.paint(self, painter, option, widget)
[docs]class TaurusGroupItem(Qt.QGraphicsItemGroup):
def __init__(self, name=None, parent=None, scene=None):
Qt.QGraphicsItemGroup.__init__(self, parent, scene)
[docs]class TaurusGroupStateItem(TaurusGroupItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
TaurusGroupItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
TaurusGroupItem.paint(self, painter, option, widget)
[docs]class TaurusPolygonStateItem(Qt.QGraphicsPolygonItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
#Qt.QGraphicsRectItem.__init__(self, parent, scene)
Qt.QGraphicsPolygonItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
Qt.QGraphicsPolygonItem.paint(self, painter, option, widget)
[docs]class TaurusLineStateItem(Qt.QGraphicsLineItem, TaurusGraphicsStateItem):
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
Qt.QGraphicsLineItem.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currBgBrush:
self._currBgBrush.setStyle(self.brush().style())
self.setBrush(self._currBgBrush)
Qt.QGraphicsLineItem.paint(self, painter, option, widget)
[docs]class TaurusTextStateItem(QGraphicsTextBoxing, TaurusGraphicsStateItem):
"""
A QGraphicsItem that represents a text related to a device state or attribute quality
"""
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
QGraphicsTextBoxing.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsStateItem, name, parent)
[docs] def paint(self, painter, option, widget):
if self._currHtmlText:
self.setHtml(self._currHtmlText)
else:
self.setPlainText(self._currText or '')
QGraphicsTextBoxing.paint(self, painter, option, widget)
[docs]class TaurusTextAttributeItem(QGraphicsTextBoxing, TaurusGraphicsAttributeItem):
"""
A QGraphicsItem that represents a text related to an attribute value
"""
def __init__(self, name=None, parent=None, scene=None):
name = name or self.__class__.__name__
QGraphicsTextBoxing.__init__(self, parent, scene)
self.call__init__(TaurusGraphicsAttributeItem, name, parent)
[docs] def paint(self, painter, option, widget):
# self.debug('TaurusTextAttributeItem(%s,%s,%s)'%(self.getName(),self._currText,self._currHtmlText))
if self._currHtmlText:
self.setHtml(self._currHtmlText)
else:
self.setPlainText(self._currText or '')
QGraphicsTextBoxing.paint(self, painter, option, widget)
TYPE_TO_GRAPHICS = {
None: {"Rectangle": Qt.QGraphicsRectItem,
"RoundRectangle": TaurusRoundRectItem,
"Ellipse": Qt.QGraphicsEllipseItem,
"Polyline": Qt.QGraphicsPolygonItem,
"Label": QGraphicsTextBoxing,
"Line": Qt.QGraphicsLineItem,
"Group": TaurusGroupItem,
"SwingObject": TaurusTextAttributeItem,
"Image": Qt.QGraphicsPixmapItem,
"Spline": QSpline, },
TaurusDevice: {"Rectangle": TaurusRectStateItem,
"RoundRectangle": TaurusRoundRectStateItem,
"Ellipse": TaurusEllipseStateItem,
"Polyline": TaurusPolygonStateItem,
"Label": TaurusTextStateItem,
"Line": Qt.QGraphicsLineItem, # TaurusLineStateItem,
"Group": TaurusGroupStateItem,
"SwingObject": TaurusTextAttributeItem,
"Image": Qt.QGraphicsPixmapItem,
"Spline": TaurusSplineStateItem, },
TaurusAttribute: {"Rectangle": TaurusRectStateItem,
"RoundRectangle": TaurusRoundRectStateItem,
"Ellipse": TaurusEllipseStateItem,
"Polyline": TaurusPolygonStateItem,
"Label": TaurusTextAttributeItem,
"Line": Qt.QGraphicsLineItem, # TaurusLineStateItem,
"Group": TaurusGroupStateItem,
"SwingObject": TaurusTextAttributeItem,
"Image": Qt.QGraphicsPixmapItem,
"Spline": TaurusSplineStateItem, },
}
[docs]class TaurusBaseGraphicsFactory(object):
def __init__(self):
pass
[docs] def getSceneObj(self):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getSceneObj()")
[docs] def getObj(self, name, params):
raise RuntimeError("Invalid call to AbstractGraphicsFactory::getObj()")
[docs] def getRectangleObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getRectangleObj()")
[docs] def getRoundRectangleObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getRoundRectangleObj()")
[docs] def getLineObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getLineObj()")
[docs] def getEllipseObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getEllipseObj()")
[docs] def getPolylineObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getPolylineObj()")
[docs] def getLabelObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getLabelObj()")
[docs] def getGroupObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getGroupObj()")
[docs] def getSwingObjectObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getSwingObjectObj()")
[docs] def getImageObj(self, parms):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getImageObj()")
[docs] def getSplineObj(self, params):
raise RuntimeError(
"Invalid call to AbstractGraphicsFactory::getSplineObj()")
[docs] def getGraphicsClassItem(self, cls, type_):
ncls = cls
try:
if issubclass(cls, TaurusDevice):
ncls = TaurusDevice
elif issubclass(cls, TaurusAttribute):
ncls = TaurusAttribute
except:
pass
ncls = TYPE_TO_GRAPHICS.get(
ncls, TYPE_TO_GRAPHICS.get(None)).get(type_)
return ncls
[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_)
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] def getNameParam(self):
"""Returns the name of the parameter which contains the name identifier.
Default implementation returns 'name'.
Overwrite has necessary."""
return 'name'
[docs] def set_common_params(self, item, params):
"""Sets the common parameters. Default implementation does nothing.
Overwrite has necessary."""
pass