#!/usr/bin/env python
# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2013 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/>.
#
# ###########################################################################
"""
This module provides base classes from which the compact widgets should inherit
"""
__docformat__ = "restructuredtext"
from taurus.external.qt import Qt
from taurus.qt.qtgui.container import TaurusWidget
# from taurus.qt.qtgui.base import TaurusBaseWritableWidget
[docs]
class TaurusReadWriteSwitcher(TaurusWidget):
"""
This is a base class for creating widgets that can switch
beetween read and write mode by combining a Taurus widget for reading
and a Taurus Widget for writing.
For example, if you want to combine a TaurusLabel with a
TaurusValueLineEdit, you can implement it as follows::
class MyRWSwitcher(TaurusReadWriteSwitcher):
readWClass = TaurusLabel
writeWClass = TaurusValueLineEdit
Alternatively, you can instantiate the TaurusReadWriteSwitcher class
directly and pass the read and write classes to the constructor::
w = TaurusReadWriteSwitcher(readWClass=TaurusLabel,
writeWClass=TaurusValueLineEdit)
Or you can even set the read and write widgets (instead of classes)
after instantiation::
w = TaurusReadWriteSwitcher()
a = TaurusLabel()
b = TaurusValueLineEdit()
w.setReadWidget(a)
w.setWriteWidget(b)
TaurusReadWriteSwitcher will normally show the read widget by default,
but it will allow to switch to "edit mode" (where the write widget
is shown instead). Enetering and exiting the edit mode is controlled
by "triggers". Triggers can be key presses, QEvents or signals.
The default implementation sets pressing F2 or doubleclicking the read
widget as the triggers for entering edit mode, and pressing Escape,
losing the focus or applying the value on the write widget as the
triggers for leaving the edit mode. This can be customized by changing
`enterEditTriggers` and `exitEditTriggers` class members or by passing
`enterEditTriggers` and `exitEditTriggers` keyword parameters to the
constructor of TaurusReadWriteSwitcher:
- `enterEditTriggers` is a tuple containing one or more of the
following:
- key shortcut (either a Qt.Qt.Key or a QKeySequence)
- event type on the read widget (a Qt.QEvent.Type)
- signal from the read widget (a str representing a Signal
signature)
- `exitEditTriggers` is a tuple containing one or more of the
following:
- key shortcut (either a Qt.Qt.Key or a QKeySequence)
- event type on the write widget (a Qt.QEvent.Type)
- signal from the write widget (a str representing a Signal
signature)
#@todo: check integration with designer
"""
readWClass = None
writeWClass = None
enterEditTriggers = (Qt.Qt.Key_F2, Qt.QEvent.MouseButtonDblClick)
exitEditTriggers = (Qt.Qt.Key_Escape, Qt.QEvent.FocusOut, "applied")
def __init__(
self,
parent=None,
designMode=False,
readWClass=None,
writeWClass=None,
enterEditTriggers=None,
exitEditTriggers=None,
):
TaurusWidget.__init__(self, parent=parent, designMode=designMode)
self.setFocusPolicy(Qt.Qt.StrongFocus)
self.setLayout(Qt.QStackedLayout())
self.readWidget = None
self.writeWidget = None
# Use parameters from constructor args or defaults from class
self.readWClass = readWClass or self.readWClass
self.writeWClass = writeWClass or self.writeWClass
self.enterEditTriggers = enterEditTriggers or self.enterEditTriggers
self.exitEditTriggers = exitEditTriggers or self.exitEditTriggers
# classify the triggers
sc, et, sig = self._classifyTriggers(self.enterEditTriggers)
self.enterEditShortCuts = sc
self.enterEditEventTypes = et
self.enterEditSignals = sig
sc, et, sig = self._classifyTriggers(self.exitEditTriggers)
self.exitEditShortCuts = sc
self.exitEditEventTypes = et
self.exitEditSignals = sig
# Actions for entering and exiting the edit
self.enterEditAction = Qt.QAction("Start Editing", self)
self.enterEditAction.setShortcuts(self.enterEditShortCuts)
self.enterEditAction.setShortcutContext(
Qt.Qt.WidgetWithChildrenShortcut
)
self.addAction(self.enterEditAction)
self.exitEditAction = Qt.QAction("Abort Editing", self)
self.exitEditAction.setShortcuts(self.exitEditShortCuts)
self.exitEditAction.setShortcutContext(
Qt.Qt.WidgetWithChildrenShortcut
)
self.addAction(self.exitEditAction)
self.enterEditAction.triggered.connect(
self._onEnterEditActionTriggered
)
self.exitEditAction.triggered.connect(self._onExitEditActionTriggered)
# add read and write widgets
if self.readWClass is not None:
self.setReadWidget(self.readWClass())
if self.writeWClass is not None:
self.setWriteWidget(self.writeWClass())
def _classifyTriggers(self, triggers):
"""classifies the diferent types of triggers
:return: a tuple of 3 lists: shortcuts,enventypes,signals
:rtype: tuple
"""
shortcuts = []
eventTypes = []
signals = []
for e in triggers:
if isinstance(e, (Qt.Qt.Key, Qt.QKeySequence)):
shortcuts.append(Qt.QKeySequence(e))
elif isinstance(e, Qt.QEvent.Type):
eventTypes.append(e)
elif isinstance(e, str):
signals.append(e)
else:
raise TypeError("Unsupported trigger type: %s" % repr(type(e)))
return shortcuts, eventTypes, signals
[docs]
def eventFilter(self, obj, event):
"""reimplemented to intercept events from the read and write widgets"""
if obj is self.readWidget and event.type() in self.enterEditEventTypes:
self.enterEdit()
return True # Note that we do not let it propagate further!
if obj is self.writeWidget and event.type() in self.exitEditEventTypes:
self.exitEdit()
return True # Note that we do not let it propagate further!
# default fallback
return obj.eventFilter(obj, event)
[docs]
def enterEdit(self, *args, **kwargs):
"""Slot for entering Edit mode
.. note:: args and kwargs are ignored
"""
self.enterEditAction.trigger()
[docs]
def exitEdit(self, *args, **kwargs):
"""Slot for entering Edit mode
.. note:: args and kwargs are ignored
"""
self.exitEditAction.trigger()
def _onEnterEditActionTriggered(self):
self.layout().setCurrentIndex(1)
def _onExitEditActionTriggered(self):
self.layout().setCurrentIndex(0)
def _updateSizePolicy(self):
"""Update the size policy of the switcher widget to the most
restrictive combination of the policies of the read and write widgets
"""
policy = None
for w in self.readWidget, self.writeWidget:
if w is not None:
p = w.sizePolicy()
if policy is None:
policy = p
else:
h = policy.horizontalPolicy() & p.horizontalPolicy()
v = policy.verticalPolicy() & p.verticalPolicy()
policy = Qt.QSizePolicy(
Qt.QSizePolicy.Policy(h), Qt.QSizePolicy.Policy(v)
)
if policy is not None:
self.setSizePolicy(policy)
[docs]
def setModel(self, model, **kwargs):
"""This implementation propagates the model to the read and write
widgets.You may reimplement it to do things like passing different
models to each.
"""
if self.readWidget is not None:
self.readWidget.setModel(model, **kwargs)
if self.writeWidget is not None:
self.writeWidget.setModel(model, **kwargs)
TaurusWidget.setModel(self, model, **kwargs)
[docs]
@classmethod
def getQtDesignerPluginInfo(cls):
ret = TaurusWidget.getQtDesignerPluginInfo()
ret["module"] = "taurus.qt.qtgui.compact"
ret["group"] = "Taurus R+W"
ret["icon"] = "designer:frame.png"
if (cls.readWClass or cls.readWClass) is None:
ret["container"] = True # for base classes
else:
# for classes which already define the subwidgets
ret["container"] = False
return ret
model = Qt.pyqtProperty(
"QString", TaurusWidget.getModel, setModel, TaurusWidget.resetModel
)
######################################
# This block (and something more would be needed if we decide
# to implement TaurusReadWriteSwitcher as a TaurusBaseWritableWidget
######################################
#
# def setDangerMessage(self, dangerMessage=None):
# '''propagate to writeWidget'''
# TaurusWidget.setDangerMessage(self, dangerMessage)
# try:
# return self.writeWidget.setDangerMessage(dangerMessage)
# except AttributeError:
# pass
#
# def setForceDangerousOperations(self, yesno):
# '''propagate to writeWidget'''
# TaurusWidget.setForceDangerousOperations(self, yesno)
# try:
# return self.writeWidget.setForceDangerousOperations(yesno)
# except AttributeError:
# pass
#
#
#######################################
def demo1():
"""Simple demo"""
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.display import TaurusLabel
from taurus.qt.qtgui.input import TaurusValueLineEdit
app = TaurusApplication(cmd_line_parser=None)
w = TaurusReadWriteSwitcher(
readWClass=TaurusLabel, writeWClass=TaurusValueLineEdit
)
w.model = "sys/tg_test/1/long_scalar"
w.show()
sys.exit(app.exec_())
def demo2():
"""demo of integrability in a form"""
import sys
from taurus.qt.qtgui.panel import TaurusForm
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.display import TaurusLabel
from taurus.qt.qtgui.input import TaurusValueLineEdit
class DemoSwitcher(TaurusReadWriteSwitcher):
readWClass = TaurusLabel
writeWClass = TaurusValueLineEdit
exitEditTriggers = ("editingFinished()", Qt.Qt.Key_Escape)
app = TaurusApplication(cmd_line_parser=None)
f = TaurusForm()
f.model = ["sys/tg_test/1/long_scalar", "sys/tg_test/1/long_scalar"]
f[0].setReadWidgetClass(DemoSwitcher)
f[0].setWriteWidgetClass(None)
f.show()
sys.exit(app.exec_())
def demo3():
"""simple demo including more than one widget"""
import sys
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.display import TaurusLabel, TaurusLed
from taurus.qt.qtgui.input import TaurusValueLineEdit, TaurusValueCheckBox
app = TaurusApplication(cmd_line_parser=None)
w1 = TaurusReadWriteSwitcher(
readWClass=TaurusLabel, writeWClass=TaurusValueLineEdit
)
w1.model = "sys/tg_test/1/long_scalar"
w2 = TaurusReadWriteSwitcher(
readWClass=TaurusLed, writeWClass=TaurusValueCheckBox
)
w2.model = "sys/tg_test/1/boolean_scalar"
f = Qt.QWidget()
f.setLayout(Qt.QVBoxLayout())
f.layout().addWidget(w1)
f.layout().addWidget(w2)
f.layout().addWidget(
TaurusReadWriteSwitcher()
) # add non-initialized switcher
f.show()
sys.exit(app.exec_())
if __name__ == "__main__":
# demo1()
# demo2()
demo3()